mm/pagewalk.c | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/mm/pagewalk.c b/mm/pagewalk.c index d48c2a986ea3..6ae95932e799 100644 --- a/mm/pagewalk.c +++ b/mm/pagewalk.c @@ -7,11 +7,13 @@ static int walk_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, struct mm_walk *walk) { - pte_t *pte; + pte_t *pte, *start_pte; int err = 0; const struct mm_walk_ops *ops = walk->ops; + spinlock_t *ptl; - pte = pte_offset_map(pmd, addr); + pte = pte_offset_map_lock(walk->mm, pmd, addr, &ptl); + start_pte = pte; for (;;) { err = ops->pte_entry(pte, addr, addr + PAGE_SIZE, walk); if (err) @@ -22,7 +24,7 @@ static int walk_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, pte++; } - pte_unmap(pte); + pte_unmap_unlock(start_pte, ptl); return err; } @@ -49,21 +51,24 @@ static int walk_pmd_range(pud_t *pud, unsigned long addr, unsigned long end, * This implies that each ->pmd_entry() handler * needs to know about pmd_trans_huge() pmds */ - if (ops->pmd_entry) + if (ops->pmd_entry) { err = ops->pmd_entry(pmd, addr, next, walk); - if (err) - break; - - /* - * Check this here so we only break down trans_huge - * pages when we _need_ to - */ - if (!ops->pte_entry) - continue; + if (err) + break; + /* No pte level walking? */ + if (!ops->pte_entry) + continue; + /* No pte level at all? */ + if (is_swap_pmd(*pmd) || pmd_trans_huge(*pmd) || pmd_devmap(*pmd)) + continue; + } else { + if (!ops->pte_entry) + continue; - split_huge_pmd(walk->vma, pmd, addr); - if (pmd_trans_unstable(pmd)) - goto again; + split_huge_pmd(walk->vma, pmd, addr); + if (pmd_trans_unstable(pmd)) + goto again; + } err = walk_pte_range(pmd, addr, next, walk); if (err) break;