|
@@ -33,19 +33,35 @@ static int walk_pmd_range(pud_t *pud, unsigned long addr, unsigned long end,
|
|
|
|
|
|
pmd = pmd_offset(pud, addr);
|
|
|
do {
|
|
|
+again:
|
|
|
next = pmd_addr_end(addr, end);
|
|
|
- split_huge_page_pmd(walk->mm, pmd);
|
|
|
- if (pmd_none_or_clear_bad(pmd)) {
|
|
|
+ if (pmd_none(*pmd)) {
|
|
|
if (walk->pte_hole)
|
|
|
err = walk->pte_hole(addr, next, walk);
|
|
|
if (err)
|
|
|
break;
|
|
|
continue;
|
|
|
}
|
|
|
+ /*
|
|
|
+ * This implies that each ->pmd_entry() handler
|
|
|
+ * needs to know about pmd_trans_huge() pmds
|
|
|
+ */
|
|
|
if (walk->pmd_entry)
|
|
|
err = walk->pmd_entry(pmd, addr, next, walk);
|
|
|
- if (!err && walk->pte_entry)
|
|
|
- err = walk_pte_range(pmd, addr, next, walk);
|
|
|
+ if (err)
|
|
|
+ break;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Check this here so we only break down trans_huge
|
|
|
+ * pages when we _need_ to
|
|
|
+ */
|
|
|
+ if (!walk->pte_entry)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ split_huge_page_pmd(walk->mm, pmd);
|
|
|
+ if (pmd_none_or_clear_bad(pmd))
|
|
|
+ goto again;
|
|
|
+ err = walk_pte_range(pmd, addr, next, walk);
|
|
|
if (err)
|
|
|
break;
|
|
|
} while (pmd++, addr = next, addr != end);
|