[RFC PATCH 0/4] mm: introduce __ptent sparse attribute

From: Alexander Gordeev

Date: Fri Jul 17 2026 - 09:35:42 EST


Hi All!

This RFC is a result the implementation of lazy MMU mode on s390 [1].

Heiko Carstens requested some mechanism that would rule out direct
dereferencing of PTE pointers, which otherwise would bypass the per-
cpu cache on s390 and lead to catastrophic results.

This is an attempt to introduce __ptent sparse attribute similar to
__private, but to be applied only to pte_t type by architectures.
When used with C=1 the modified sparse "dereference of PTE pointer"
warning is emitted.

With using that new mechanism I was able to identify direct PTE
pointer dereferences, which should not have existed after commit
c33c794828f2 ("mm: ptep_get() conversion") (and its follow-ups).

In addition to ptep_get() and set_pte() accessors I suggest to
introduce ptep_get_nopgtable() and set_pte_nopgtable() variants
to use when a PTE pointer does not point to the page table, but
rather to a copy of the PTE. These would allow dropping unnecessary
checks on s390, but is already the case for folio_pte_batch_flags().

Patches 1,2 are independent from the sparse rework and are good to go AFAICT.
Patch 3 is the required change to be coupled with the sparse rework below.
Patch 4 is an example of how __ptent was implemented on s390.

I hesitate to post the sparse patch itself before the rework is is agreed,
but I am CC-ing the sparse mailing list.

1. https://lore.kernel.org/linux-s390/cover.1784121418.git.agordeev@xxxxxxxxxxxxx/
2. https://lore.kernel.org/linux-s390/20260423122824.10371E07-hca@xxxxxxxxxxxxx/

Thanks!

diff --git a/expand.c b/expand.c
index f14e7181..a06b005c 100644
--- a/expand.c
+++ b/expand.c
@@ -718,6 +718,8 @@ static int expand_dereference(struct expression *expr)
*/
if (expr->ctype->ctype.modifiers & MOD_NODEREF)
warning(unop->pos, "dereference of noderef expression");
+ if ((expr->ctype->ctype.modifiers & MOD_PTENT) && unop->ctype != &lazy_ptr_ctype)
+ warning(unop->pos, "dereference of PTE pointer");

/*
* Is it "symbol" or "symbol + offset"?
diff --git a/parse.c b/parse.c
index 9389079e..213faeaf 100644
--- a/parse.c
+++ b/parse.c
@@ -553,6 +553,7 @@ static struct init_keyword {
D("cleanup", &cleanup_op),
D("nocast", &attr_mod_op, .mods = MOD_NOCAST),
D("noderef", &attr_mod_op, .mods = MOD_NODEREF),
+ D("ptent", &attr_mod_op, .mods = MOD_PTENT),
D("safe", &attr_mod_op, .mods = MOD_SAFE),
D("unused", &attr_mod_op, .mods = MOD_UNUSED),
D("externally_visible", &attr_mod_op, .mods = MOD_EXT_VISIBLE),
diff --git a/show-parse.c b/show-parse.c
index ceb6b3cb..0784d51d 100644
--- a/show-parse.c
+++ b/show-parse.c
@@ -135,6 +135,7 @@ static const char *show_modifiers(unsigned long mod, int term)
{MOD_GNU_INLINE, "[gnu_inline]"},
{MOD_NOCAST, "[nocast]"},
{MOD_NODEREF, "[noderef]"},
+ {MOD_PTENT, "[ptent]"},
{MOD_NORETURN, "[noreturn]"},
{MOD_PURE, "[pure]"},
{MOD_SAFE, "[safe]"},
diff --git a/symbol.h b/symbol.h
index 3552d439..5fb1a811 100644
--- a/symbol.h
+++ b/symbol.h
@@ -247,7 +247,7 @@ struct symbol {

#define MOD_GNU_INLINE 0x00010000
#define MOD_USERTYPE 0x00020000
- // MOD UNUSED 0x00040000
+#define MOD_PTENT 0x00040000
// MOD UNUSED 0x00080000
// MOD UNUSED 0x00100000
// MOD UNUSED 0x00200000
@@ -270,9 +270,9 @@ struct symbol {
#define MOD_SPECIFIER MOD_SIGNEDNESS
#define MOD_IGNORE (MOD_STORAGE | MOD_ACCESS | MOD_USERTYPE | MOD_EXPLICITLY_SIGNED | MOD_EXT_VISIBLE | MOD_UNUSED | MOD_GNU_INLINE)
#define MOD_QUALIFIER (MOD_CONST | MOD_VOLATILE | MOD_RESTRICT)
-#define MOD_PTRINHERIT (MOD_QUALIFIER | MOD_ATOMIC | MOD_NODEREF | MOD_NORETURN | MOD_NOCAST)
+#define MOD_PTRINHERIT (MOD_QUALIFIER | MOD_ATOMIC | MOD_NODEREF | MOD_PTENT | MOD_NORETURN | MOD_NOCAST)
/* modifiers preserved by typeof() operator */
-#define MOD_TYPEOF (MOD_QUALIFIER | MOD_ATOMIC | MOD_NOCAST | MOD_SPECIFIER)
+#define MOD_TYPEOF (MOD_QUALIFIER | MOD_ATOMIC | MOD_NOCAST | MOD_SPECIFIER | MOD_PTENT)
/* modifiers for function attributes */
#define MOD_FUN_ATTR (MOD_PURE|MOD_NORETURN)
/* like cvr-qualifiers but 'reversed' (OK: source <= target) */



Alexander Gordeev (4):
mm: use proper PTE accessors in madvise() and mremap()
mm: introduce ptep_get_nopgtable() and set_pte_nopgtable() accessors
mm: introduce __ptent sparse attribute
s390/mm: implement __ptent-aware pte_t type

arch/s390/include/asm/page.h | 16 ++++++++++--
arch/s390/include/asm/pgtable.h | 44 +++++++++++++++++++++++++++++++++
include/linux/compiler_types.h | 2 ++
include/linux/pgtable.h | 25 +++++++++++++++++++
mm/internal.h | 16 +++++-------
mm/ksm.c | 2 +-
mm/madvise.c | 2 +-
mm/mremap.c | 2 +-
8 files changed, 94 insertions(+), 15 deletions(-)

--
2.53.0