[RFC PATCH 2/4] mm: introduce ptep_get_nopgtable() and set_pte_nopgtable() accessors
Alexander Gordeev <[email protected]> Fri, 17 Jul 2026 15:32:20 +0200
| Newsgroups | org.kernel.vger.linux-sparse,org.kernel.vger.linux-kernel,org.kernel.vger.linux-s390,org.kvack.linux-mm |
|---|---|
| Message-ID | <19627b802d38510a6e006ec666d3177278ad8a38.1784292223.git.agordeev@linux.ibm.com> |
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 <[email protected]>
---
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_ar=
ea_struct *vma,
#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
#endif
=20
+#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 =3D 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(st=
ruct folio *folio,
unsigned int max_nr, fpb_t flags)
{
bool any_writable =3D false, any_young =3D false, any_dirty =3D false;
- pte_t expected_pte, pte =3D *ptentp;
+ pte_t expected_pte, pte =3D ptep_get_nopgtable(ptentp);
unsigned int nr, cur_nr;
=20
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))) !=3D folio, foli=
o);
- /*
- * 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)));
=20
/* Limit max_nr to the actual remaining PFNs in the folio we could batc=
h. */
max_nr =3D min_t(unsigned long, max_nr,
@@ -379,12 +373,14 @@ static inline unsigned int folio_pte_batch_flags(st=
ruct folio *folio,
nr +=3D cur_nr;
}
=20
+ pte =3D ptep_get_nopgtable(ptentp);
if (any_writable)
- *ptentp =3D pte_mkwrite(*ptentp, vma);
+ pte =3D pte_mkwrite(pte, vma);
if (any_young)
- *ptentp =3D pte_mkyoung(*ptentp);
+ pte =3D pte_mkyoung(pte);
if (any_dirty)
- *ptentp =3D pte_mkdirty(*ptentp);
+ pte =3D pte_mkdirty(pte);
+ set_pte_nopgtable(ptentp, pte);
=20
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,
=20
set_pte_at(mm, pvmw.address, pvmw.pte, entry);
}
- *orig_pte =3D entry;
+ set_pte_nopgtable(orig_pte, entry);
err =3D 0;
=20
out_unlock:
--=20
2.53.0