[RFC PATCH 2/4] mm: introduce ptep_get_nopgtable() and set_pte_nopgtable() accessors
From: Alexander Gordeev
Date: Fri Jul 17 2026 - 09:35:30 EST
Add helpers to safely access PTE values stored outside page
tables, such as in stack variables or temporary copies. These
adopt the check from folio_pte_batch_flags() to ensure the
pointer does not point into an actual page table.
Follow the pattern established by commit c33c794828f2 ("mm:
ptep_get() conversion") and use the new accessors instead of
direct pointer dereferences.
Signed-off-by: Alexander Gordeev <agordeev@xxxxxxxxxxxxx>
---
include/linux/pgtable.h | 25 +++++++++++++++++++++++++
mm/internal.h | 16 ++++++----------
mm/ksm.c | 2 +-
3 files changed, 32 insertions(+), 11 deletions(-)
diff --git a/include/linux/pgtable.h b/include/linux/pgtable.h
index 2981e386da7b..e38f045d0e73 100644
--- a/include/linux/pgtable.h
+++ b/include/linux/pgtable.h
@@ -489,6 +489,31 @@ static inline int pudp_set_access_flags(struct vm_area_struct *vma,
#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
#endif
+#ifndef ptep_get_nopgtable
+static inline pte_t ptep_get_nopgtable(pte_t *ptep)
+{
+ /*
+ * Ensure this is a pointer to a copy not a pointer into a page table.
+ * If this is a stack value, it won't be a valid virtual address, but
+ * that's fine because it also cannot be pointing into the page table.
+ */
+ VM_WARN_ON(virt_addr_valid(ptep) && PageTable(virt_to_page(ptep)));
+
+ return *ptep;
+}
+#endif
+
+#ifndef set_pte_nopgtable
+static inline void set_pte_nopgtable(pte_t *ptep, pte_t pte)
+{
+ /*
+ * See comment in ptep_get_nopgtable().
+ */
+ VM_WARN_ON(virt_addr_valid(ptep) && PageTable(virt_to_page(ptep)));
+ *ptep = pte;
+}
+#endif
+
#ifndef ptep_get
static inline pte_t ptep_get(pte_t *ptep)
{
diff --git a/mm/internal.h b/mm/internal.h
index 181e79f1d6a2..0d04a107962a 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -339,18 +339,12 @@ static inline unsigned int folio_pte_batch_flags(struct folio *folio,
unsigned int max_nr, fpb_t flags)
{
bool any_writable = false, any_young = false, any_dirty = false;
- pte_t expected_pte, pte = *ptentp;
+ pte_t expected_pte, pte = ptep_get_nopgtable(ptentp);
unsigned int nr, cur_nr;
VM_WARN_ON_FOLIO(!pte_present(pte), folio);
VM_WARN_ON_FOLIO(!folio_test_large(folio) || max_nr < 1, folio);
VM_WARN_ON_FOLIO(page_folio(pfn_to_page(pte_pfn(pte))) != folio, folio);
- /*
- * Ensure this is a pointer to a copy not a pointer into a page table.
- * If this is a stack value, it won't be a valid virtual address, but
- * that's fine because it also cannot be pointing into the page table.
- */
- VM_WARN_ON(virt_addr_valid(ptentp) && PageTable(virt_to_page(ptentp)));
/* Limit max_nr to the actual remaining PFNs in the folio we could batch. */
max_nr = min_t(unsigned long, max_nr,
@@ -379,12 +373,14 @@ static inline unsigned int folio_pte_batch_flags(struct folio *folio,
nr += cur_nr;
}
+ pte = ptep_get_nopgtable(ptentp);
if (any_writable)
- *ptentp = pte_mkwrite(*ptentp, vma);
+ pte = pte_mkwrite(pte, vma);
if (any_young)
- *ptentp = pte_mkyoung(*ptentp);
+ pte = pte_mkyoung(pte);
if (any_dirty)
- *ptentp = pte_mkdirty(*ptentp);
+ pte = pte_mkdirty(pte);
+ set_pte_nopgtable(ptentp, pte);
return min(nr, max_nr);
}
diff --git a/mm/ksm.c b/mm/ksm.c
index 7d5b76478f0b..5b22c602a105 100644
--- a/mm/ksm.c
+++ b/mm/ksm.c
@@ -1352,7 +1352,7 @@ static int write_protect_page(struct vm_area_struct *vma, struct folio *folio,
set_pte_at(mm, pvmw.address, pvmw.pte, entry);
}
- *orig_pte = entry;
+ set_pte_nopgtable(orig_pte, entry);
err = 0;
out_unlock:
--
2.53.0