Linux-RISC-V Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 1/2] riscv: track effective hardware PTE A/D updates
@ 2026-05-22 14:23 Yunhui Cui
  2026-05-22 14:23 ` [PATCH v2 2/2] riscv: preserve hardware-updated A/D bits in PTE accessors Yunhui Cui
                   ` (2 more replies)
  0 siblings, 3 replies; 10+ messages in thread
From: Yunhui Cui @ 2026-05-22 14:23 UTC (permalink / raw)
  To: pjw, palmer, aou, alex, akpm, pasha.tatashin, andrew+kernel,
	rmclure, debug, baolin.wang, zhangchunyan, cuiyunhui, apopple,
	namcao, wangruikang, apatel, liu.xuemei1, ajones, cleger, charlie,
	hui.wang, guodong, pincheng.plct, linux-riscv, linux-kernel
  Cc: Qingwei Hu

Track the effective hardware PTE A/D update mode separately from
Svadu capability discovery. When both Svade and Svadu are present,
enable SBI FWFT PTE A/D hardware updating on each online CPU via
CPUHP and use the resulting runtime state for arch_has_hw_pte_young().
Fall back to software-managed A/D updates if enabling hardware updates
fails.

When Svadu is present without Svade, assume hardware PTE A/D updating
is enabled from boot, and document that boot-time behavior in the DT
binding.

Signed-off-by: Yunhui Cui <cuiyunhui@bytedance.com>
Reviewed-by: Qingwei Hu <qingwei.hu@bytedance.com>
---
 .../devicetree/bindings/riscv/extensions.yaml |  6 +-
 arch/riscv/include/asm/cpufeature.h           |  6 ++
 arch/riscv/include/asm/pgtable.h              |  8 +--
 arch/riscv/kernel/cpufeature.c                | 58 +++++++++++++++++--
 4 files changed, 65 insertions(+), 13 deletions(-)

diff --git a/Documentation/devicetree/bindings/riscv/extensions.yaml b/Documentation/devicetree/bindings/riscv/extensions.yaml
index 2b0a8a93bb214..b09888e9988de 100644
--- a/Documentation/devicetree/bindings/riscv/extensions.yaml
+++ b/Documentation/devicetree/bindings/riscv/extensions.yaml
@@ -294,10 +294,10 @@ properties:
                of the PTE A/D bits or page faults when they need updated.
             2) Only Svade present in DT => Supervisor must assume Svade to be
                always enabled.
-            3) Only Svadu present in DT => Supervisor must assume Svadu to be
-               always enabled.
+            3) Only Svadu present in DT => Supervisor must assume Svadu is
+               enabled at boot.
             4) Both Svade and Svadu present in DT => Supervisor must assume
-               Svadu turned-off at boot time. To use Svadu, supervisor must
+               Svadu is disabled at boot time. To use Svadu, supervisor must
                explicitly enable it using the SBI FWFT extension.
 
         - const: svadu
diff --git a/arch/riscv/include/asm/cpufeature.h b/arch/riscv/include/asm/cpufeature.h
index 739fcc84bf7b2..ad9fad6eee55d 100644
--- a/arch/riscv/include/asm/cpufeature.h
+++ b/arch/riscv/include/asm/cpufeature.h
@@ -128,6 +128,12 @@ struct riscv_isa_ext_data {
 extern const struct riscv_isa_ext_data riscv_isa_ext[];
 extern const size_t riscv_isa_ext_count;
 extern bool riscv_isa_fallback;
+DECLARE_STATIC_KEY_FALSE(riscv_hw_pte_ad_updating);
+
+static __always_inline bool riscv_has_hw_pte_ad_updating(void)
+{
+	return static_branch_unlikely(&riscv_hw_pte_ad_updating);
+}
 
 unsigned long riscv_isa_extension_base(const unsigned long *isa_bitmap);
 static __always_inline bool riscv_cpu_has_extension_likely(int cpu, const unsigned long ext)
diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h
index a1a7c6520a095..20663a466cf6c 100644
--- a/arch/riscv/include/asm/pgtable.h
+++ b/arch/riscv/include/asm/pgtable.h
@@ -732,14 +732,14 @@ static inline pgprot_t pgprot_writecombine(pgprot_t _prot)
 #define pgprot_dmacoherent pgprot_writecombine
 
 /*
- * Both Svade and Svadu control the hardware behavior when the PTE A/D bits need to be set. By
- * default the M-mode firmware enables the hardware updating scheme when only Svadu is present in
- * DT.
+ * Both Svade and Svadu control the hardware behavior when the PTE A/D bits
+ * need to be set. The core MM code only cares whether hardware updating of
+ * the accessed/dirty state is currently active.
  */
 #define arch_has_hw_pte_young arch_has_hw_pte_young
 static inline bool arch_has_hw_pte_young(void)
 {
-	return riscv_has_extension_unlikely(RISCV_ISA_EXT_SVADU);
+	return riscv_has_hw_pte_ad_updating();
 }
 
 /*
diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
index f46aa5602d74d..831dd6a7c1a06 100644
--- a/arch/riscv/kernel/cpufeature.c
+++ b/arch/riscv/kernel/cpufeature.c
@@ -35,6 +35,7 @@
 static bool any_cpu_has_zicboz;
 static bool any_cpu_has_zicbop;
 static bool any_cpu_has_zicbom;
+DEFINE_STATIC_KEY_FALSE(riscv_hw_pte_ad_updating);
 
 unsigned long elf_hwcap __read_mostly;
 
@@ -287,15 +288,60 @@ static int riscv_ext_zvfbfwma_validate(const struct riscv_isa_ext_data *data,
 	return -EPROBE_DEFER;
 }
 
-static int riscv_ext_svadu_validate(const struct riscv_isa_ext_data *data,
-				    const unsigned long *isa_bitmap)
+static void riscv_set_hw_pte_ad_updating(void)
+{
+	static_branch_enable(&riscv_hw_pte_ad_updating);
+}
+
+static int riscv_hw_pte_ad_updating_starting(unsigned int cpu)
+{
+	int ret;
+
+	ret = sbi_fwft_set(SBI_FWFT_PTE_AD_HW_UPDATING, 1, 0);
+	if (ret) {
+		if (ret != -EOPNOTSUPP)
+			pr_err("CPU%u failed to enable hardware PTE A/D updating: %d\n",
+			       cpu, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int __init riscv_hw_pte_ad_updating_init(void)
 {
-	/* SVADE has already been detected, use SVADE only */
-	if (__riscv_isa_extension_available(isa_bitmap, RISCV_ISA_EXT_SVADE))
-		return -EOPNOTSUPP;
+	bool has_svade, has_svadu;
+	int state;
 
+	has_svade = riscv_has_extension_unlikely(RISCV_ISA_EXT_SVADE);
+	has_svadu = riscv_has_extension_unlikely(RISCV_ISA_EXT_SVADU);
+
+	if (!has_svadu)
+		return 0;
+
+	if (!has_svade)
+		goto enable;
+
+	state = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
+				  "riscv/pte-ad:starting",
+				  riscv_hw_pte_ad_updating_starting,
+				  NULL);
+	if (state < 0) {
+		pr_info("riscv: leave PTE A/D updates software-managed (%d)\n",
+			state);
+		return 0;
+	}
+
+	/*
+	 * A successful CPUHP_AP_ONLINE_DYN registration means the startup
+	 * callback has already succeeded on all online CPUs.
+	 */
+enable:
+	riscv_set_hw_pte_ad_updating();
+	pr_debug("riscv: hardware PTE A/D updating enabled\n");
 	return 0;
 }
+arch_initcall(riscv_hw_pte_ad_updating_init);
 
 static int riscv_cfilp_validate(const struct riscv_isa_ext_data *data,
 				const unsigned long *isa_bitmap)
@@ -584,7 +630,7 @@ const struct riscv_isa_ext_data riscv_isa_ext[] = {
 	__RISCV_ISA_EXT_SUPERSET(ssnpm, RISCV_ISA_EXT_SSNPM, riscv_xlinuxenvcfg_exts),
 	__RISCV_ISA_EXT_DATA(sstc, RISCV_ISA_EXT_SSTC),
 	__RISCV_ISA_EXT_DATA(svade, RISCV_ISA_EXT_SVADE),
-	__RISCV_ISA_EXT_DATA_VALIDATE(svadu, RISCV_ISA_EXT_SVADU, riscv_ext_svadu_validate),
+	__RISCV_ISA_EXT_DATA(svadu, RISCV_ISA_EXT_SVADU),
 	__RISCV_ISA_EXT_DATA(svinval, RISCV_ISA_EXT_SVINVAL),
 	__RISCV_ISA_EXT_DATA(svnapot, RISCV_ISA_EXT_SVNAPOT),
 	__RISCV_ISA_EXT_DATA(svpbmt, RISCV_ISA_EXT_SVPBMT),
-- 
2.39.5


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v2 2/2] riscv: preserve hardware-updated A/D bits in PTE accessors
  2026-05-22 14:23 [PATCH v2 1/2] riscv: track effective hardware PTE A/D updates Yunhui Cui
@ 2026-05-22 14:23 ` Yunhui Cui
  2026-05-22 20:01   ` Andrew Jones
  2026-05-22 19:57 ` [PATCH v2 1/2] riscv: track effective hardware PTE A/D updates Andrew Jones
  2026-06-05 11:44 ` Inochi Amaoto
  2 siblings, 1 reply; 10+ messages in thread
