[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