[PATCH v2 02/20] arm64/pgtable: Clear AF with LDCLR on supported systems

From: James Houghton

Date: Fri Oct 02 2026 - 20:22:16 EST


Replace the cmpxchg loop with atomic64_fetch_andnot_relaxed, which, on
systems with LSE atomics, should end up as LDCLR.

This is somewhat overdue. In recent versions of the architecture, if a
system supports FEAT_LSE, forward progress guarantees for exclusive
monitors and cmpxchg loops are relaxed.

Mechanically, I would love to write test_and_clear_bit_relaxed(), but no
such function exists (test_and_clear_bit() has full ordering, which is
also undesirable). Implementing test_and_clear_bit_relaxed() properly is
a pretty large change; use atomic64 instead, as pte_t contains a u64.

Signed-off-by: James Houghton <jthoughton@xxxxxxxxxx>
---
arch/arm64/include/asm/pgtable.h | 14 ++++----------
1 file changed, 4 insertions(+), 10 deletions(-)

diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
index 763c5a411d64..e47c3d010715 100644
--- a/arch/arm64/include/asm/pgtable.h
+++ b/arch/arm64/include/asm/pgtable.h
@@ -1252,17 +1252,11 @@ static inline void __pte_clear(struct mm_struct *mm,
static inline bool __ptep_test_and_clear_young(struct vm_area_struct *vma,
unsigned long address, pte_t *ptep)
{
- pte_t old_pte, pte;
+ atomic64_t *pteval = (atomic64_t *)&pte_val(*ptep);
+ s64 af_mask = PTE_AF;

- pte = __ptep_get(ptep);
- do {
- old_pte = pte;
- pte = pte_mkold(pte);
- pte_val(pte) = cmpxchg_relaxed(&pte_val(*ptep),
- pte_val(old_pte), pte_val(pte));
- } while (pte_val(pte) != pte_val(old_pte));
-
- return pte_young(pte);
+ /* Atomically clear PTE_AF, checking that it was set before. */
+ return af_mask & atomic64_fetch_andnot_relaxed(af_mask, pteval);
}

static inline bool __ptep_clear_flush_young(struct vm_area_struct *vma,
--
2.56.0.rc1.315.gc6ed9934b7-goog