* [RFC V2 1/3] lib/vsprintf: Add support for pgtable entries
2026-06-10 4:35 [RFC V2 0/3] lib/vsprintf: Add support for pgtable entries Anshuman Khandual
@ 2026-06-10 4:35 ` Anshuman Khandual
2026-06-10 11:13 ` Usama Arif
2026-06-10 4:35 ` [RFC V2 2/3] kunit: printf: Add test " Anshuman Khandual
2026-06-10 4:35 ` [RFC V2 3/3] mm: Replace pgtable entry prints with new format Anshuman Khandual
2 siblings, 1 reply; 5+ messages in thread
From: Anshuman Khandual @ 2026-06-10 4:35 UTC (permalink / raw)
To: linux-mm
Cc: Anshuman Khandual, Andy Shevchenko, Rasmus Villemoes,
Sergey Senozhatsky, Petr Mladek, Steven Rostedt, Jonathan Corbet,
Andrew Morton, David Hildenbrand, linux-kernel, linux-doc,
David Hildenbrand, Lorenzo Stoakes, Andy Whitcroft
Add some print formats for pgtable entries at any pgtable level. These new
formats are %pp[g|4|u|m|t][d|e] i.e %ppgd, %pp4d, %ppud, %ppmd, and %ppte.
These currently support both 32 bit and 64 bit pgtable entries that can be
extended up to 128 bit when required.
Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
---
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: David Hildenbrand <david@kernel.org>
Cc: Lorenzo Stoakes <ljs@kernel.org>
Cc: Petr Mladek <pmladek@suse.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Andy Whitcroft <apw@canonical.com>
Cc: linux-mm@kvack.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-doc@vger.kernel.org
Documentation/core-api/printk-formats.rst | 19 ++++++++
lib/vsprintf.c | 58 +++++++++++++++++++++++
scripts/checkpatch.pl | 2 +-
3 files changed, 78 insertions(+), 1 deletion(-)
diff --git a/Documentation/core-api/printk-formats.rst b/Documentation/core-api/printk-formats.rst
index c0b1b6089307..e69f91a9dd9d 100644
--- a/Documentation/core-api/printk-formats.rst
+++ b/Documentation/core-api/printk-formats.rst
@@ -696,6 +696,25 @@ Rust
Only intended to be used from Rust code to format ``core::fmt::Arguments``.
Do *not* use it from C.
+Page Table Entry
+----------------
+
+::
+
+ %p[pgd|p4dp|pud|pmd|pte]
+
+Print page table entry at any level.
+
+Passed by reference.
+
+Examples for a 64 bit page table entry, given &(u64)0xc0ffee::
+
+ %ppte 0x0000000000c0ffee
+ %ppmd 0x0000000000c0ffee
+ %ppud 0x0000000000c0ffee
+ %pp4d 0x0000000000c0ffee
+ %ppgd 0x0000000000c0ffee
+
Thanks
======
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 9f359b31c8d1..d4ad3048a4db 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -856,6 +856,59 @@ static char *default_pointer(char *buf, char *end, const void *ptr,
return ptr_to_id(buf, end, ptr, spec);
}
+static char *pxd_pointer(char *buf, char *end, const void *ptr,
+ struct printf_spec spec, const char *fmt)
+{
+ if (check_pointer(&buf, end, ptr, spec))
+ return buf;
+
+ if (fmt[1] == 't' && fmt[2] == 'e') {
+ pte_t *pte = (pte_t *)ptr;
+
+ static_assert(sizeof(pte_t) == 4 ||
+ sizeof(pte_t) == 8,
+ "pte_t size must be 4 or 8 bytes");
+ return special_hex_number(buf, end, pte_val(ptep_get(pte)), sizeof(pte_t));
+ }
+
+ if (fmt[1] == 'm' && fmt[2] == 'd') {
+ pmd_t *pmd = (pmd_t *)ptr;
+
+ static_assert(sizeof(pmd_t) == 4 ||
+ sizeof(pmd_t) == 8,
+ "pmd_t size must be 4 or 8 bytes");
+ return special_hex_number(buf, end, pmd_val(pmdp_get(pmd)), sizeof(pmd_t));
+ }
+
+ if (fmt[1] == 'u' && fmt[2] == 'd') {
+ pud_t *pud = (pud_t *)ptr;
+
+ static_assert(sizeof(pud_t) == 4 ||
+ sizeof(pud_t) == 8,
+ "pud_t size must be 4 or 8 bytes");
+ return special_hex_number(buf, end, pud_val(pudp_get(pud)), sizeof(pud_t));
+ }
+
+ if (fmt[1] == '4' && fmt[2] == 'd') {
+ p4d_t *p4d = (p4d_t *)ptr;
+
+ static_assert(sizeof(p4d_t) == 4 ||
+ sizeof(p4d_t) == 8,
+ "p4d_t size must be 4 or 8 bytes");
+ return special_hex_number(buf, end, p4d_val(p4dp_get(p4d)), sizeof(p4d_t));
+ }
+
+ if (fmt[1] == 'g' && fmt[2] == 'd') {
+ pgd_t *pgd = (pgd_t *)ptr;
+
+ static_assert(sizeof(pgd_t) == 4 ||
+ sizeof(pgd_t) == 8,
+ "pgd_t size must be 4 or 8 bytes");
+ return special_hex_number(buf, end, pgd_val(pgdp_get(pgd)), sizeof(pgd_t));
+ }
+ return default_pointer(buf, end, ptr, spec);
+}
+
int kptr_restrict __read_mostly;
static noinline_for_stack
@@ -2506,6 +2559,9 @@ early_param("no_hash_pointers", no_hash_pointers_enable);
* Without an option prints the full name of the node
* f full name
* P node name, including a possible unit address
+ * - 'p[g|4|u|m|t|][d|e]' For a page table entry, this prints its
+ * contents in a hexadecimal format
+ *
* - 'x' For printing the address unmodified. Equivalent to "%lx".
* Please read the documentation (path below) before using!
* - '[ku]s' For a BPF/tracing related format specifier, e.g. used out of
@@ -2615,6 +2671,8 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
default:
return error_string(buf, end, "(einval)", spec);
}
+ case 'p':
+ return pxd_pointer(buf, end, ptr, spec, fmt);
default:
return default_pointer(buf, end, ptr, spec);
}
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index 0492d6afc9a1..f68955858e29 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -6975,7 +6975,7 @@ sub process {
my $fmt = get_quoted_string($lines[$count - 1], raw_line($count, 0));
$fmt =~ s/%%//g;
- while ($fmt =~ /(\%[\*\d\.]*p(\w)(\w*))/g) {
+ while ($fmt =~ /(\%[\*\d\.]*p(\w)(\w*)(pte|pmd|pud|p4d|pgd))/g) {
$specifier = $1;
$extension = $2;
$qualifier = $3;
--
2.30.2
^ permalink raw reply related [flat|nested] 5+ messages in thread* Re: [RFC V2 1/3] lib/vsprintf: Add support for pgtable entries
2026-06-10 4:35 ` [RFC V2 1/3] " Anshuman Khandual
@ 2026-06-10 11:13 ` Usama Arif
0 siblings, 0 replies; 5+ messages in thread
From: Usama Arif @ 2026-06-10 11:13 UTC (permalink / raw)
To: Anshuman Khandual
Cc: Usama Arif, linux-mm, Andy Shevchenko, Rasmus Villemoes,
Sergey Senozhatsky, Petr Mladek, Steven Rostedt, Jonathan Corbet,
Andrew Morton, David Hildenbrand, linux-kernel, linux-doc,
David Hildenbrand, Lorenzo Stoakes, Andy Whitcroft
On Wed, 10 Jun 2026 05:35:43 +0100 Anshuman Khandual <anshuman.khandual@arm.com> wrote:
> Add some print formats for pgtable entries at any pgtable level. These new
> formats are %pp[g|4|u|m|t][d|e] i.e %ppgd, %pp4d, %ppud, %ppmd, and %ppte.
> These currently support both 32 bit and 64 bit pgtable entries that can be
> extended up to 128 bit when required.
>
> Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
> ---
> Cc: Andrew Morton <akpm@linux-foundation.org>
> Cc: David Hildenbrand <david@kernel.org>
> Cc: Lorenzo Stoakes <ljs@kernel.org>
> Cc: Petr Mladek <pmladek@suse.com>
> Cc: Steven Rostedt <rostedt@goodmis.org>
> Cc: Jonathan Corbet <corbet@lwn.net>
> Cc: Andy Whitcroft <apw@canonical.com>
> Cc: linux-mm@kvack.org
> Cc: linux-kernel@vger.kernel.org
> Cc: linux-doc@vger.kernel.org
>
> Documentation/core-api/printk-formats.rst | 19 ++++++++
> lib/vsprintf.c | 58 +++++++++++++++++++++++
> scripts/checkpatch.pl | 2 +-
> 3 files changed, 78 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/core-api/printk-formats.rst b/Documentation/core-api/printk-formats.rst
> index c0b1b6089307..e69f91a9dd9d 100644
> --- a/Documentation/core-api/printk-formats.rst
> +++ b/Documentation/core-api/printk-formats.rst
> @@ -696,6 +696,25 @@ Rust
> Only intended to be used from Rust code to format ``core::fmt::Arguments``.
> Do *not* use it from C.
>
> +Page Table Entry
> +----------------
> +
> +::
> +
> + %p[pgd|p4dp|pud|pmd|pte]
s/p4dp/p4d to match others
> +
> +Print page table entry at any level.
> +
> +Passed by reference.
> +
> +Examples for a 64 bit page table entry, given &(u64)0xc0ffee::
> +
> + %ppte 0x0000000000c0ffee
> + %ppmd 0x0000000000c0ffee
> + %ppud 0x0000000000c0ffee
> + %pp4d 0x0000000000c0ffee
> + %ppgd 0x0000000000c0ffee
> +
> Thanks
> ======
>
> diff --git a/lib/vsprintf.c b/lib/vsprintf.c
> index 9f359b31c8d1..d4ad3048a4db 100644
> --- a/lib/vsprintf.c
> +++ b/lib/vsprintf.c
> @@ -856,6 +856,59 @@ static char *default_pointer(char *buf, char *end, const void *ptr,
> return ptr_to_id(buf, end, ptr, spec);
> }
>
> +static char *pxd_pointer(char *buf, char *end, const void *ptr,
> + struct printf_spec spec, const char *fmt)
> +{
> + if (check_pointer(&buf, end, ptr, spec))
> + return buf;
> +
> + if (fmt[1] == 't' && fmt[2] == 'e') {
> + pte_t *pte = (pte_t *)ptr;
> +
> + static_assert(sizeof(pte_t) == 4 ||
> + sizeof(pte_t) == 8,
> + "pte_t size must be 4 or 8 bytes");
> + return special_hex_number(buf, end, pte_val(ptep_get(pte)), sizeof(pte_t));
> + }
> +
> + if (fmt[1] == 'm' && fmt[2] == 'd') {
> + pmd_t *pmd = (pmd_t *)ptr;
> +
> + static_assert(sizeof(pmd_t) == 4 ||
> + sizeof(pmd_t) == 8,
> + "pmd_t size must be 4 or 8 bytes");
> + return special_hex_number(buf, end, pmd_val(pmdp_get(pmd)), sizeof(pmd_t));
> + }
> +
> + if (fmt[1] == 'u' && fmt[2] == 'd') {
> + pud_t *pud = (pud_t *)ptr;
> +
> + static_assert(sizeof(pud_t) == 4 ||
> + sizeof(pud_t) == 8,
> + "pud_t size must be 4 or 8 bytes");
> + return special_hex_number(buf, end, pud_val(pudp_get(pud)), sizeof(pud_t));
> + }
> +
> + if (fmt[1] == '4' && fmt[2] == 'd') {
> + p4d_t *p4d = (p4d_t *)ptr;
> +
> + static_assert(sizeof(p4d_t) == 4 ||
> + sizeof(p4d_t) == 8,
> + "p4d_t size must be 4 or 8 bytes");
> + return special_hex_number(buf, end, p4d_val(p4dp_get(p4d)), sizeof(p4d_t));
> + }
> +
> + if (fmt[1] == 'g' && fmt[2] == 'd') {
> + pgd_t *pgd = (pgd_t *)ptr;
> +
> + static_assert(sizeof(pgd_t) == 4 ||
> + sizeof(pgd_t) == 8,
> + "pgd_t size must be 4 or 8 bytes");
> + return special_hex_number(buf, end, pgd_val(pgdp_get(pgd)), sizeof(pgd_t));
You mentioned in the coverletter that pgdp_get() is the reason arm32 builds dont work.
Just wanted to check what the issue is?
I had a look at arch/arm/include/asm/pgtable.h and I couldnt understand why
it reads pgdp_get(pgpd) instead of pgdp_get(pgdp)?
> + }
> + return default_pointer(buf, end, ptr, spec);
> +}
> +
> int kptr_restrict __read_mostly;
>
> static noinline_for_stack
> @@ -2506,6 +2559,9 @@ early_param("no_hash_pointers", no_hash_pointers_enable);
> * Without an option prints the full name of the node
> * f full name
> * P node name, including a possible unit address
> + * - 'p[g|4|u|m|t|][d|e]' For a page table entry, this prints its
> + * contents in a hexadecimal format
> + *
> * - 'x' For printing the address unmodified. Equivalent to "%lx".
> * Please read the documentation (path below) before using!
> * - '[ku]s' For a BPF/tracing related format specifier, e.g. used out of
> @@ -2615,6 +2671,8 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
> default:
> return error_string(buf, end, "(einval)", spec);
> }
> + case 'p':
> + return pxd_pointer(buf, end, ptr, spec, fmt);
> default:
> return default_pointer(buf, end, ptr, spec);
> }
> diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
> index 0492d6afc9a1..f68955858e29 100755
> --- a/scripts/checkpatch.pl
> +++ b/scripts/checkpatch.pl
> @@ -6975,7 +6975,7 @@ sub process {
> my $fmt = get_quoted_string($lines[$count - 1], raw_line($count, 0));
> $fmt =~ s/%%//g;
>
> - while ($fmt =~ /(\%[\*\d\.]*p(\w)(\w*))/g) {
> + while ($fmt =~ /(\%[\*\d\.]*p(\w)(\w*)(pte|pmd|pud|p4d|pgd))/g) {
> $specifier = $1;
> $extension = $2;
> $qualifier = $3;
> --
> 2.30.2
>
>
^ permalink raw reply [flat|nested] 5+ messages in thread
* [RFC V2 2/3] kunit: printf: Add test for pgtable entries
2026-06-10 4:35 [RFC V2 0/3] lib/vsprintf: Add support for pgtable entries Anshuman Khandual
2026-06-10 4:35 ` [RFC V2 1/3] " Anshuman Khandual
@ 2026-06-10 4:35 ` Anshuman Khandual
2026-06-10 4:35 ` [RFC V2 3/3] mm: Replace pgtable entry prints with new format Anshuman Khandual
2 siblings, 0 replies; 5+ messages in thread
From: Anshuman Khandual @ 2026-06-10 4:35 UTC (permalink / raw)
To: linux-mm
Cc: Anshuman Khandual, Andy Shevchenko, Rasmus Villemoes,
Sergey Senozhatsky, Petr Mladek, Steven Rostedt, Jonathan Corbet,
Andrew Morton, David Hildenbrand, linux-kernel, linux-doc
Add test for new pgtable entry print formats with entry size being 64 bits.
Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
---
Cc: Petr Mladek <pmladek@suse.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Cc: Rasmus Villemoes <linux@rasmusvillemoes.dk>
Cc: Sergey Senozhatsky <senozhatsky@chromium.org>
Cc: linux-kernel@vger.kernel.org
lib/tests/printf_kunit.c | 57 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 57 insertions(+)
diff --git a/lib/tests/printf_kunit.c b/lib/tests/printf_kunit.c
index bb70b9cddadd..850bfc0e3409 100644
--- a/lib/tests/printf_kunit.c
+++ b/lib/tests/printf_kunit.c
@@ -791,6 +791,62 @@ errptr(struct kunit *kunittest)
#endif
}
+struct pgtable_test {
+ u64 val;
+ const char *name;
+};
+
+static struct pgtable_test pgtable_test_cases[] = {
+ { .val = 0xc0ffee, .name = "0x0000000000c0ffee"},
+ { .val = 0xdeadbeef, .name = "0x00000000deadbeef"},
+ { .val = 0xaabbcc, .name = "0x0000000000aabbcc"},
+ { .val = 0xcc, .name = "0x00000000000000cc"},
+ { .val = 0x1, .name = "0x0000000000000001"},
+ { .val = 0x11, .name = "0x0000000000000011"},
+ { .val = 0x111, .name = "0x0000000000000111"},
+ { .val = 0x10000010001, .name = "0x0000010000010001"},
+ { .val = 0xc0ffeec0ffee, .name = "0x0000c0ffeec0ffee"},
+ { .val = 0x10000000000, .name = "0x0000010000000000"},
+ { .val = 0x11000000000, .name = "0x0000011000000000"},
+ { .val = 0x1000000000000001, .name = "0x1000000000000001"},
+ { .val = 0x1100000000000010, .name = "0x1100000000000010"},
+ { .val = 0x1110000000000100, .name = "0x1110000000000100"},
+ { .val = 0xfff000000000ff00, .name = "0xfff000000000ff00"},
+};
+
+static void
+pgtable_ptr(struct kunit *kunittest)
+{
+ char buf[64];
+ int i;
+
+ if (sizeof(pte_t) != 8)
+ kunit_skip(kunittest, "pte_t size is not 64 bits");
+
+ for (i = 0; i < ARRAY_SIZE(pgtable_test_cases); i++) {
+ pte_t pte = __pte(pgtable_test_cases[i].val);
+ pmd_t pmd = __pmd(pgtable_test_cases[i].val);
+ pud_t pud = __pud(pgtable_test_cases[i].val);
+ p4d_t p4d = __p4d(pgtable_test_cases[i].val);
+ pgd_t pgd = __pgd(pgtable_test_cases[i].val);
+
+ snprintf(buf, sizeof(buf), "%ppte", &pte);
+ KUNIT_EXPECT_STREQ(kunittest, buf, pgtable_test_cases[i].name);
+
+ snprintf(buf, sizeof(buf), "%ppmd", &pmd);
+ KUNIT_EXPECT_STREQ(kunittest, buf, pgtable_test_cases[i].name);
+
+ snprintf(buf, sizeof(buf), "%ppud", &pud);
+ KUNIT_EXPECT_STREQ(kunittest, buf, pgtable_test_cases[i].name);
+
+ snprintf(buf, sizeof(buf), "%pp4d", &p4d);
+ KUNIT_EXPECT_STREQ(kunittest, buf, pgtable_test_cases[i].name);
+
+ snprintf(buf, sizeof(buf), "%ppgd", &pgd);
+ KUNIT_EXPECT_STREQ(kunittest, buf, pgtable_test_cases[i].name);
+ }
+}
+
static int printf_suite_init(struct kunit_suite *suite)
{
total_tests = 0;
@@ -839,6 +895,7 @@ static struct kunit_case printf_test_cases[] = {
KUNIT_CASE(errptr),
KUNIT_CASE(fwnode_pointer),
KUNIT_CASE(fourcc_pointer),
+ KUNIT_CASE(pgtable_ptr),
{}
};
--
2.30.2
^ permalink raw reply related [flat|nested] 5+ messages in thread* [RFC V2 3/3] mm: Replace pgtable entry prints with new format
2026-06-10 4:35 [RFC V2 0/3] lib/vsprintf: Add support for pgtable entries Anshuman Khandual
2026-06-10 4:35 ` [RFC V2 1/3] " Anshuman Khandual
2026-06-10 4:35 ` [RFC V2 2/3] kunit: printf: Add test " Anshuman Khandual
@ 2026-06-10 4:35 ` Anshuman Khandual
2 siblings, 0 replies; 5+ messages in thread
From: Anshuman Khandual @ 2026-06-10 4:35 UTC (permalink / raw)
To: linux-mm
Cc: Anshuman Khandual, Andy Shevchenko, Rasmus Villemoes,
Sergey Senozhatsky, Petr Mladek, Steven Rostedt, Jonathan Corbet,
Andrew Morton, David Hildenbrand, linux-kernel, linux-doc,
David Hildenbrand, Lorenzo Stoakes
Replace all existing pgtable entry prints with recently added new format in
__print_bad_page_map_pgtable().
Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
---
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: David Hildenbrand <david@kernel.org>
Cc: Lorenzo Stoakes <ljs@kernel.org>
Cc: linux-mm@kvack.org
Cc: linux-kernel@vger.kernel.org
mm/memory.c | 15 +++++----------
1 file changed, 5 insertions(+), 10 deletions(-)
diff --git a/mm/memory.c b/mm/memory.c
index 86a973119bd4..8a25790f7c24 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -521,7 +521,6 @@ static bool is_bad_page_map_ratelimited(void)
static void __print_bad_page_map_pgtable(struct mm_struct *mm, unsigned long addr)
{
- unsigned long long pgdv, p4dv, pudv, pmdv;
p4d_t p4d, *p4dp;
pud_t pud, *pudp;
pmd_t pmd, *pmdp;
@@ -532,34 +531,30 @@ static void __print_bad_page_map_pgtable(struct mm_struct *mm, unsigned long add
* see locking requirements for print_bad_page_map().
*/
pgdp = pgd_offset(mm, addr);
- pgdv = pgd_val(*pgdp);
if (!pgd_present(*pgdp) || pgd_leaf(*pgdp)) {
- pr_alert("pgd:%08llx\n", pgdv);
+ pr_alert("pgd:%ppgd\n", pgdp);
return;
}
p4dp = p4d_offset(pgdp, addr);
p4d = p4dp_get(p4dp);
- p4dv = p4d_val(p4d);
if (!p4d_present(p4d) || p4d_leaf(p4d)) {
- pr_alert("pgd:%08llx p4d:%08llx\n", pgdv, p4dv);
+ pr_alert("pgd:%ppgd p4d:%pp4d\n", pgdp, p4dp);
return;
}
pudp = pud_offset(p4dp, addr);
pud = pudp_get(pudp);
- pudv = pud_val(pud);
if (!pud_present(pud) || pud_leaf(pud)) {
- pr_alert("pgd:%08llx p4d:%08llx pud:%08llx\n", pgdv, p4dv, pudv);
+ pr_alert("pgd:%ppgd p4d:%pp4d pud:%ppud\n", pgdp, p4dp, pudp);
return;
}
pmdp = pmd_offset(pudp, addr);
pmd = pmdp_get(pmdp);
- pmdv = pmd_val(pmd);
/*
* Dumping the PTE would be nice, but it's tricky with CONFIG_HIGHPTE,
@@ -567,8 +562,8 @@ static void __print_bad_page_map_pgtable(struct mm_struct *mm, unsigned long add
* doing another map would be bad. print_bad_page_map() should
* already take care of printing the PTE.
*/
- pr_alert("pgd:%08llx p4d:%08llx pud:%08llx pmd:%08llx\n", pgdv,
- p4dv, pudv, pmdv);
+ pr_alert("pgd:%ppgd p4d:%pp4d pud:%ppud pmd:%ppmd\n", pgdp,
+ p4dp, pudp, pmdp);
}
/*
--
2.30.2
^ permalink raw reply related [flat|nested] 5+ messages in thread