From: Yunhui Cui @ 2026-05-22 14:23 UTC (permalink / raw)
  To: pjw, palmer, aou, alex, akpm, pasha.tatashin, andrew+kernel,
	rmclure, debug, baolin.wang, zhangchunyan, cuiyunhui, apopple,
	namcao, wangruikang, apatel, liu.xuemei1, ajones, cleger, charlie,
	hui.wang, guodong, pincheng.plct, linux-riscv, linux-kernel
  Cc: Qingwei Hu

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@bytedance.com>
Reviewed-by: Qingwei Hu <qingwei.hu@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;
+	pteval = cur & ~RISCV_PTE_ACCESS_FLAG_MASK;
+	pteval |= entry & (RISCV_PTE_ACCESS_FLAG_MASK & ~hw_flags);
+	pteval |= (cur | entry) & hw_flags;
+
+	return pteval;
+}
+
 int ptep_set_access_flags(struct vm_area_struct *vma,
 			  unsigned long address, pte_t *ptep,
 			  pte_t entry, int dirty)
 {
+	unsigned long old_pteval;
+	unsigned long new_pteval;
+	unsigned long prev_pteval;
+	bool changed;
+
+	old_pteval = pte_val(ptep_get(ptep));
+	do {
+		new_pteval = riscv_pte_access_flags(old_pteval, pte_val(entry));
+		if (new_pteval == old_pteval)
+			break;
+
+		prev_pteval = cmpxchg_relaxed(&pte_val(*ptep), old_pteval,
+					      new_pteval);
+		if (prev_pteval == old_pteval)
+			break;
+
+		old_pteval = prev_pteval;
+	} while (1);
+
+	changed = old_pteval != new_pteval;
 	if (riscv_has_extension_unlikely(RISCV_ISA_EXT_SVVPTC)) {
-		if (!pte_same(ptep_get(ptep), entry)) {
-			__set_pte_at(vma->vm_mm, ptep, entry);
-			/* Here only not svadu is impacted */
+		if (changed)
 			flush_tlb_page(vma, address);
-			return true;
-		}
 
-		return false;
+		return changed;
 	}
 
-	if (!pte_same(ptep_get(ptep), entry))
-		__set_pte_at(vma->vm_mm, ptep, entry);
 	/*
 	 * update_mmu_cache will unconditionally execute, handling both
 	 * the case that the PTE changed and the spurious fault case.
@@ -32,9 +64,23 @@ int ptep_set_access_flags(struct vm_area_struct *vma,
 bool ptep_test_and_clear_young(struct vm_area_struct *vma,
 		unsigned long address, pte_t *ptep)
 {
-	if (!pte_young(ptep_get(ptep)))
-		return false;
-	return test_and_clear_bit(_PAGE_ACCESSED_OFFSET, &pte_val(*ptep));
+	unsigned long old_pteval;
+	unsigned long new_pteval;
+	unsigned long prev_pteval;
+
+	old_pteval = pte_val(ptep_get(ptep));
+	do {
+		if (!(old_pteval & _PAGE_ACCESSED))
+			return false;
+
+		new_pteval = pte_val(pte_mkold(__pte(old_pteval)));
+		prev_pteval = cmpxchg_relaxed(&pte_val(*ptep), old_pteval,
+					      new_pteval);
+		if (prev_pteval == old_pteval)
+			return true;
+
+		old_pteval = prev_pteval;
+	} while (1);
 }
 EXPORT_SYMBOL_GPL(ptep_test_and_clear_young);
 
-- 
2.39.5


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH v2 1/2] riscv: track effective hardware PTE A/D updates
  2026-05-22 14:23 [PATCH v2 1/2] riscv: track effective hardware PTE A/D updates Yunhui Cui
  2026-05-22 14:23 ` [PATCH v2 2/2] riscv: preserve hardware-updated A/D bits in PTE accessors Yunhui Cui
@ 2026-05-22 19:57 ` Andrew Jones
  2026-05-23  3:58   ` [External] " yunhui cui
  2026-06-05 11:44 ` Inochi Amaoto
  2 siblings, 1 reply; 10+ messages in thread
From: Andrew Jones @ 2026-05-22 19:57 UTC (permalink / raw)
  To: Yunhui Cui
  Cc: pjw, palmer, aou, alex, akpm, pasha.tatashin, andrew+kernel,
	rmclure, debug, baolin.wang, zhangchunyan, apopple, namcao,
	wangruikang, apatel, liu.xuemei1, ajones, cleger, charlie,
	hui.wang, guodong, pincheng.plct, linux-riscv, linux-kernel,
	Qingwei Hu

On Fri, May 22, 2026 at 10:23:57PM +0800, Yunhui Cui wrote:
> Track the effective hardware PTE A/D update mode separately from
> Svadu capability discovery. When both Svade and Svadu are present,
> enable SBI FWFT PTE A/D hardware updating on each online CPU via
> CPUHP and use the resulting runtime state for arch_has_hw_pte_young().
> Fall back to software-managed A/D updates if enabling hardware updates
> fails.
> 
> When Svadu is present without Svade, assume hardware PTE A/D updating
> is enabled from boot, and document that boot-time behavior in the DT
> binding.
> 
> Signed-off-by: Yunhui Cui <cuiyunhui@bytedance.com>
> Reviewed-by: Qingwei Hu <qingwei.hu@bytedance.com>
> ---
>  .../devicetree/bindings/riscv/extensions.yaml |  6 +-
>  arch/riscv/include/asm/cpufeature.h           |  6 ++
>  arch/riscv/include/asm/pgtable.h              |  8 +--
>  arch/riscv/kernel/cpufeature.c                | 58 +++++++++++++++++--
>  4 files changed, 65 insertions(+), 13 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/riscv/extensions.yaml b/Documentation/devicetree/bindings/riscv/extensions.yaml
> index 2b0a8a93bb214..b09888e9988de 100644
> --- a/Documentation/devicetree/bindings/riscv/extensions.yaml
> +++ b/Documentation/devicetree/bindings/riscv/extensions.yaml
> @@ -294,10 +294,10 @@ properties:
>                 of the PTE A/D bits or page faults when they need updated.
>              2) Only Svade present in DT => Supervisor must assume Svade to be
>                 always enabled.
> -            3) Only Svadu present in DT => Supervisor must assume Svadu to be
> -               always enabled.
> +            3) Only Svadu present in DT => Supervisor must assume Svadu is
> +               enabled at boot.
>              4) Both Svade and Svadu present in DT => Supervisor must assume
> -               Svadu turned-off at boot time. To use Svadu, supervisor must
> +               Svadu is disabled at boot time. To use Svadu, supervisor must
>                 explicitly enable it using the SBI FWFT extension.

Looks good to me, but I think dt binding changes are typically in separate
patches.

>  
>          - const: svadu
> diff --git a/arch/riscv/include/asm/cpufeature.h b/arch/riscv/include/asm/cpufeature.h
> index 739fcc84bf7b2..ad9fad6eee55d 100644
> --- a/arch/riscv/include/asm/cpufeature.h
> +++ b/arch/riscv/include/asm/cpufeature.h
> @@ -128,6 +128,12 @@ struct riscv_isa_ext_data {
>  extern const struct riscv_isa_ext_data riscv_isa_ext[];
>  extern const size_t riscv_isa_ext_count;
>  extern bool riscv_isa_fallback;
> +DECLARE_STATIC_KEY_FALSE(riscv_hw_pte_ad_updating);
> +
> +static __always_inline bool riscv_has_hw_pte_ad_updating(void)
> +{
> +	return static_branch_unlikely(&riscv_hw_pte_ad_updating);
> +}
>  
>  unsigned long riscv_isa_extension_base(const unsigned long *isa_bitmap);
>  static __always_inline bool riscv_cpu_has_extension_likely(int cpu, const unsigned long ext)
> diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h
> index a1a7c6520a095..20663a466cf6c 100644
> --- a/arch/riscv/include/asm/pgtable.h
> +++ b/arch/riscv/include/asm/pgtable.h
> @@ -732,14 +732,14 @@ static inline pgprot_t pgprot_writecombine(pgprot_t _prot)
>  #define pgprot_dmacoherent pgprot_writecombine
>  
>  /*
> - * Both Svade and Svadu control the hardware behavior when the PTE A/D bits need to be set. By
> - * default the M-mode firmware enables the hardware updating scheme when only Svadu is present in
> - * DT.
> + * Both Svade and Svadu control the hardware behavior when the PTE A/D bits
> + * need to be set. The core MM code only cares whether hardware updating of
> + * the accessed/dirty state is currently active.
>   */
>  #define arch_has_hw_pte_young arch_has_hw_pte_young
>  static inline bool arch_has_hw_pte_young(void)
>  {
> -	return riscv_has_extension_unlikely(RISCV_ISA_EXT_SVADU);
> +	return riscv_has_hw_pte_ad_updating();
>  }
>  
>  /*
> diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
> index f46aa5602d74d..831dd6a7c1a06 100644
> --- a/arch/riscv/kernel/cpufeature.c
> +++ b/arch/riscv/kernel/cpufeature.c
> @@ -35,6 +35,7 @@
>  static bool any_cpu_has_zicboz;
>  static bool any_cpu_has_zicbop;
>  static bool any_cpu_has_zicbom;
> +DEFINE_STATIC_KEY_FALSE(riscv_hw_pte_ad_updating);
>  
>  unsigned long elf_hwcap __read_mostly;
>  
> @@ -287,15 +288,60 @@ static int riscv_ext_zvfbfwma_validate(const struct riscv_isa_ext_data *data,
>  	return -EPROBE_DEFER;
>  }
>  
> -static int riscv_ext_svadu_validate(const struct riscv_isa_ext_data *data,
> -				    const unsigned long *isa_bitmap)
> +static void riscv_set_hw_pte_ad_updating(void)
> +{
> +	static_branch_enable(&riscv_hw_pte_ad_updating);
> +}
> +
> +static int riscv_hw_pte_ad_updating_starting(unsigned int cpu)
> +{
> +	int ret;
> +
> +	ret = sbi_fwft_set(SBI_FWFT_PTE_AD_HW_UPDATING, 1, 0);
> +	if (ret) {
> +		if (ret != -EOPNOTSUPP)
> +			pr_err("CPU%u failed to enable hardware PTE A/D updating: %d\n",
> +			       cpu, ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int __init riscv_hw_pte_ad_updating_init(void)
>  {
> -	/* SVADE has already been detected, use SVADE only */
> -	if (__riscv_isa_extension_available(isa_bitmap, RISCV_ISA_EXT_SVADE))
> -		return -EOPNOTSUPP;
> +	bool has_svade, has_svadu;
> +	int state;
>  
> +	has_svade = riscv_has_extension_unlikely(RISCV_ISA_EXT_SVADE);
> +	has_svadu = riscv_has_extension_unlikely(RISCV_ISA_EXT_SVADU);
> +
> +	if (!has_svadu)
> +		return 0;
> +
> +	if (!has_svade)
> +		goto enable;
> +
> +	state = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,

I don't think CPUHP_AP_ONLINE_DYN is correct. Setting HW A/D is something
that should be done at _STARTING time.

Thanks,
drew

> +				  "riscv/pte-ad:starting",
> +				  riscv_hw_pte_ad_updating_starting,
> +				  NULL);
> +	if (state < 0) {
> +		pr_info("riscv: leave PTE A/D updates software-managed (%d)\n",
> +			state);
> +		return 0;
> +	}
> +
> +	/*
> +	 * A successful CPUHP_AP_ONLINE_DYN registration means the startup
> +	 * callback has already succeeded on all online CPUs.
> +	 */
> +enable:
> +	riscv_set_hw_pte_ad_updating();
> +	pr_debug("riscv: hardware PTE A/D updating enabled\n");
>  	return 0;
>  }
> +arch_initcall(riscv_hw_pte_ad_updating_init);
>  
>  static int riscv_cfilp_validate(const struct riscv_isa_ext_data *data,
>  				const unsigned long *isa_bitmap)
> @@ -584,7 +630,7 @@ const struct riscv_isa_ext_data riscv_isa_ext[] = {
>  	__RISCV_ISA_EXT_SUPERSET(ssnpm, RISCV_ISA_EXT_SSNPM, riscv_xlinuxenvcfg_exts),
>  	__RISCV_ISA_EXT_DATA(sstc, RISCV_ISA_EXT_SSTC),
>  	__RISCV_ISA_EXT_DATA(svade, RISCV_ISA_EXT_SVADE),
> -	__RISCV_ISA_EXT_DATA_VALIDATE(svadu, RISCV_ISA_EXT_SVADU, riscv_ext_svadu_validate),
> +	__RISCV_ISA_EXT_DATA(svadu, RISCV_ISA_EXT_SVADU),
>  	__RISCV_ISA_EXT_DATA(svinval, RISCV_ISA_EXT_SVINVAL),
>  	__RISCV_ISA_EXT_DATA(svnapot, RISCV_ISA_EXT_SVNAPOT),
>  	__RISCV_ISA_EXT_DATA(svpbmt, RISCV_ISA_EXT_SVPBMT),
> -- 
> 2.39.5
> 
> 
> _______________________________________________
> linux-riscv mailing list
> linux-riscv@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-riscv

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v2 2/2] riscv: preserve hardware-updated A/D bits in PTE accessors
  2026-05-22 14:23 ` [PATCH v2 2/2] riscv: preserve hardware-updated A/D bits in PTE accessors Yunhui Cui
@ 2026-05-22 20:01   ` Andrew Jones
  2026-05-25  2:57     ` [External] " yunhui cui
  0 siblings, 1 reply; 10+ messages in thread
From: Andrew Jones @ 2026-05-22 20:01 UTC (permalink / raw)
  To: Yunhui Cui
  Cc: pjw, palmer, aou, alex, akpm, pasha.tatashin, andrew+kernel,
	rmclure, debug, baolin.wang, zhangchunyan, apopple, namcao,
	wangruikang, apatel, liu.xuemei1, ajones, cleger, charlie,
	hui.wang, guodong, pincheng.plct, linux-riscv, linux-kernel,
	Qingwei Hu

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@bytedance.com>
> Reviewed-by: Qingwei Hu <qingwei.hu@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

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [External] Re: [PATCH v2 1/2] riscv: track effective hardware PTE A/D updates
  2026-05-22 19:57 ` [PATCH v2 1/2] riscv: track effective hardware PTE A/D updates Andrew Jones
