diff --git a/mm/pagewalk.c b/mm/pagewalk.c index 1dcbf6ed536a..ffb583e0b0c7 100644 --- a/mm/pagewalk.c +++ b/mm/pagewalk.c @@ -610,12 +610,16 @@ int walk_page_range_debug(struct mm_struct *mm, unsigned long start, * to account for page table freeing on vmap huge page mapping. */ mmap_assert_write_locked(mm); + /* + * x86, arm64 ptdump allow walks of efi mm's and x86 ptdump allows walks + * of arbitrary mm's. + * + * However, they both must also hold the init_mm lock to account for + * concurrent kernel page table freeing. + */ + mmap_assert_write_locked(&init_mm); - /* For convenience, we allow traversal of kernel mappings. */ - if (mm == &init_mm) - return walk_kernel_page_table_range(start, end, ops, - pgd, private); - if (start >= end || !walk.mm) + if (start >= end) return -EINVAL; return walk_pgd_range(start, end, &walk); diff --git a/mm/ptdump.c b/mm/ptdump.c index 616a54c0cc42..48d1bcdc79a7 100644 --- a/mm/ptdump.c +++ b/mm/ptdump.c @@ -155,11 +155,18 @@ void ptdump_walk_pgd(struct ptdump_state *st, struct mm_struct *mm, pgd_t *pgd) get_online_mems(); mmap_write_lock(mm); + /* To stabilise kernel page tables we must hold the init_mm lock too. */ + if (mm != &init_mm) + mmap_write_lock_nested(&init_mm, SINGLE_DEPTH_NESTING); + while (range->start != range->end) { walk_page_range_debug(mm, range->start, range->end, &ptdump_ops, pgd, st); range++; } + + if (mm != &init_mm) + mmap_write_unlock(&init_mm); mmap_write_unlock(mm); put_online_mems();