s390/mm,ptdump: convert to generic page table dumper
authorHeiko Carstens <hca@linux.ibm.com>
Fri, 4 Sep 2020 15:41:27 +0000 (17:41 +0200)
committerVasily Gorbik <gor@linux.ibm.com>
Mon, 14 Sep 2020 09:38:34 +0000 (11:38 +0200)
Make use of generic ptdump infrastructure.

Reviewed-by: Vasily Gorbik <gor@linux.ibm.com>
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
arch/s390/Kconfig
arch/s390/Kconfig.debug
arch/s390/configs/debug_defconfig
arch/s390/configs/defconfig
arch/s390/mm/Makefile
arch/s390/mm/dump_pagetables.c

index 8a6121f937092b7ac23f75b5726d2f361a355f9b..85bf121211d10cc8603872fbb0175afce96ba016 100644 (file)
@@ -120,6 +120,7 @@ config S390
        select GENERIC_CPU_VULNERABILITIES
        select GENERIC_FIND_FIRST_BIT
        select GENERIC_GETTIMEOFDAY
+       select GENERIC_PTDUMP
        select GENERIC_SMP_IDLE_THREAD
        select GENERIC_TIME_VSYSCALL
        select HAVE_ALIGNED_STRUCT_PAGE if SLUB
index 761fe2b0b2f6dfe857050935a14f9679a3dbe1dd..ab48b694ade8ede5dfb2438fb2689ee9900a7c1b 100644 (file)
@@ -3,17 +3,5 @@
 config TRACE_IRQFLAGS_SUPPORT
        def_bool y
 
-config S390_PTDUMP
-       bool "Export kernel pagetable layout to userspace via debugfs"
-       depends on DEBUG_KERNEL
-       select DEBUG_FS
-       help
-         Say Y here if you want to show the kernel pagetable layout in a
-         debugfs file. This information is only useful for kernel developers
-         who are working in architecture specific areas of the kernel.
-         It is probably not a good idea to enable this feature in a production
-         kernel.
-         If in doubt, say "N"
-
 config EARLY_PRINTK
        def_bool y
index 0cf9a82326a85b4f254f46bd0648096621577831..f79eafb597cb3252b42bf7554730a78df1008f16 100644 (file)
@@ -774,6 +774,7 @@ CONFIG_MAGIC_SYSRQ=y
 CONFIG_DEBUG_PAGEALLOC=y
 CONFIG_PAGE_OWNER=y
 CONFIG_DEBUG_RODATA_TEST=y
+CONFIG_PTDUMP_DEBUGFS=y
 CONFIG_DEBUG_OBJECTS=y
 CONFIG_DEBUG_OBJECTS_SELFTEST=y
 CONFIG_DEBUG_OBJECTS_FREE=y
@@ -819,7 +820,6 @@ CONFIG_SCHED_TRACER=y
 CONFIG_FTRACE_SYSCALLS=y
 CONFIG_BLK_DEV_IO_TRACE=y
 CONFIG_HIST_TRIGGERS=y
-CONFIG_S390_PTDUMP=y
 CONFIG_NOTIFIER_ERROR_INJECTION=m
 CONFIG_NETDEV_NOTIFIER_ERROR_INJECT=m
 CONFIG_FAULT_INJECTION=y
index 5df9759e8ff6700e6771f9999604b3192e545568..9593cc8a9efd6a5df97e4844b2fdb574afe6b61f 100644 (file)
@@ -758,6 +758,7 @@ CONFIG_GDB_SCRIPTS=y
 CONFIG_FRAME_WARN=1024
 CONFIG_DEBUG_SECTION_MISMATCH=y
 CONFIG_MAGIC_SYSRQ=y
+CONFIG_PTDUMP_DEBUGFS=y
 CONFIG_DEBUG_MEMORY_INIT=y
 CONFIG_PANIC_ON_OOPS=y
 CONFIG_TEST_LOCKUP=m
@@ -772,7 +773,6 @@ CONFIG_SCHED_TRACER=y
 CONFIG_FTRACE_SYSCALLS=y
 CONFIG_BLK_DEV_IO_TRACE=y
 CONFIG_HIST_TRIGGERS=y
-CONFIG_S390_PTDUMP=y
 CONFIG_LKDTM=m
 CONFIG_PERCPU_TEST=m
 CONFIG_ATOMIC64_SELFTEST=y
index 3175413186b9d1e777e3151c9f344b879b755bb9..8ab9daeeace3802e83f79b1eb7301f6051a4b786 100644 (file)
@@ -8,7 +8,7 @@ obj-y           += page-states.o pageattr.o pgtable.o pgalloc.o
 
 obj-$(CONFIG_CMM)              += cmm.o
 obj-$(CONFIG_HUGETLB_PAGE)     += hugetlbpage.o
-obj-$(CONFIG_S390_PTDUMP)      += dump_pagetables.o
+obj-$(CONFIG_PTDUMP_DEBUGFS)   += dump_pagetables.o
 obj-$(CONFIG_PGSTE)            += gmap.o
 
 KASAN_SANITIZE_kasan_init.o    := n
index c2ac9b8ae61206a23b338fa55aaeab634598f7ba..93a29e2f13d4299e1b4fb7b8173db21bb6f7b28a 100644 (file)
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
+#include <linux/ptdump.h>
 #include <linux/seq_file.h>
 #include <linux/debugfs.h>
-#include <linux/sched.h>
 #include <linux/mm.h>
 #include <linux/kasan.h>
 #include <asm/kasan.h>
@@ -42,10 +42,11 @@ static struct addr_marker address_markers[] = {
 };
 
 struct pg_state {
+       struct ptdump_state ptdump;
+       struct seq_file *seq;
        int level;
        unsigned int current_prot;
        unsigned long start_address;
-       unsigned long current_address;
        const struct addr_marker *marker;
 };
 
@@ -63,215 +64,75 @@ static void print_prot(struct seq_file *m, unsigned int pr, int level)
        seq_puts(m, (pr & _PAGE_NOEXEC) ? "NX\n" : "X\n");
 }
 
-static void note_page(struct seq_file *m, struct pg_state *st,
-                    unsigned int new_prot, int level)
+static void note_page(struct ptdump_state *pt_st, unsigned long addr, int level, u64 val)
 {
-       static const char units[] = "KMGTPE";
        int width = sizeof(unsigned long) * 2;
+       static const char units[] = "KMGTPE";
        const char *unit = units;
-       unsigned int prot, cur;
        unsigned long delta;
+       struct pg_state *st;
+       struct seq_file *m;
+       unsigned int prot;
 
-       /*
-        * If we have a "break" in the series, we need to flush the state
-        * that we have now. "break" is either changing perms, levels or
-        * address space marker.
-        */
-       prot = new_prot;
-       cur = st->current_prot;
-
-       if (!st->level) {
-               /* First entry */
-               st->current_prot = new_prot;
-               st->level = level;
-               st->marker = address_markers;
+       st = container_of(pt_st, struct pg_state, ptdump);
+       m = st->seq;
+       prot = val & (_PAGE_PROTECT | _PAGE_NOEXEC);
+       if (level == 4 && (val & _PAGE_INVALID))
+               prot = _PAGE_INVALID;
+       /* For pmd_none() & friends val gets passed as zero. */
+       if (level != 4 && !val)
+               prot = _PAGE_INVALID;
+       /* Final flush from generic code. */
+       if (level == -1)
+               addr = max_addr;
+       if (st->level == -1) {
                seq_printf(m, "---[ %s ]---\n", st->marker->name);
-       } else if (prot != cur || level != st->level ||
-                  st->current_address >= st->marker[1].start_address) {
-               /* Print the actual finished series */
+               st->start_address = addr;
+               st->current_prot = prot;
+               st->level = level;
+       } else if (prot != st->current_prot || level != st->level ||
+                  addr >= st->marker[1].start_address) {
                seq_printf(m, "0x%0*lx-0x%0*lx ",
                           width, st->start_address,
-                          width, st->current_address);
-               delta = (st->current_address - st->start_address) >> 10;
+                          width, addr);
+               delta = (addr - st->start_address) >> 10;
                while (!(delta & 0x3ff) && unit[1]) {
                        delta >>= 10;
                        unit++;
                }
                seq_printf(m, "%9lu%c ", delta, *unit);
                print_prot(m, st->current_prot, st->level);
-               while (st->current_address >= st->marker[1].start_address) {
+               while (addr >= st->marker[1].start_address) {
                        st->marker++;
                        seq_printf(m, "---[ %s ]---\n", st->marker->name);
                }
-               st->start_address = st->current_address;
-               st->current_prot = new_prot;
+               st->start_address = addr;
+               st->current_prot = prot;
                st->level = level;
        }
 }
 
-#ifdef CONFIG_KASAN
-static void note_kasan_early_shadow_page(struct seq_file *m,
-                                               struct pg_state *st)
-{
-       unsigned int prot;
-
-       prot = pte_val(*kasan_early_shadow_pte) &
-               (_PAGE_PROTECT | _PAGE_INVALID | _PAGE_NOEXEC);
-       note_page(m, st, prot, 4);
-}
-#endif
-
-/*
- * The actual page table walker functions. In order to keep the
- * implementation of print_prot() short, we only check and pass
- * _PAGE_INVALID and _PAGE_PROTECT flags to note_page() if a region,
- * segment or page table entry is invalid or read-only.
- * After all it's just a hint that the current level being walked
- * contains an invalid or read-only entry.
- */
-static void walk_pte_level(struct seq_file *m, struct pg_state *st,
-                          pmd_t *pmd, unsigned long addr)
-{
-       unsigned int prot;
-       pte_t *pte;
-       int i;
-
-       for (i = 0; i < PTRS_PER_PTE && addr < max_addr; i++) {
-               st->current_address = addr;
-               pte = pte_offset_kernel(pmd, addr);
-               prot = pte_val(*pte) &
-                       (_PAGE_PROTECT | _PAGE_INVALID | _PAGE_NOEXEC);
-               note_page(m, st, prot, 4);
-               addr += PAGE_SIZE;
-       }
-}
-
-static void walk_pmd_level(struct seq_file *m, struct pg_state *st,
-                          pud_t *pud, unsigned long addr)
-{
-       unsigned int prot;
-       pmd_t *pmd;
-       int i;
-
-#ifdef CONFIG_KASAN
-       if ((pud_val(*pud) & PAGE_MASK) == __pa(kasan_early_shadow_pmd)) {
-               note_kasan_early_shadow_page(m, st);
-               return;
-       }
-#endif
-
-       pmd = pmd_offset(pud, addr);
-       for (i = 0; i < PTRS_PER_PMD && addr < max_addr; i++, pmd++) {
-               st->current_address = addr;
-               if (!pmd_none(*pmd)) {
-                       if (pmd_large(*pmd)) {
-                               prot = pmd_val(*pmd) &
-                                       (_SEGMENT_ENTRY_PROTECT |
-                                        _SEGMENT_ENTRY_NOEXEC);
-                               note_page(m, st, prot, 3);
-                       } else
-                               walk_pte_level(m, st, pmd, addr);
-               } else
-                       note_page(m, st, _PAGE_INVALID, 3);
-               addr += PMD_SIZE;
-       }
-}
-
-static void walk_pud_level(struct seq_file *m, struct pg_state *st,
-                          p4d_t *p4d, unsigned long addr)
-{
-       unsigned int prot;
-       pud_t *pud;
-       int i;
-
-#ifdef CONFIG_KASAN
-       if ((p4d_val(*p4d) & PAGE_MASK) == __pa(kasan_early_shadow_pud)) {
-               note_kasan_early_shadow_page(m, st);
-               return;
-       }
-#endif
-
-       pud = pud_offset(p4d, addr);
-       for (i = 0; i < PTRS_PER_PUD && addr < max_addr; i++, pud++) {
-               st->current_address = addr;
-               if (!pud_none(*pud))
-                       if (pud_large(*pud)) {
-                               prot = pud_val(*pud) &
-                                       (_REGION_ENTRY_PROTECT |
-                                        _REGION_ENTRY_NOEXEC);
-                               note_page(m, st, prot, 2);
-                       } else
-                               walk_pmd_level(m, st, pud, addr);
-               else
-                       note_page(m, st, _PAGE_INVALID, 2);
-               addr += PUD_SIZE;
-       }
-}
-
-static void walk_p4d_level(struct seq_file *m, struct pg_state *st,
-                          pgd_t *pgd, unsigned long addr)
-{
-       p4d_t *p4d;
-       int i;
-
-#ifdef CONFIG_KASAN
-       if ((pgd_val(*pgd) & PAGE_MASK) == __pa(kasan_early_shadow_p4d)) {
-               note_kasan_early_shadow_page(m, st);
-               return;
-       }
-#endif
-
-       p4d = p4d_offset(pgd, addr);
-       for (i = 0; i < PTRS_PER_P4D && addr < max_addr; i++, p4d++) {
-               st->current_address = addr;
-               if (!p4d_none(*p4d))
-                       walk_pud_level(m, st, p4d, addr);
-               else
-                       note_page(m, st, _PAGE_INVALID, 2);
-               addr += P4D_SIZE;
-       }
-}
-
-static void walk_pgd_level(struct seq_file *m)
-{
-       unsigned long addr = 0;
-       struct pg_state st;
-       pgd_t *pgd;
-       int i;
-
-       memset(&st, 0, sizeof(st));
-       for (i = 0; i < PTRS_PER_PGD && addr < max_addr; i++) {
-               st.current_address = addr;
-               pgd = pgd_offset_k(addr);
-               if (!pgd_none(*pgd))
-                       walk_p4d_level(m, &st, pgd, addr);
-               else
-                       note_page(m, &st, _PAGE_INVALID, 1);
-               addr += PGDIR_SIZE;
-               cond_resched();
-       }
-       /* Flush out the last page */
-       st.current_address = max_addr;
-       note_page(m, &st, 0, 0);
-}
-
 static int ptdump_show(struct seq_file *m, void *v)
 {
-       walk_pgd_level(m);
+       struct pg_state st = {
+               .ptdump = {
+                       .note_page = note_page,
+                       .range = (struct ptdump_range[]) {
+                               {.start = 0, .end = max_addr},
+                               {.start = 0, .end = 0},
+                       }
+               },
+               .seq = m,
+               .level = -1,
+               .current_prot = 0,
+               .start_address = 0,
+               .marker = address_markers,
+       };
+
+       ptdump_walk_pgd(&st.ptdump, &init_mm, NULL);
        return 0;
 }
-
-static int ptdump_open(struct inode *inode, struct file *filp)
-{
-       return single_open(filp, ptdump_show, NULL);
-}
-
-static const struct file_operations ptdump_fops = {
-       .open           = ptdump_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(ptdump);
 
 static int pt_dump_init(void)
 {