@ 2026-05-23  3:58   ` yunhui cui
  2026-05-24 20:44     ` Andrew Jones
  0 siblings, 1 reply; 10+ messages in thread
From: yunhui cui @ 2026-05-23  3:58 UTC (permalink / raw)
  To: Andrew Jones
  Cc: pjw, palmer, aou, alex, akpm, pasha.tatashin, andrew+kernel,
	rmclure, debug, baolin.wang, zhangchunyan, apopple, namcao,
	wangruikang, apatel, liu.xuemei1, ajones, cleger, charlie,
	hui.wang, guodong, pincheng.plct, linux-riscv, linux-kernel,
	Qingwei Hu

Hi Andrew,

On Sat, May 23, 2026 at 3:58 AM Andrew Jones
<andrew.jones@oss.qualcomm.com> wrote:
>
> On Fri, May 22, 2026 at 10:23:57PM +0800, Yunhui Cui wrote:
> > Track the effective hardware PTE A/D update mode separately from
> > Svadu capability discovery. When both Svade and Svadu are present,
> > enable SBI FWFT PTE A/D hardware updating on each online CPU via
> > CPUHP and use the resulting runtime state for arch_has_hw_pte_young().
> > Fall back to software-managed A/D updates if enabling hardware updates
> > fails.
> >
> > When Svadu is present without Svade, assume hardware PTE A/D updating
> > is enabled from boot, and document that boot-time behavior in the DT
> > binding.
> >
> > Signed-off-by: Yunhui Cui <cuiyunhui@bytedance.com>
> > Reviewed-by: Qingwei Hu <qingwei.hu@bytedance.com>
> > ---
> >  .../devicetree/bindings/riscv/extensions.yaml |  6 +-
> >  arch/riscv/include/asm/cpufeature.h           |  6 ++
> >  arch/riscv/include/asm/pgtable.h              |  8 +--
> >  arch/riscv/kernel/cpufeature.c                | 58 +++++++++++++++++--
> >  4 files changed, 65 insertions(+), 13 deletions(-)
> >
> > diff --git a/Documentation/devicetree/bindings/riscv/extensions.yaml b/Documentation/devicetree/bindings/riscv/extensions.yaml
> > index 2b0a8a93bb214..b09888e9988de 100644
> > --- a/Documentation/devicetree/bindings/riscv/extensions.yaml
> > +++ b/Documentation/devicetree/bindings/riscv/extensions.yaml
> > @@ -294,10 +294,10 @@ properties:
> >                 of the PTE A/D bits or page faults when they need updated.
> >              2) Only Svade present in DT => Supervisor must assume Svade to be
> >                 always enabled.
> > -            3) Only Svadu present in DT => Supervisor must assume Svadu to be
> > -               always enabled.
> > +            3) Only Svadu present in DT => Supervisor must assume Svadu is
> > +               enabled at boot.
> >              4) Both Svade and Svadu present in DT => Supervisor must assume
> > -               Svadu turned-off at boot time. To use Svadu, supervisor must
> > +               Svadu is disabled at boot time. To use Svadu, supervisor must
> >                 explicitly enable it using the SBI FWFT extension.
>
> Looks good to me, but I think dt binding changes are typically in separate
> patches.

Okay, I'll split the DT binding update into a separate patch.

