On Mon, Jul 22, 2019 at 04:42:08PM +0100, Steven Price wrote: > Add a generic version of page table dumping that architectures can > opt-in to > > Signed-off-by: Steven Price <steven.price@xxxxxxx> [...] > +#ifdef CONFIG_KASAN > +/* > + * This is an optimization for KASAN=y case. Since all kasan page tables > + * eventually point to the kasan_early_shadow_page we could call note_page() > + * right away without walking through lower level page tables. This saves > + * us dozens of seconds (minutes for 5-level config) while checking for > + * W+X mapping or reading kernel_page_tables debugfs file. > + */ > +static inline bool kasan_page_table(struct ptdump_state *st, void *pt, > + unsigned long addr) > +{ > + if (__pa(pt) == __pa(kasan_early_shadow_pmd) || > +#ifdef CONFIG_X86 > + (pgtable_l5_enabled() && > + __pa(pt) == __pa(kasan_early_shadow_p4d)) || > +#endif > + __pa(pt) == __pa(kasan_early_shadow_pud)) { > + st->note_page(st, addr, 5, pte_val(kasan_early_shadow_pte[0])); > + return true; > + } > + return false; Having you tried this with CONFIG_DEBUG_VIRTUAL? The kasan_early_shadow_pmd is a kernel object rather than a linear map object, so you should use __pa_symbol for that. It's a bit horrid to have to test multiple levels in one function; can't we check the relevant level inline in each of the test_p?d funcs? They're optional anyway, so they only need to be defined for CONFIG_KASAN. Thanks, Mark. > +} > +#else > +static inline bool kasan_page_table(struct ptdump_state *st, void *pt, > + unsigned long addr) > +{ > + return false; > +} > +#endif > + > +static int ptdump_test_p4d(unsigned long addr, unsigned long next, > + p4d_t *p4d, struct mm_walk *walk) > +{ > + struct ptdump_state *st = walk->private; > + > + if (kasan_page_table(st, p4d, addr)) > + return 1; > + return 0; > +} > +static int ptdump_test_pud(unsigned long addr, unsigned long next, > + pud_t *pud, struct mm_walk *walk) > +{ > + struct ptdump_state *st = walk->private; > + > + if (kasan_page_table(st, pud, addr)) > + return 1; > + return 0; > +} > + > +static int ptdump_test_pmd(unsigned long addr, unsigned long next, > + pmd_t *pmd, struct mm_walk *walk) > +{ > + struct ptdump_state *st = walk->private; > + > + if (kasan_page_table(st, pmd, addr)) > + return 1; > + return 0; > +} > + > +static int ptdump_hole(unsigned long addr, unsigned long next, > + struct mm_walk *walk) > +{ > + struct ptdump_state *st = walk->private; > + > + st->note_page(st, addr, -1, 0); > + > + return 0; > +} > + > +void ptdump_walk_pgd(struct ptdump_state *st, struct mm_struct *mm) > +{ > + struct mm_walk walk = { > + .mm = mm, > + .pgd_entry = ptdump_pgd_entry, > + .p4d_entry = ptdump_p4d_entry, > + .pud_entry = ptdump_pud_entry, > + .pmd_entry = ptdump_pmd_entry, > + .pte_entry = ptdump_pte_entry, > + .test_p4d = ptdump_test_p4d, > + .test_pud = ptdump_test_pud, > + .test_pmd = ptdump_test_pmd, > + .pte_hole = ptdump_hole, > + .private = st > + }; > + const struct ptdump_range *range = st->range; > + > + down_read(&mm->mmap_sem); > + while (range->start != range->end) { > + walk_page_range(range->start, range->end, &walk); > + range++; > + } > + up_read(&mm->mmap_sem); > + > + /* Flush out the last page */ > + st->note_page(st, 0, 0, 0); > +} > -- > 2.20.1 >