[PATCH v2 2/2] riscv: preserve hardware-updated A/D bits in PTE accessors
Andrew Jones
andrew.jones at oss.qualcomm.com
Fri May 22 13:01:57 PDT 2026
On Fri, May 22, 2026 at 10:23:58PM +0800, Yunhui Cui wrote:
> Use cmpxchg-based merges for live RISC-V PTE permission updates so
> software changes do not lose concurrently hardware-updated accessed and
> dirty state. Cover ptep_set_access_flags(),
> ptep_test_and_clear_young(), and ptep_set_wrprotect(), and extend the
> same wrprotect handling to the PUD leaf helper used by huge mappings.
>
> Keep the existing Svvptc flush behaviour, but only flush when the
> merged PTE value actually changed.
>
> Signed-off-by: Yunhui Cui <cuiyunhui at bytedance.com>
> Reviewed-by: Qingwei Hu <qingwei.hu at bytedance.com>
> ---
> arch/riscv/include/asm/pgtable.h | 19 +++++++--
> arch/riscv/mm/pgtable.c | 68 ++++++++++++++++++++++++++------
> 2 files changed, 73 insertions(+), 14 deletions(-)
>
> diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h
> index 20663a466cf6c..984c37ca8aef7 100644
> --- a/arch/riscv/include/asm/pgtable.h
> +++ b/arch/riscv/include/asm/pgtable.h
> @@ -668,15 +668,21 @@ static inline pte_t ptep_get_and_clear(struct mm_struct *mm,
> static inline void ptep_set_wrprotect(struct mm_struct *mm,
> unsigned long address, pte_t *ptep)
> {
> - pte_t read_pte = READ_ONCE(*ptep);
> + pte_t old_pte;
> + pte_t pte;
> /*
> * ptep_set_wrprotect can be called for shadow stack ranges too.
> * shadow stack memory is XWR = 010 and thus clearing _PAGE_WRITE will lead to
> * encoding 000b which is wrong encoding with V = 1. This should lead to page fault
> * but we dont want this wrong configuration to be set in page tables.
> */
> - atomic_long_set((atomic_long_t *)ptep,
> - ((pte_val(read_pte) & ~(unsigned long)_PAGE_WRITE) | _PAGE_READ));
> + pte = READ_ONCE(*ptep);
> + do {
> + old_pte = pte;
> + pte = pte_wrprotect(pte);
> + pte_val(pte) = cmpxchg_relaxed(&pte_val(*ptep), pte_val(old_pte),
> + pte_val(pte));
> + } while (pte_val(pte) != pte_val(old_pte));
> }
>
> #define __HAVE_ARCH_PTEP_CLEAR_YOUNG_FLUSH
> @@ -1030,6 +1036,13 @@ static inline void pmdp_set_wrprotect(struct mm_struct *mm,
> ptep_set_wrprotect(mm, address, (pte_t *)pmdp);
> }
>
> +#define __HAVE_ARCH_PUDP_SET_WRPROTECT
> +static inline void pudp_set_wrprotect(struct mm_struct *mm,
> + unsigned long address, pud_t *pudp)
> +{
> + ptep_set_wrprotect(mm, address, (pte_t *)pudp);
> +}
> +
> #define pmdp_establish pmdp_establish
> static inline pmd_t pmdp_establish(struct vm_area_struct *vma,
> unsigned long address, pmd_t *pmdp, pmd_t pmd)
> diff --git a/arch/riscv/mm/pgtable.c b/arch/riscv/mm/pgtable.c
> index 9c4427d0b1874..b77e82362442e 100644
> --- a/arch/riscv/mm/pgtable.c
> +++ b/arch/riscv/mm/pgtable.c
> @@ -5,23 +5,55 @@
> #include <linux/kernel.h>
> #include <linux/pgtable.h>
>
> +#define RISCV_PTE_ACCESS_FLAG_MASK (_PAGE_READ | _PAGE_WRITE | _PAGE_EXEC | \
> + _PAGE_ACCESSED | _PAGE_DIRTY | \
> + _PAGE_SOFT_DIRTY)
> +
> +static inline unsigned long riscv_pte_access_flags(unsigned long cur,
> + unsigned long entry)
> +{
> + unsigned long pteval;
> + unsigned long hw_flags;
> +
> + hw_flags = _PAGE_ACCESSED | _PAGE_DIRTY | _PAGE_SOFT_DIRTY;
Why are we setting _PAGE_SOFT_DIRTY in something called hw_flags? Do we
even need to consider _PAGE_SOFT_DIRTY here?
Thanks,
drew
More information about the linux-riscv
mailing list