>
> >
> >          - const: svadu
> > diff --git a/arch/riscv/include/asm/cpufeature.h b/arch/riscv/include/asm/cpufeature.h
> > index 739fcc84bf7b2..ad9fad6eee55d 100644
> > --- a/arch/riscv/include/asm/cpufeature.h
> > +++ b/arch/riscv/include/asm/cpufeature.h
> > @@ -128,6 +128,12 @@ struct riscv_isa_ext_data {
> >  extern const struct riscv_isa_ext_data riscv_isa_ext[];
> >  extern const size_t riscv_isa_ext_count;
> >  extern bool riscv_isa_fallback;
> > +DECLARE_STATIC_KEY_FALSE(riscv_hw_pte_ad_updating);
> > +
> > +static __always_inline bool riscv_has_hw_pte_ad_updating(void)
> > +{
> > +     return static_branch_unlikely(&riscv_hw_pte_ad_updating);
> > +}
> >
> >  unsigned long riscv_isa_extension_base(const unsigned long *isa_bitmap);
> >  static __always_inline bool riscv_cpu_has_extension_likely(int cpu, const unsigned long ext)
> > diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h
> > index a1a7c6520a095..20663a466cf6c 100644
> > --- a/arch/riscv/include/asm/pgtable.h
> > +++ b/arch/riscv/include/asm/pgtable.h
> > @@ -732,14 +732,14 @@ static inline pgprot_t pgprot_writecombine(pgprot_t _prot)
> >  #define pgprot_dmacoherent pgprot_writecombine
> >
> >  /*
> > - * Both Svade and Svadu control the hardware behavior when the PTE A/D bits need to be set. By
> > - * default the M-mode firmware enables the hardware updating scheme when only Svadu is present in
> > - * DT.
> > + * Both Svade and Svadu control the hardware behavior when the PTE A/D bits
> > + * need to be set. The core MM code only cares whether hardware updating of
> > + * the accessed/dirty state is currently active.
> >   */
> >  #define arch_has_hw_pte_young arch_has_hw_pte_young
> >  static inline bool arch_has_hw_pte_young(void)
> >  {
> > -     return riscv_has_extension_unlikely(RISCV_ISA_EXT_SVADU);
> > +     return riscv_has_hw_pte_ad_updating();
> >  }
> >
> >  /*
> > diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
> > index f46aa5602d74d..831dd6a7c1a06 100644
> > --- a/arch/riscv/kernel/cpufeature.c
> > +++ b/arch/riscv/kernel/cpufeature.c
> > @@ -35,6 +35,7 @@
> >  static bool any_cpu_has_zicboz;
> >  static bool any_cpu_has_zicbop;
> >  static bool any_cpu_has_zicbom;
> > +DEFINE_STATIC_KEY_FALSE(riscv_hw_pte_ad_updating);
> >
> >  unsigned long elf_hwcap __read_mostly;
> >
> > @@ -287,15 +288,60 @@ static int riscv_ext_zvfbfwma_validate(const struct riscv_isa_ext_data *data,
> >       return -EPROBE_DEFER;
> >  }
> >
> > -static int riscv_ext_svadu_validate(const struct riscv_isa_ext_data *data,
> > -                                 const unsigned long *isa_bitmap)
> > +static void riscv_set_hw_pte_ad_updating(void)
> > +{
> > +     static_branch_enable(&riscv_hw_pte_ad_updating);
> > +}
> > +
> > +static int riscv_hw_pte_ad_updating_starting(unsigned int cpu)
> > +{
> > +     int ret;
> > +
> > +     ret = sbi_fwft_set(SBI_FWFT_PTE_AD_HW_UPDATING, 1, 0);
> > +     if (ret) {
> > +             if (ret != -EOPNOTSUPP)
> > +                     pr_err("CPU%u failed to enable hardware PTE A/D updating: %d\n",
> > +                            cpu, ret);
> > +             return ret;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int __init riscv_hw_pte_ad_updating_init(void)
> >  {
> > -     /* SVADE has already been detected, use SVADE only */
> > -     if (__riscv_isa_extension_available(isa_bitmap, RISCV_ISA_EXT_SVADE))
> > -             return -EOPNOTSUPP;
> > +     bool has_svade, has_svadu;
> > +     int state;
> >
> > +     has_svade = riscv_has_extension_unlikely(RISCV_ISA_EXT_SVADE);
> > +     has_svadu = riscv_has_extension_unlikely(RISCV_ISA_EXT_SVADU);
> > +
> > +     if (!has_svadu)
> > +             return 0;
> > +
> > +     if (!has_svade)
> > +             goto enable;
> > +
> > +     state = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
>
> I don't think CPUHP_AP_ONLINE_DYN is correct. Setting HW A/D is something
> that should be done at _STARTING time.

I don't think moving this to _STARTING is the right fix, because this
callback can fail, while _STARTING callbacks are not supposed to fail.

CPUHP_AP_ONLINE_DYN already covers both the CPUs that are online when
cpuhp_setup_state() is called and CPUs brought online later, e.g. with
maxcpus=. If FWFT setup fails for a later CPU, only that CPU's online
operation is aborted.

The immediate issue in my current code is the missing teardown/rollback
handling.

>
> Thanks,
> drew
>
> > +                               "riscv/pte-ad:starting",
> > +                               riscv_hw_pte_ad_updating_starting,
> > +                               NULL);
> > +     if (state < 0) {
> > +             pr_info("riscv: leave PTE A/D updates software-managed (%d)\n",
> > +                     state);
> > +             return 0;
> > +     }
> > +
> > +     /*
> > +      * A successful CPUHP_AP_ONLINE_DYN registration means the startup
> > +      * callback has already succeeded on all online CPUs.
> > +      */
> > +enable:
> > +     riscv_set_hw_pte_ad_updating();
> > +     pr_debug("riscv: hardware PTE A/D updating enabled\n");
> >       return 0;
> >  }
> > +arch_initcall(riscv_hw_pte_ad_updating_init);
> >
> >  static int riscv_cfilp_validate(const struct riscv_isa_ext_data *data,
> >                               const unsigned long *isa_bitmap)
> > @@ -584,7 +630,7 @@ const struct riscv_isa_ext_data riscv_isa_ext[] = {
> >       __RISCV_ISA_EXT_SUPERSET(ssnpm, RISCV_ISA_EXT_SSNPM, riscv_xlinuxenvcfg_exts),
> >       __RISCV_ISA_EXT_DATA(sstc, RISCV_ISA_EXT_SSTC),
> >       __RISCV_ISA_EXT_DATA(svade, RISCV_ISA_EXT_SVADE),
> > -     __RISCV_ISA_EXT_DATA_VALIDATE(svadu, RISCV_ISA_EXT_SVADU, riscv_ext_svadu_validate),
> > +     __RISCV_ISA_EXT_DATA(svadu, RISCV_ISA_EXT_SVADU),
> >       __RISCV_ISA_EXT_DATA(svinval, RISCV_ISA_EXT_SVINVAL),
> >       __RISCV_ISA_EXT_DATA(svnapot, RISCV_ISA_EXT_SVNAPOT),
> >       __RISCV_ISA_EXT_DATA(svpbmt, RISCV_ISA_EXT_SVPBMT),
> > --
> > 2.39.5
> >
> >
> > _______________________________________________
> > linux-riscv mailing list
> > linux-riscv@lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/linux-riscv


Thanks,
Yunhui

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [External] Re: [PATCH v2 1/2] riscv: track effective hardware PTE A/D updates
  2026-05-23  3:58   ` [External] " yunhui cui
@ 2026-05-24 20:44     ` Andrew Jones
  2026-06-09  6:38       ` yunhui cui
  0 siblings, 1 reply; 10+ messages in thread
From: Andrew Jones @ 2026-05-24 20:44 UTC (permalink / raw)
  To: yunhui cui
  Cc: pjw, palmer, aou, alex, akpm, pasha.tatashin, andrew+kernel,
	rmclure, debug, baolin.wang, zhangchunyan, apopple, namcao,
	wangruikang, apatel, liu.xuemei1, ajones, cleger, charlie,
	hui.wang, guodong, pincheng.plct, linux-riscv, linux-kernel,
	Qingwei Hu

On Sat, May 23, 2026 at 11:58:05AM +0800, yunhui cui wrote:
> Hi Andrew,
> 
> On Sat, May 23, 2026 at 3:58 AM Andrew Jones
> <andrew.jones@oss.qualcomm.com> wrote:
> >
> > On Fri, May 22, 2026 at 10:23:57PM +0800, Yunhui Cui wrote:
> > > Track the effective hardware PTE A/D update mode separately from
> > > Svadu capability discovery. When both Svade and Svadu are present,
> > > enable SBI FWFT PTE A/D hardware updating on each online CPU via
> > > CPUHP and use the resulting runtime state for arch_has_hw_pte_young().
> > > Fall back to software-managed A/D updates if enabling hardware updates
> > > fails.
> > >
> > > When Svadu is present without Svade, assume hardware PTE A/D updating
> > > is enabled from boot, and document that boot-time behavior in the DT
> > > binding.
> > >
> > > Signed-off-by: Yunhui Cui <cuiyunhui@bytedance.com>
> > > Reviewed-by: Qingwei Hu <qingwei.hu@bytedance.com>
> > > ---
> > >  .../devicetree/bindings/riscv/extensions.yaml |  6 +-
> > >  arch/riscv/include/asm/cpufeature.h           |  6 ++
> > >  arch/riscv/include/asm/pgtable.h              |  8 +--
> > >  arch/riscv/kernel/cpufeature.c                | 58 +++++++++++++++++--
> > >  4 files changed, 65 insertions(+), 13 deletions(-)
> > >
> > > diff --git a/Documentation/devicetree/bindings/riscv/extensions.yaml b/Documentation/devicetree/bindings/riscv/extensions.yaml
> > > index 2b0a8a93bb214..b09888e9988de 100644
> > > --- a/Documentation/devicetree/bindings/riscv/extensions.yaml
> > > +++ b/Documentation/devicetree/bindings/riscv/extensions.yaml
> > > @@ -294,10 +294,10 @@ properties:
> > >                 of the PTE A/D bits or page faults when they need updated.
> > >              2) Only Svade present in DT => Supervisor must assume Svade to be
> > >                 always enabled.
> > > -            3) Only Svadu present in DT => Supervisor must assume Svadu to be
> > > -               always enabled.
> > > +            3) Only Svadu present in DT => Supervisor must assume Svadu is
> > > +               enabled at boot.
> > >              4) Both Svade and Svadu present in DT => Supervisor must assume
> > > -               Svadu turned-off at boot time. To use Svadu, supervisor must
> > > +               Svadu is disabled at boot time. To use Svadu, supervisor must
> > >                 explicitly enable it using the SBI FWFT extension.
> >
> > Looks good to me, but I think dt binding changes are typically in separate
> > patches.
> 
> Okay, I'll split the DT binding update into a separate patch.

Make sure the appropriate lists and maintainers are CC'ed too.

> 
> >
> > >
> > >          - const: svadu
> > > diff --git a/arch/riscv/include/asm/cpufeature.h b/arch/riscv/include/asm/cpufeature.h
> > > index 739fcc84bf7b2..ad9fad6eee55d 100644
> > > --- a/arch/riscv/include/asm/cpufeature.h
> > > +++ b/arch/riscv/include/asm/cpufeature.h
> > > @@ -128,6 +128,12 @@ struct riscv_isa_ext_data {
> > >  extern const struct riscv_isa_ext_data riscv_isa_ext[];
> > >  extern const size_t riscv_isa_ext_count;
> > >  extern bool riscv_isa_fallback;
> > > +DECLARE_STATIC_KEY_FALSE(riscv_hw_pte_ad_updating);
> > > +
> > > +static __always_inline bool riscv_has_hw_pte_ad_updating(void)
> > > +{
> > > +     return static_branch_unlikely(&riscv_hw_pte_ad_updating);
> > > +}
> > >
> > >  unsigned long riscv_isa_extension_base(const unsigned long *isa_bitmap);
> > >  static __always_inline bool riscv_cpu_has_extension_likely(int cpu, const unsigned long ext)
> > > diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h
> > > index a1a7c6520a095..20663a466cf6c 100644
> > > --- a/arch/riscv/include/asm/pgtable.h
> > > +++ b/arch/riscv/include/asm/pgtable.h
> > > @@ -732,14 +732,14 @@ static inline pgprot_t pgprot_writecombine(pgprot_t _prot)
> > >  #define pgprot_dmacoherent pgprot_writecombine
> > >
> > >  /*
> > > - * Both Svade and Svadu control the hardware behavior when the PTE A/D bits need to be set. By
> > > - * default the M-mode firmware enables the hardware updating scheme when only Svadu is present in
> > > - * DT.
> > > + * Both Svade and Svadu control the hardware behavior when the PTE A/D bits
> > > + * need to be set. The core MM code only cares whether hardware updating of
> > > + * the accessed/dirty state is currently active.
> > >   */
> > >  #define arch_has_hw_pte_young arch_has_hw_pte_young
> > >  static inline bool arch_has_hw_pte_young(void)
> > >  {
> > > -     return riscv_has_extension_unlikely(RISCV_ISA_EXT_SVADU);
> > > +     return riscv_has_hw_pte_ad_updating();
> > >  }
> > >
> > >  /*
> > > diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
> > > index f46aa5602d74d..831dd6a7c1a06 100644
> > > --- a/arch/riscv/kernel/cpufeature.c
> > > +++ b/arch/riscv/kernel/cpufeature.c
> > > @@ -35,6 +35,7 @@
> > >  static bool any_cpu_has_zicboz;
> > >  static bool any_cpu_has_zicbop;
> > >  static bool any_cpu_has_zicbom;
> > > +DEFINE_STATIC_KEY_FALSE(riscv_hw_pte_ad_updating);
> > >
> > >  unsigned long elf_hwcap __read_mostly;
> > >
> > > @@ -287,15 +288,60 @@ static int riscv_ext_zvfbfwma_validate(const struct riscv_isa_ext_data *data,
> > >       return -EPROBE_DEFER;
> > >  }
> > >
> > > -static int riscv_ext_svadu_validate(const struct riscv_isa_ext_data *data,
> > > -                                 const unsigned long *isa_bitmap)
> > > +static void riscv_set_hw_pte_ad_updating(void)
> > > +{
> > > +     static_branch_enable(&riscv_hw_pte_ad_updating);
> > > +}
> > > +
> > > +static int riscv_hw_pte_ad_updating_starting(unsigned int cpu)
> > > +{
> > > +     int ret;
> > > +
> > > +     ret = sbi_fwft_set(SBI_FWFT_PTE_AD_HW_UPDATING, 1, 0);
> > > +     if (ret) {
> > > +             if (ret != -EOPNOTSUPP)
> > > +                     pr_err("CPU%u failed to enable hardware PTE A/D updating: %d\n",
> > > +                            cpu, ret);
> > > +             return ret;
> > > +     }
> > > +
> > > +     return 0;
> > > +}
> > > +
> > > +static int __init riscv_hw_pte_ad_updating_init(void)
> > >  {
> > > -     /* SVADE has already been detected, use SVADE only */
> > > -     if (__riscv_isa_extension_available(isa_bitmap, RISCV_ISA_EXT_SVADE))
> > > -             return -EOPNOTSUPP;
> > > +     bool has_svade, has_svadu;
> > > +     int state;
> > >
> > > +     has_svade = riscv_has_extension_unlikely(RISCV_ISA_EXT_SVADE);
> > > +     has_svadu = riscv_has_extension_unlikely(RISCV_ISA_EXT_SVADU);
> > > +
> > > +     if (!has_svadu)
> > > +             return 0;
> > > +
> > > +     if (!has_svade)
> > > +             goto enable;
> > > +
> > > +     state = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
> >
> > I don't think CPUHP_AP_ONLINE_DYN is correct. Setting HW A/D is something
> > that should be done at _STARTING time.
> 
> I don't think moving this to _STARTING is the right fix, because this
> callback can fail, while _STARTING callbacks are not supposed to fail.
> 
> CPUHP_AP_ONLINE_DYN already covers both the CPUs that are online when
> cpuhp_setup_state() is called and CPUs brought online later, e.g. with
> maxcpus=. If FWFT setup fails for a later CPU, only that CPU's online
> operation is aborted.

The problem is that the later hotplugged harts will see the global static
key so software won't update A/D bits and HW A/D updating won't be active
on that hart either until after CPUHP_AP_ONLINE_DYN - risking unexpected
exceptions. Since _STARTING callbacks can't abort hotplug and since we
really want HW A/D active before the hart takes its first page fault, then
maybe this needs to be done at secondary startup time.

Thanks,
drew

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [External] Re: [PATCH v2 2/2] riscv: preserve hardware-updated A/D bits in PTE accessors
  2026-05-22 20:01   ` Andrew Jones
@ 2026-05-25  2:57     ` yunhui cui
  0 siblings, 0 replies; 10+ messages in thread
From: yunhui cui @ 2026-05-25  2:57 UTC (permalink / raw)
  To: Andrew Jones
  Cc: pjw, palmer, aou, alex, akpm, pasha.tatashin, andrew+kernel,
	rmclure, debug, baolin.wang, zhangchunyan, apopple, namcao,
	wangruikang, apatel, liu.xuemei1, ajones, cleger, charlie,
	hui.wang, guodong, pincheng.plct, linux-riscv, linux-kernel,
	Qingwei Hu

Hi Andrew,

On Sat, May 23, 2026 at 4:02 AM Andrew Jones
<andrew.jones@oss.qualcomm.com> wrote:
>
> 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@bytedance.com>
> > Reviewed-by: Qingwei Hu <qingwei.hu@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?

_PAGE_SOFT_DIRTY is not hardware-updated, so hw_flags was a misleading
name. I'll rename it in the next version.
It still needs to be preserved here, because RISC-V pte_mkdirty()
already sets it together with _PAGE_DIRTY, and the cmpxchg retry could
otherwise lose that soft-dirty state.

>
> Thanks,
> drew

Thanks,
Yunhui

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v2 1/2] riscv: track effective hardware PTE A/D updates
  2026-05-22 14:23 [PATCH v2 1/2] riscv: track effective hardware PTE A/D updates Yunhui Cui
  2026-05-22 14:23 ` [PATCH v2 2/2] riscv: preserve hardware-updated A/D bits in PTE accessors Yunhui Cui
  2026-05-22 19:57 ` [PATCH v2 1/2] riscv: track effective hardware PTE A/D updates Andrew Jones
@ 2026-06-05 11:44 ` Inochi Amaoto
  2026-06-09  6:45   ` [External] " yunhui cui
  2 siblings, 1 reply; 10+ messages in thread
From: Inochi Amaoto @ 2026-06-05 11:44 UTC (permalink / raw)
  To: Yunhui Cui, pjw, palmer, aou, alex, akpm, pasha.tatashin,
	andrew+kernel, rmclure, debug, baolin.wang, zhangchunyan, apopple,
	namcao, wangruikang, apatel, liu.xuemei1, ajones, cleger, charlie,
	hui.wang, guodong, pincheng.plct, linux-riscv, linux-kernel
  Cc: Qingwei Hu

On Fri, May 22, 2026 at 10:23:57PM +0800, Yunhui Cui wrote:
> Track the effective hardware PTE A/D update mode separately from
> Svadu capability discovery. When both Svade and Svadu are present,
> enable SBI FWFT PTE A/D hardware updating on each online CPU via
> CPUHP and use the resulting runtime state for arch_has_hw_pte_young().
> Fall back to software-managed A/D updates if enabling hardware updates
> fails.
> 
> When Svadu is present without Svade, assume hardware PTE A/D updating
> is enabled from boot, and document that boot-time behavior in the DT
> binding.
> 
> Signed-off-by: Yunhui Cui <cuiyunhui@bytedance.com>
> Reviewed-by: Qingwei Hu <qingwei.hu@bytedance.com>
> ---
>  .../devicetree/bindings/riscv/extensions.yaml |  6 +-
>  arch/riscv/include/asm/cpufeature.h           |  6 ++
>  arch/riscv/include/asm/pgtable.h              |  8 +--
>  arch/riscv/kernel/cpufeature.c                | 58 +++++++++++++++++--
>  4 files changed, 65 insertions(+), 13 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/riscv/extensions.yaml b/Documentation/devicetree/bindings/riscv/extensions.yaml
> index 2b0a8a93bb214..b09888e9988de 100644
> --- a/Documentation/devicetree/bindings/riscv/extensions.yaml
> +++ b/Documentation/devicetree/bindings/riscv/extensions.yaml
> @@ -294,10 +294,10 @@ properties:
>                 of the PTE A/D bits or page faults when they need updated.
>              2) Only Svade present in DT => Supervisor must assume Svade to be
>                 always enabled.
> -            3) Only Svadu present in DT => Supervisor must assume Svadu to be
> -               always enabled.
> +            3) Only Svadu present in DT => Supervisor must assume Svadu is
> +               enabled at boot.
>              4) Both Svade and Svadu present in DT => Supervisor must assume
> -               Svadu turned-off at boot time. To use Svadu, supervisor must
> +               Svadu is disabled at boot time. To use Svadu, supervisor must
>                 explicitly enable it using the SBI FWFT extension.
>  
>          - const: svadu
> diff --git a/arch/riscv/include/asm/cpufeature.h b/arch/riscv/include/asm/cpufeature.h
> index 739fcc84bf7b2..ad9fad6eee55d 100644
> --- a/arch/riscv/include/asm/cpufeature.h
> +++ b/arch/riscv/include/asm/cpufeature.h
> @@ -128,6 +128,12 @@ struct riscv_isa_ext_data {
>  extern const struct riscv_isa_ext_data riscv_isa_ext[];
>  extern const size_t riscv_isa_ext_count;
>  extern bool riscv_isa_fallback;
> +DECLARE_STATIC_KEY_FALSE(riscv_hw_pte_ad_updating);
> +

I think a EXPORT_SYMBOL is required, otherwise the kvm is failed to be
built as module.

Regards,
Inochi


> +static __always_inline bool riscv_has_hw_pte_ad_updating(void)
> +{
> +	return static_branch_unlikely(&riscv_hw_pte_ad_updating);
> +}
>  
>  unsigned long riscv_isa_extension_base(const unsigned long *isa_bitmap);
>  static __always_inline bool riscv_cpu_has_extension_likely(int cpu, const unsigned long ext)
> diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h
> index a1a7c6520a095..20663a466cf6c 100644
> --- a/arch/riscv/include/asm/pgtable.h
> +++ b/arch/riscv/include/asm/pgtable.h
> @@ -732,14 +732,14 @@ static inline pgprot_t pgprot_writecombine(pgprot_t _prot)
>  #define pgprot_dmacoherent pgprot_writecombine
>  
>  /*
> - * Both Svade and Svadu control the hardware behavior when the PTE A/D bits need to be set. By
> - * default the M-mode firmware enables the hardware updating scheme when only Svadu is present in
> - * DT.
> + * Both Svade and Svadu control the hardware behavior when the PTE A/D bits
> + * need to be set. The core MM code only cares whether hardware updating of
> + * the accessed/dirty state is currently active.
>   */
>  #define arch_has_hw_pte_young arch_has_hw_pte_young
>  static inline bool arch_has_hw_pte_young(void)
>  {
> -	return riscv_has_extension_unlikely(RISCV_ISA_EXT_SVADU);
> +	return riscv_has_hw_pte_ad_updating();
>  }
>  
>  /*
> diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
> index f46aa5602d74d..831dd6a7c1a06 100644
> --- a/arch/riscv/kernel/cpufeature.c
> +++ b/arch/riscv/kernel/cpufeature.c
> @@ -35,6 +35,7 @@
>  static bool any_cpu_has_zicboz;
>  static bool any_cpu_has_zicbop;
>  static bool any_cpu_has_zicbom;
> +DEFINE_STATIC_KEY_FALSE(riscv_hw_pte_ad_updating);
>  
>  unsigned long elf_hwcap __read_mostly;
>  
> @@ -287,15 +288,60 @@ static int riscv_ext_zvfbfwma_validate(const struct riscv_isa_ext_data *data,
>  	return -EPROBE_DEFER;
>  }
>  
> -static int riscv_ext_svadu_validate(const struct riscv_isa_ext_data *data,
> -				    const unsigned long *isa_bitmap)
> +static void riscv_set_hw_pte_ad_updating(void)
> +{
> +	static_branch_enable(&riscv_hw_pte_ad_updating);
> +}
> +
> +static int riscv_hw_pte_ad_updating_starting(unsigned int cpu)
> +{
> +	int ret;
> +
> +	ret = sbi_fwft_set(SBI_FWFT_PTE_AD_HW_UPDATING, 1, 0);
> +	if (ret) {
> +		if (ret != -EOPNOTSUPP)
> +			pr_err("CPU%u failed to enable hardware PTE A/D updating: %d\n",
> +			       cpu, ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int __init riscv_hw_pte_ad_updating_init(void)
>  {
> -	/* SVADE has already been detected, use SVADE only */
> -	if (__riscv_isa_extension_available(isa_bitmap, RISCV_ISA_EXT_SVADE))
> -		return -EOPNOTSUPP;
> +	bool has_svade, has_svadu;
> +	int state;
>  
> +	has_svade = riscv_has_extension_unlikely(RISCV_ISA_EXT_SVADE);
> +	has_svadu = riscv_has_extension_unlikely(RISCV_ISA_EXT_SVADU);
> +
> +	if (!has_svadu)
> +		return 0;
> +
> +	if (!has_svade)
> +		goto enable;
> +
> +	state = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
> +				  "riscv/pte-ad:starting",
> +				  riscv_hw_pte_ad_updating_starting,
> +				  NULL);
> +	if (state < 0) {
> +		pr_info("riscv: leave PTE A/D updates software-managed (%d)\n",
> +			state);
> +		return 0;
> +	}
> +
> +	/*
> +	 * A successful CPUHP_AP_ONLINE_DYN registration means the startup
> +	 * callback has already succeeded on all online CPUs.
> +	 */
> +enable:
> +	riscv_set_hw_pte_ad_updating();
> +	pr_debug("riscv: hardware PTE A/D updating enabled\n");
>  	return 0;
>  }
> +arch_initcall(riscv_hw_pte_ad_updating_init);
>  
>  static int riscv_cfilp_validate(const struct riscv_isa_ext_data *data,
>  				const unsigned long *isa_bitmap)
> @@ -584,7 +630,7 @@ const struct riscv_isa_ext_data riscv_isa_ext[] = {
>  	__RISCV_ISA_EXT_SUPERSET(ssnpm, RISCV_ISA_EXT_SSNPM, riscv_xlinuxenvcfg_exts),
>  	__RISCV_ISA_EXT_DATA(sstc, RISCV_ISA_EXT_SSTC),
>  	__RISCV_ISA_EXT_DATA(svade, RISCV_ISA_EXT_SVADE),
> -	__RISCV_ISA_EXT_DATA_VALIDATE(svadu, RISCV_ISA_EXT_SVADU, riscv_ext_svadu_validate),
> +	__RISCV_ISA_EXT_DATA(svadu, RISCV_ISA_EXT_SVADU),
>  	__RISCV_ISA_EXT_DATA(svinval, RISCV_ISA_EXT_SVINVAL),
>  	__RISCV_ISA_EXT_DATA(svnapot, RISCV_ISA_EXT_SVNAPOT),
>  	__RISCV_ISA_EXT_DATA(svpbmt, RISCV_ISA_EXT_SVPBMT),
> -- 
> 2.39.5
> 

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [External] Re: [PATCH v2 1/2] riscv: track effective hardware PTE A/D updates
  2026-05-24 20:44     ` Andrew Jones
@ 2026-06-09  6:38       ` yunhui cui
  0 siblings, 0 replies; 10+ messages in thread
From: yunhui cui @ 2026-06-09  6:38 UTC (permalink / raw)
  To: Andrew Jones
  Cc: pjw, palmer, aou, alex, akpm, pasha.tatashin, andrew+kernel,
	rmclure, debug, baolin.wang, zhangchunyan, apopple, namcao,
	wangruikang, apatel, liu.xuemei1, ajones, cleger, charlie,
	hui.wang, guodong, pincheng.plct, linux-riscv, linux-kernel,
	Qingwei Hu

Hi Andrew,

On Mon, May 25, 2026 at 4:44 AM Andrew Jones
<andrew.jones@oss.qualcomm.com> wrote:
>
> On Sat, May 23, 2026 at 11:58:05AM +0800, yunhui cui wrote:
> > Hi Andrew,
> >
> > On Sat, May 23, 2026 at 3:58 AM Andrew Jones
> > <andrew.jones@oss.qualcomm.com> wrote:
> > >
> > > On Fri, May 22, 2026 at 10:23:57PM +0800, Yunhui Cui wrote:
> > > > Track the effective hardware PTE A/D update mode separately from
> > > > Svadu capability discovery. When both Svade and Svadu are present,
> > > > enable SBI FWFT PTE A/D hardware updating on each online CPU via
> > > > CPUHP and use the resulting runtime state for arch_has_hw_pte_young().
> > > > Fall back to software-managed A/D updates if enabling hardware updates
> > > > fails.
> > > >
> > > > When Svadu is present without Svade, assume hardware PTE A/D updating
> > > > is enabled from boot, and document that boot-time behavior in the DT
> > > > binding.
> > > >
> > > > Signed-off-by: Yunhui Cui <cuiyunhui@bytedance.com>
> > > > Reviewed-by: Qingwei Hu <qingwei.hu@bytedance.com>
> > > > ---
> > > >  .../devicetree/bindings/riscv/extensions.yaml |  6 +-
> > > >  arch/riscv/include/asm/cpufeature.h           |  6 ++
> > > >  arch/riscv/include/asm/pgtable.h              |  8 +--
> > > >  arch/riscv/kernel/cpufeature.c                | 58 +++++++++++++++++--
> > > >  4 files changed, 65 insertions(+), 13 deletions(-)
> > > >
> > > > diff --git a/Documentation/devicetree/bindings/riscv/extensions.yaml b/Documentation/devicetree/bindings/riscv/extensions.yaml
> > > > index 2b0a8a93bb214..b09888e9988de 100644
> > > > --- a/Documentation/devicetree/bindings/riscv/extensions.yaml
> > > > +++ b/Documentation/devicetree/bindings/riscv/extensions.yaml
> > > > @@ -294,10 +294,10 @@ properties:
> > > >                 of the PTE A/D bits or page faults when they need updated.
> > > >              2) Only Svade present in DT => Supervisor must assume Svade to be
> > > >                 always enabled.
> > > > -            3) Only Svadu present in DT => Supervisor must assume Svadu to be
> > > > -               always enabled.
> > > > +            3) Only Svadu present in DT => Supervisor must assume Svadu is
> > > > +               enabled at boot.
> > > >              4) Both Svade and Svadu present in DT => Supervisor must assume
> > > > -               Svadu turned-off at boot time. To use Svadu, supervisor must
> > > > +               Svadu is disabled at boot time. To use Svadu, supervisor must
> > > >                 explicitly enable it using the SBI FWFT extension.
> > >
> > > Looks good to me, but I think dt binding changes are typically in separate
> > > patches.
> >
> > Okay, I'll split the DT binding update into a separate patch.
>
> Make sure the appropriate lists and maintainers are CC'ed too.
>
> >
> > >
> > > >
> > > >          - const: svadu
> > > > diff --git a/arch/riscv/include/asm/cpufeature.h b/arch/riscv/include/asm/cpufeature.h
> > > > index 739fcc84bf7b2..ad9fad6eee55d 100644
> > > > --- a/arch/riscv/include/asm/cpufeature.h
> > > > +++ b/arch/riscv/include/asm/cpufeature.h
> > > > @@ -128,6 +128,12 @@ struct riscv_isa_ext_data {
> > > >  extern const struct riscv_isa_ext_data riscv_isa_ext[];
> > > >  extern const size_t riscv_isa_ext_count;
> > > >  extern bool riscv_isa_fallback;
> > > > +DECLARE_STATIC_KEY_FALSE(riscv_hw_pte_ad_updating);
> > > > +
> > > > +static __always_inline bool riscv_has_hw_pte_ad_updating(void)
> > > > +{
> > > > +     return static_branch_unlikely(&riscv_hw_pte_ad_updating);
> > > > +}
> > > >
> > > >  unsigned long riscv_isa_extension_base(const unsigned long *isa_bitmap);
> > > >  static __always_inline bool riscv_cpu_has_extension_likely(int cpu, const unsigned long ext)
> > > > diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h
> > > > index a1a7c6520a095..20663a466cf6c 100644
> > > > --- a/arch/riscv/include/asm/pgtable.h
> > > > +++ b/arch/riscv/include/asm/pgtable.h
> > > > @@ -732,14 +732,14 @@ static inline pgprot_t pgprot_writecombine(pgprot_t _prot)
> > > >  #define pgprot_dmacoherent pgprot_writecombine
> > > >
> > > >  /*
> > > > - * Both Svade and Svadu control the hardware behavior when the PTE A/D bits need to be set. By
> > > > - * default the M-mode firmware enables the hardware updating scheme when only Svadu is present in
> > > > - * DT.
> > > > + * Both Svade and Svadu control the hardware behavior when the PTE A/D bits
> > > > + * need to be set. The core MM code only cares whether hardware updating of
> > > > + * the accessed/dirty state is currently active.
> > > >   */
> > > >  #define arch_has_hw_pte_young arch_has_hw_pte_young
> > > >  static inline bool arch_has_hw_pte_young(void)
> > > >  {
> > > > -     return riscv_has_extension_unlikely(RISCV_ISA_EXT_SVADU);
> > > > +     return riscv_has_hw_pte_ad_updating();
> > > >  }
> > > >
> > > >  /*
> > > > diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
> > > > index f46aa5602d74d..831dd6a7c1a06 100644
> > > > --- a/arch/riscv/kernel/cpufeature.c
> > > > +++ b/arch/riscv/kernel/cpufeature.c
> > > > @@ -35,6 +35,7 @@
> > > >  static bool any_cpu_has_zicboz;
> > > >  static bool any_cpu_has_zicbop;
> > > >  static bool any_cpu_has_zicbom;
> > > > +DEFINE_STATIC_KEY_FALSE(riscv_hw_pte_ad_updating);
> > > >
> > > >  unsigned long elf_hwcap __read_mostly;
> > > >
> > > > @@ -287,15 +288,60 @@ static int riscv_ext_zvfbfwma_validate(const struct riscv_isa_ext_data *data,
> > > >       return -EPROBE_DEFER;
> > > >  }
> > > >
> > > > -static int riscv_ext_svadu_validate(const struct riscv_isa_ext_data *data,
> > > > -                                 const unsigned long *isa_bitmap)
> > > > +static void riscv_set_hw_pte_ad_updating(void)
> > > > +{
> > > > +     static_branch_enable(&riscv_hw_pte_ad_updating);
> > > > +}
> > > > +
> > > > +static int riscv_hw_pte_ad_updating_starting(unsigned int cpu)
> > > > +{
> > > > +     int ret;
> > > > +
> > > > +     ret = sbi_fwft_set(SBI_FWFT_PTE_AD_HW_UPDATING, 1, 0);
> > > > +     if (ret) {
> > > > +             if (ret != -EOPNOTSUPP)
> > > > +                     pr_err("CPU%u failed to enable hardware PTE A/D updating: %d\n",
> > > > +                            cpu, ret);
> > > > +             return ret;
> > > > +     }
> > > > +
> > > > +     return 0;
> > > > +}
> > > > +
> > > > +static int __init riscv_hw_pte_ad_updating_init(void)
> > > >  {
> > > > -     /* SVADE has already been detected, use SVADE only */
> > > > -     if (__riscv_isa_extension_available(isa_bitmap, RISCV_ISA_EXT_SVADE))
> > > > -             return -EOPNOTSUPP;
> > > > +     bool has_svade, has_svadu;
> > > > +     int state;
> > > >
> > > > +     has_svade = riscv_has_extension_unlikely(RISCV_ISA_EXT_SVADE);
> > > > +     has_svadu = riscv_has_extension_unlikely(RISCV_ISA_EXT_SVADU);
> > > > +
> > > > +     if (!has_svadu)
> > > > +             return 0;
> > > > +
> > > > +     if (!has_svade)
> > > > +             goto enable;
> > > > +
> > > > +     state = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
> > >
> > > I don't think CPUHP_AP_ONLINE_DYN is correct. Setting HW A/D is something
> > > that should be done at _STARTING time.
> >
> > I don't think moving this to _STARTING is the right fix, because this
> > callback can fail, while _STARTING callbacks are not supposed to fail.
> >
> > CPUHP_AP_ONLINE_DYN already covers both the CPUs that are online when
> > cpuhp_setup_state() is called and CPUs brought online later, e.g. with
> > maxcpus=. If FWFT setup fails for a later CPU, only that CPU's online
> > operation is aborted.
>
> The problem is that the later hotplugged harts will see the global static
> key so software won't update A/D bits and HW A/D updating won't be active
> on that hart either until after CPUHP_AP_ONLINE_DYN - risking unexpected
> exceptions. Since _STARTING callbacks can't abort hotplug and since we
> really want HW A/D active before the hart takes its first page fault, then
> maybe this needs to be done at secondary startup time.

Agreed. I'll drop the CPUHP_AP_ONLINE_DYN setup in the next version.

Instead, I plan to enable SBI_FWFT_PTE_AD_HW_UPDATING on all currently
online harts during init, before enabling the global
riscv_hw_pte_ad_updating static key. Later secondary harts will enable
the same FWFT feature at the beginning of smp_callin(), before
notify_cpu_starting() and before they are marked online.

If the init-time FWFT setup fails, the static key will remain disabled and
the kernel will fall back to software-managed A/D updates. If a later
secondary hart fails after the static key has already been enabled, that
hart bringup will be aborted. I'll also add rollback for the init-time
partial failure path.

This should remove the window where a hotplugged hart observes the global
static key but has not enabled local FWFT yet.

>
> Thanks,
> drew

Thanks,
Yunhui

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [External] Re: [PATCH v2 1/2] riscv: track effective hardware PTE A/D updates
  2026-06-05 11:44 ` Inochi Amaoto
@ 2026-06-09  6:45   ` yunhui cui
  0 siblings, 0 replies; 10+ messages in thread
From: yunhui cui @ 2026-06-09  6:45 UTC (permalink / raw)
  To: Inochi Amaoto
  Cc: pjw, palmer, aou, alex, akpm, pasha.tatashin, andrew+kernel,
	rmclure, debug, baolin.wang, zhangchunyan, apopple, namcao,
	wangruikang, apatel, liu.xuemei1, ajones, cleger, charlie,
	hui.wang, guodong, pincheng.plct, linux-riscv, linux-kernel,
	Qingwei Hu

Hi Inochi,

On Fri, Jun 5, 2026 at 7:45 PM Inochi Amaoto <inochiama@gmail.com> wrote:
>
> On Fri, May 22, 2026 at 10:23:57PM +0800, Yunhui Cui wrote:
> > Track the effective hardware PTE A/D update mode separately from
> > Svadu capability discovery. When both Svade and Svadu are present,
> > enable SBI FWFT PTE A/D hardware updating on each online CPU via
> > CPUHP and use the resulting runtime state for arch_has_hw_pte_young().
> > Fall back to software-managed A/D updates if enabling hardware updates
> > fails.
> >
> > When Svadu is present without Svade, assume hardware PTE A/D updating
> > is enabled from boot, and document that boot-time behavior in the DT
> > binding.
> >
> > Signed-off-by: Yunhui Cui <cuiyunhui@bytedance.com>
> > Reviewed-by: Qingwei Hu <qingwei.hu@bytedance.com>
> > ---
> >  .../devicetree/bindings/riscv/extensions.yaml |  6 +-
> >  arch/riscv/include/asm/cpufeature.h           |  6 ++
> >  arch/riscv/include/asm/pgtable.h              |  8 +--
> >  arch/riscv/kernel/cpufeature.c                | 58 +++++++++++++++++--
> >  4 files changed, 65 insertions(+), 13 deletions(-)
> >
> > diff --git a/Documentation/devicetree/bindings/riscv/extensions.yaml b/Documentation/devicetree/bindings/riscv/extensions.yaml
> > index 2b0a8a93bb214..b09888e9988de 100644
> > --- a/Documentation/devicetree/bindings/riscv/extensions.yaml
> > +++ b/Documentation/devicetree/bindings/riscv/extensions.yaml
> > @@ -294,10 +294,10 @@ properties:
> >                 of the PTE A/D bits or page faults when they need updated.
> >              2) Only Svade present in DT => Supervisor must assume Svade to be
> >                 always enabled.
> > -            3) Only Svadu present in DT => Supervisor must assume Svadu to be
> > -               always enabled.
> > +            3) Only Svadu present in DT => Supervisor must assume Svadu is
> > +               enabled at boot.
> >              4) Both Svade and Svadu present in DT => Supervisor must assume
> > -               Svadu turned-off at boot time. To use Svadu, supervisor must
> > +               Svadu is disabled at boot time. To use Svadu, supervisor must
> >                 explicitly enable it using the SBI FWFT extension.
> >
> >          - const: svadu
> > diff --git a/arch/riscv/include/asm/cpufeature.h b/arch/riscv/include/asm/cpufeature.h
> > index 739fcc84bf7b2..ad9fad6eee55d 100644
> > --- a/arch/riscv/include/asm/cpufeature.h
> > +++ b/arch/riscv/include/asm/cpufeature.h
> > @@ -128,6 +128,12 @@ struct riscv_isa_ext_data {
> >  extern const struct riscv_isa_ext_data riscv_isa_ext[];
> >  extern const size_t riscv_isa_ext_count;
> >  extern bool riscv_isa_fallback;
> > +DECLARE_STATIC_KEY_FALSE(riscv_hw_pte_ad_updating);
> > +
>
> I think a EXPORT_SYMBOL is required, otherwise the kvm is failed to be
> built as module.

  Thanks, I'll fix it in v3.

>
> Regards,
> Inochi
>
>
> > +static __always_inline bool riscv_has_hw_pte_ad_updating(void)
> > +{
> > +     return static_branch_unlikely(&riscv_hw_pte_ad_updating);
> > +}
> >
> >  unsigned long riscv_isa_extension_base(const unsigned long *isa_bitmap);
> >  static __always_inline bool riscv_cpu_has_extension_likely(int cpu, const unsigned long ext)
> > diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h
> > index a1a7c6520a095..20663a466cf6c 100644
> > --- a/arch/riscv/include/asm/pgtable.h
> > +++ b/arch/riscv/include/asm/pgtable.h
> > @@ -732,14 +732,14 @@ static inline pgprot_t pgprot_writecombine(pgprot_t _prot)
> >  #define pgprot_dmacoherent pgprot_writecombine
> >
> >  /*
> > - * Both Svade and Svadu control the hardware behavior when the PTE A/D bits need to be set. By
> > - * default the M-mode firmware enables the hardware updating scheme when only Svadu is present in
> > - * DT.
> > + * Both Svade and Svadu control the hardware behavior when the PTE A/D bits
> > + * need to be set. The core MM code only cares whether hardware updating of
> > + * the accessed/dirty state is currently active.
> >   */
> >  #define arch_has_hw_pte_young arch_has_hw_pte_young
> >  static inline bool arch_has_hw_pte_young(void)
> >  {
> > -     return riscv_has_extension_unlikely(RISCV_ISA_EXT_SVADU);
> > +     return riscv_has_hw_pte_ad_updating();
> >  }
> >
> >  /*
> > diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
> > index f46aa5602d74d..831dd6a7c1a06 100644
> > --- a/arch/riscv/kernel/cpufeature.c
> > +++ b/arch/riscv/kernel/cpufeature.c
> > @@ -35,6 +35,7 @@
> >  static bool any_cpu_has_zicboz;
> >  static bool any_cpu_has_zicbop;
> >  static bool any_cpu_has_zicbom;
> > +DEFINE_STATIC_KEY_FALSE(riscv_hw_pte_ad_updating);
> >
> >  unsigned long elf_hwcap __read_mostly;
> >
> > @@ -287,15 +288,60 @@ static int riscv_ext_zvfbfwma_validate(const struct riscv_isa_ext_data *data,
> >       return -EPROBE_DEFER;
> >  }
> >
> > -static int riscv_ext_svadu_validate(const struct riscv_isa_ext_data *data,
> > -                                 const unsigned long *isa_bitmap)
> > +static void riscv_set_hw_pte_ad_updating(void)
> > +{
> > +     static_branch_enable(&riscv_hw_pte_ad_updating);
> > +}
> > +
> > +static int riscv_hw_pte_ad_updating_starting(unsigned int cpu)
> > +{
> > +     int ret;
> > +
> > +     ret = sbi_fwft_set(SBI_FWFT_PTE_AD_HW_UPDATING, 1, 0);
> > +     if (ret) {
> > +             if (ret != -EOPNOTSUPP)
> > +                     pr_err("CPU%u failed to enable hardware PTE A/D updating: %d\n",
> > +                            cpu, ret);
> > +             return ret;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int __init riscv_hw_pte_ad_updating_init(void)
> >  {
> > -     /* SVADE has already been detected, use SVADE only */
> > -     if (__riscv_isa_extension_available(isa_bitmap, RISCV_ISA_EXT_SVADE))
> > -             return -EOPNOTSUPP;
> > +     bool has_svade, has_svadu;
> > +     int state;
> >
> > +     has_svade = riscv_has_extension_unlikely(RISCV_ISA_EXT_SVADE);
> > +     has_svadu = riscv_has_extension_unlikely(RISCV_ISA_EXT_SVADU);
> > +
> > +     if (!has_svadu)
> > +             return 0;
> > +
> > +     if (!has_svade)
> > +             goto enable;
> > +
> > +     state = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
> > +                               "riscv/pte-ad:starting",
> > +                               riscv_hw_pte_ad_updating_starting,
> > +                               NULL);
> > +     if (state < 0) {
> > +             pr_info("riscv: leave PTE A/D updates software-managed (%d)\n",
> > +                     state);
> > +             return 0;
> > +     }
> > +
> > +     /*
> > +      * A successful CPUHP_AP_ONLINE_DYN registration means the startup
> > +      * callback has already succeeded on all online CPUs.
> > +      */
> > +enable:
> > +     riscv_set_hw_pte_ad_updating();
> > +     pr_debug("riscv: hardware PTE A/D updating enabled\n");
> >       return 0;
> >  }
> > +arch_initcall(riscv_hw_pte_ad_updating_init);
> >
> >  static int riscv_cfilp_validate(const struct riscv_isa_ext_data *data,
> >                               const unsigned long *isa_bitmap)
> > @@ -584,7 +630,7 @@ const struct riscv_isa_ext_data riscv_isa_ext[] = {
> >       __RISCV_ISA_EXT_SUPERSET(ssnpm, RISCV_ISA_EXT_SSNPM, riscv_xlinuxenvcfg_exts),
> >       __RISCV_ISA_EXT_DATA(sstc, RISCV_ISA_EXT_SSTC),
> >       __RISCV_ISA_EXT_DATA(svade, RISCV_ISA_EXT_SVADE),
> > -     __RISCV_ISA_EXT_DATA_VALIDATE(svadu, RISCV_ISA_EXT_SVADU, riscv_ext_svadu_validate),
> > +     __RISCV_ISA_EXT_DATA(svadu, RISCV_ISA_EXT_SVADU),
> >       __RISCV_ISA_EXT_DATA(svinval, RISCV_ISA_EXT_SVINVAL),
> >       __RISCV_ISA_EXT_DATA(svnapot, RISCV_ISA_EXT_SVNAPOT),
> >       __RISCV_ISA_EXT_DATA(svpbmt, RISCV_ISA_EXT_SVPBMT),
> > --
> > 2.39.5
> >

Thanks,
Yunhui

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2026-06-09  6:45 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-22 14:23 [PATCH v2 1/2] riscv: track effective hardware PTE A/D updates Yunhui Cui
2026-05-22 14:23 ` [PATCH v2 2/2] riscv: preserve hardware-updated A/D bits in PTE accessors Yunhui Cui
2026-05-22 20:01   ` Andrew Jones
2026-05-25  2:57     ` [External] " yunhui cui
2026-05-22 19:57 ` [PATCH v2 1/2] riscv: track effective hardware PTE A/D updates Andrew Jones
2026-05-23  3:58   ` [External] " yunhui cui
2026-05-24 20:44     ` Andrew Jones
2026-06-09  6:38       ` yunhui cui
2026-06-05 11:44 ` Inochi Amaoto
2026-06-09  6:45   ` [External] " yunhui cui

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox