Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2] arm64: clear_page[s] using memset
@ 2026-09-16 10:03 Linus Walleij
  2026-09-16 15:28 ` Leonardo Bras
  0 siblings, 1 reply; 5+ messages in thread
From: Linus Walleij @ 2026-09-16 10:03 UTC (permalink / raw)
  To: Catalin Marinas, Will Deacon, Marc Zyngier, Oliver Upton,
	Joey Gouly, Suzuki K Poulose, Zenghui Yu
  Cc: linux-arm-kernel, kvmarm, Usama Anjum, Linus Walleij

There is no need to try to second-guess the compiler when
clearing memory. Just call memset() like everyone else.

Since memset() already has an architecture-local MOPS
optimization, we do not need to do anything else to preserve
the MOPS optimization.

While at it, implement the shorthand for directly calling
the new prototype clear_pages() for larger page chunks.

No performance regressions can be seen, the fastpath
benchmarks differences are in the noise.

Usama Anjum tested next-20260821 with one warm-up and three repeats
in four sessions, for a total of 12 measured runs. The commands were:

  perf bench mem memset -k 1GB -f default -s 16GB
  perf bench mem mmap -p 1GB -f demand -s 32GB -l 5
  perf bench mem mmap -p 4KB -f demand -s 32GB -l 5

The results were:

aws-m7g.metal:
  Benchmark    Base bytes/sec   Change with patch
  memset 1GB   63932542232.12                1.92%
  mmap 1GB     63272579168.03               -0.57%
  mmap 4KB     49692830849.48               -1.11%

cesw-aarch64-ampereone-1s-a192-32x:
  Benchmark    Base bytes/sec   Change with patch
  memset 1GB   33895562998.71                0.25%
  mmap 1GB     34338454210.17                1.16%
  mmap 4KB     25687107580.90               -0.85%

Assisted-by: LLM
Suggested-by: Will Deacon <will@kernel.org>
Tested-by: Usama Anjum <usama.anjum@arm.com>
Link: https://lore.kernel.org/linux-arm-kernel/20260303-aarch64-clear-pages-v1-1-ad0c3ee9a555@kernel.org/
Link: https://lore.kernel.org/linux-arm-kernel/67ab6c51-4511-46ab-931b-a4323551147a@arm.com/
Signed-off-by: Linus Walleij <linusw@kernel.org>
---
Changes in v2:
- Rebased on v7.3-rc1
- Include Usama Anjum's performance results and Tested-by tag.
- Link to v1: https://lore.kernel.org/r/20260306-aarch64-clear-pages-c-v1-1-77c1bb0f1c21@kernel.org
---
 arch/arm64/include/asm/page.h    | 13 +++++++++-
 arch/arm64/kernel/image-vars.h   |  1 -
 arch/arm64/kvm/hyp/nvhe/Makefile |  2 +-
 arch/arm64/lib/Makefile          |  2 +-
 arch/arm64/lib/clear_page.S      | 53 ----------------------------------------
 5 files changed, 14 insertions(+), 57 deletions(-)

diff --git a/arch/arm64/include/asm/page.h b/arch/arm64/include/asm/page.h
index 58200de8a221..b2eaa63d9056 100644
--- a/arch/arm64/include/asm/page.h
+++ b/arch/arm64/include/asm/page.h
@@ -13,6 +13,7 @@
 #ifndef __ASSEMBLER__
 
 #include <linux/personality.h> /* for READ_IMPLIES_EXEC */
+#include <linux/string.h> /* for memset() */
 #include <linux/types.h> /* for gfp_t */
 #include <asm/pgtable-types.h>
 
@@ -20,7 +21,17 @@ struct page;
 struct vm_area_struct;
 
 extern void copy_page(void *to, const void *from);
-extern void clear_page(void *to);
+
+static inline void clear_pages(void *addr, unsigned int npages)
+{
+	memset(addr, 0, npages * PAGE_SIZE);
+}
+#define clear_pages clear_pages
+
+static inline void clear_page(void *addr)
+{
+	clear_pages(addr, 1);
+}
 
 void copy_user_highpage(struct page *to, struct page *from,
 			unsigned long vaddr, struct vm_area_struct *vma);
diff --git a/arch/arm64/kernel/image-vars.h b/arch/arm64/kernel/image-vars.h
index 14beb7b9d304..facda8137d9b 100644
--- a/arch/arm64/kernel/image-vars.h
+++ b/arch/arm64/kernel/image-vars.h
@@ -119,7 +119,6 @@ KVM_NVHE_ALIAS(__start___kvm_ex_table);
 KVM_NVHE_ALIAS(__stop___kvm_ex_table);
 
 /* Position-independent library routines */
-KVM_NVHE_ALIAS_HYP(clear_page, __pi_clear_page);
 KVM_NVHE_ALIAS_HYP(copy_page, __pi_copy_page);
 KVM_NVHE_ALIAS_HYP(memcpy, __pi_memcpy);
 KVM_NVHE_ALIAS_HYP(memset, __pi_memset);
diff --git a/arch/arm64/kvm/hyp/nvhe/Makefile b/arch/arm64/kvm/hyp/nvhe/Makefile
index f57450ebcb49..2ed489bf544e 100644
--- a/arch/arm64/kvm/hyp/nvhe/Makefile
+++ b/arch/arm64/kvm/hyp/nvhe/Makefile
@@ -17,7 +17,7 @@ ccflags-y += -fno-stack-protector	\
 hostprogs := gen-hyprel
 HOST_EXTRACFLAGS += -I$(objtree)/include
 
-lib-objs := clear_page.o copy_page.o memcpy.o memset.o tishift.o
+lib-objs := copy_page.o memcpy.o memset.o tishift.o
 lib-objs := $(addprefix ../../../lib/, $(lib-objs))
 
 CFLAGS_switch.nvhe.o += -Wno-override-init
diff --git a/arch/arm64/lib/Makefile b/arch/arm64/lib/Makefile
index b33e1ca4a781..38ffd359123c 100644
--- a/arch/arm64/lib/Makefile
+++ b/arch/arm64/lib/Makefile
@@ -5,7 +5,7 @@ KCSAN_SANITIZE_delay.o := n
 
 lib-y		:= clear_user.o delay.o copy_from_user.o		\
 		   copy_to_user.o copy_page.o				\
-		   clear_page.o csum.o insn.o memchr.o memcpy.o		\
+		   csum.o insn.o memchr.o memcpy.o			\
 		   memset.o memcmp.o strcmp.o strncmp.o strlen.o	\
 		   strnlen.o strchr.o strrchr.o tishift.o
 
diff --git a/arch/arm64/lib/clear_page.S b/arch/arm64/lib/clear_page.S
deleted file mode 100644
index bd6f7d5eb6eb..000000000000
--- a/arch/arm64/lib/clear_page.S
+++ /dev/null
@@ -1,53 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Copyright (C) 2012 ARM Ltd.
- */
-
-#include <linux/linkage.h>
-#include <linux/const.h>
-#include <asm/assembler.h>
-#include <asm/page.h>
-
-/*
- * Clear page @dest
- *
- * Parameters:
- *	x0 - dest
- */
-SYM_FUNC_START(__pi_clear_page)
-#ifdef CONFIG_AS_HAS_MOPS
-	.arch_extension mops
-alternative_if_not ARM64_HAS_MOPS
-	b	.Lno_mops
-alternative_else_nop_endif
-
-	mov	x1, #PAGE_SIZE
-	setpn	[x0]!, x1!, xzr
-	setmn	[x0]!, x1!, xzr
-	seten	[x0]!, x1!, xzr
-	ret
-.Lno_mops:
-#endif
-	mrs	x1, dczid_el0
-	tbnz	x1, #4, 2f	/* Branch if DC ZVA is prohibited */
-	and	w1, w1, #0xf
-	mov	x2, #4
-	lsl	x1, x2, x1
-
-1:	dc	zva, x0
-	add	x0, x0, x1
-	tst	x0, #(PAGE_SIZE - 1)
-	b.ne	1b
-	ret
-
-2:	stnp	xzr, xzr, [x0]
-	stnp	xzr, xzr, [x0, #16]
-	stnp	xzr, xzr, [x0, #32]
-	stnp	xzr, xzr, [x0, #48]
-	add	x0, x0, #64
-	tst	x0, #(PAGE_SIZE - 1)
-	b.ne	2b
-	ret
-SYM_FUNC_END(__pi_clear_page)
-SYM_FUNC_ALIAS(clear_page, __pi_clear_page)
-EXPORT_SYMBOL(clear_page)

---
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
change-id: 20260305-aarch64-clear-pages-c-590dae98c333

Best regards,
--  
Linus Walleij <linusw@kernel.org>



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

* Re: [PATCH v2] arm64: clear_page[s] using memset
  2026-09-16 10:03 [PATCH v2] arm64: clear_page[s] using memset Linus Walleij
@ 2026-09-16 15:28 ` Leonardo Bras
  2026-09-16 15:54   ` Leonardo Bras
  0 siblings, 1 reply; 5+ messages in thread
From: Leonardo Bras @ 2026-09-16 15:28 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Leonardo Bras, Catalin Marinas, Will Deacon, Marc Zyngier,
	Oliver Upton, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
	linux-arm-kernel, kvmarm, Usama Anjum

On Wed, Sep 16, 2026 at 12:03:56PM +0200, Linus Walleij wrote:
> There is no need to try to second-guess the compiler when
> clearing memory. Just call memset() like everyone else.
> 
> Since memset() already has an architecture-local MOPS
> optimization, we do not need to do anything else to preserve
> the MOPS optimization.
> 
> While at it, implement the shorthand for directly calling
> the new prototype clear_pages() for larger page chunks.
> 
> No performance regressions can be seen, the fastpath
> benchmarks differences are in the noise.
> 
> Usama Anjum tested next-20260821 with one warm-up and three repeats
> in four sessions, for a total of 12 measured runs. The commands were:
> 
>   perf bench mem memset -k 1GB -f default -s 16GB
>   perf bench mem mmap -p 1GB -f demand -s 32GB -l 5
>   perf bench mem mmap -p 4KB -f demand -s 32GB -l 5
> 
> The results were:
> 
> aws-m7g.metal:
>   Benchmark    Base bytes/sec   Change with patch
>   memset 1GB   63932542232.12                1.92%
>   mmap 1GB     63272579168.03               -0.57%
>   mmap 4KB     49692830849.48               -1.11%
> 
> cesw-aarch64-ampereone-1s-a192-32x:
>   Benchmark    Base bytes/sec   Change with patch
>   memset 1GB   33895562998.71                0.25%
>   mmap 1GB     34338454210.17                1.16%
>   mmap 4KB     25687107580.90               -0.85%
> 
> Assisted-by: LLM
> Suggested-by: Will Deacon <will@kernel.org>
> Tested-by: Usama Anjum <usama.anjum@arm.com>
> Link: https://lore.kernel.org/linux-arm-kernel/20260303-aarch64-clear-pages-v1-1-ad0c3ee9a555@kernel.org/
> Link: https://lore.kernel.org/linux-arm-kernel/67ab6c51-4511-46ab-931b-a4323551147a@arm.com/
> Signed-off-by: Linus Walleij <linusw@kernel.org>
> ---
> Changes in v2:
> - Rebased on v7.3-rc1
> - Include Usama Anjum's performance results and Tested-by tag.
> - Link to v1: https://lore.kernel.org/r/20260306-aarch64-clear-pages-c-v1-1-77c1bb0f1c21@kernel.org
> ---
>  arch/arm64/include/asm/page.h    | 13 +++++++++-
>  arch/arm64/kernel/image-vars.h   |  1 -
>  arch/arm64/kvm/hyp/nvhe/Makefile |  2 +-
>  arch/arm64/lib/Makefile          |  2 +-
>  arch/arm64/lib/clear_page.S      | 53 ----------------------------------------
>  5 files changed, 14 insertions(+), 57 deletions(-)
> 
> diff --git a/arch/arm64/include/asm/page.h b/arch/arm64/include/asm/page.h
> index 58200de8a221..b2eaa63d9056 100644
> --- a/arch/arm64/include/asm/page.h
> +++ b/arch/arm64/include/asm/page.h
> @@ -13,6 +13,7 @@
>  #ifndef __ASSEMBLER__
>  
>  #include <linux/personality.h> /* for READ_IMPLIES_EXEC */
> +#include <linux/string.h> /* for memset() */
>  #include <linux/types.h> /* for gfp_t */
>  #include <asm/pgtable-types.h>
>  
> @@ -20,7 +21,17 @@ struct page;
>  struct vm_area_struct;
>  
>  extern void copy_page(void *to, const void *from);
> -extern void clear_page(void *to);
> +
> +static inline void clear_pages(void *addr, unsigned int npages)
> +{
> +	memset(addr, 0, npages * PAGE_SIZE);
> +}
> +#define clear_pages clear_pages
> +
> +static inline void clear_page(void *addr)
> +{
> +	clear_pages(addr, 1);
> +}
>  
>  void copy_user_highpage(struct page *to, struct page *from,
>  			unsigned long vaddr, struct vm_area_struct *vma);
> diff --git a/arch/arm64/kernel/image-vars.h b/arch/arm64/kernel/image-vars.h
> index 14beb7b9d304..facda8137d9b 100644
> --- a/arch/arm64/kernel/image-vars.h
> +++ b/arch/arm64/kernel/image-vars.h
> @@ -119,7 +119,6 @@ KVM_NVHE_ALIAS(__start___kvm_ex_table);
>  KVM_NVHE_ALIAS(__stop___kvm_ex_table);
>  
>  /* Position-independent library routines */
> -KVM_NVHE_ALIAS_HYP(clear_page, __pi_clear_page);
>  KVM_NVHE_ALIAS_HYP(copy_page, __pi_copy_page);
>  KVM_NVHE_ALIAS_HYP(memcpy, __pi_memcpy);
>  KVM_NVHE_ALIAS_HYP(memset, __pi_memset);
> diff --git a/arch/arm64/kvm/hyp/nvhe/Makefile b/arch/arm64/kvm/hyp/nvhe/Makefile
> index f57450ebcb49..2ed489bf544e 100644
> --- a/arch/arm64/kvm/hyp/nvhe/Makefile
> +++ b/arch/arm64/kvm/hyp/nvhe/Makefile
> @@ -17,7 +17,7 @@ ccflags-y += -fno-stack-protector	\
>  hostprogs := gen-hyprel
>  HOST_EXTRACFLAGS += -I$(objtree)/include
>  
> -lib-objs := clear_page.o copy_page.o memcpy.o memset.o tishift.o
> +lib-objs := copy_page.o memcpy.o memset.o tishift.o
>  lib-objs := $(addprefix ../../../lib/, $(lib-objs))
>  
>  CFLAGS_switch.nvhe.o += -Wno-override-init
> diff --git a/arch/arm64/lib/Makefile b/arch/arm64/lib/Makefile
> index b33e1ca4a781..38ffd359123c 100644
> --- a/arch/arm64/lib/Makefile
> +++ b/arch/arm64/lib/Makefile
> @@ -5,7 +5,7 @@ KCSAN_SANITIZE_delay.o := n
>  
>  lib-y		:= clear_user.o delay.o copy_from_user.o		\
>  		   copy_to_user.o copy_page.o				\
> -		   clear_page.o csum.o insn.o memchr.o memcpy.o		\
> +		   csum.o insn.o memchr.o memcpy.o			\
>  		   memset.o memcmp.o strcmp.o strncmp.o strlen.o	\
>  		   strnlen.o strchr.o strrchr.o tishift.o
>  
> diff --git a/arch/arm64/lib/clear_page.S b/arch/arm64/lib/clear_page.S
> deleted file mode 100644
> index bd6f7d5eb6eb..000000000000
> --- a/arch/arm64/lib/clear_page.S
> +++ /dev/null
> @@ -1,53 +0,0 @@
> -/* SPDX-License-Identifier: GPL-2.0-only */
> -/*
> - * Copyright (C) 2012 ARM Ltd.
> - */
> -
> -#include <linux/linkage.h>
> -#include <linux/const.h>
> -#include <asm/assembler.h>
> -#include <asm/page.h>
> -
> -/*
> - * Clear page @dest
> - *
> - * Parameters:
> - *	x0 - dest
> - */
> -SYM_FUNC_START(__pi_clear_page)
> -#ifdef CONFIG_AS_HAS_MOPS
> -	.arch_extension mops
> -alternative_if_not ARM64_HAS_MOPS
> -	b	.Lno_mops
> -alternative_else_nop_endif
> -
> -	mov	x1, #PAGE_SIZE
> -	setpn	[x0]!, x1!, xzr
> -	setmn	[x0]!, x1!, xzr
> -	seten	[x0]!, x1!, xzr
> -	ret
> -.Lno_mops:
> -#endif
> -	mrs	x1, dczid_el0
> -	tbnz	x1, #4, 2f	/* Branch if DC ZVA is prohibited */
> -	and	w1, w1, #0xf
> -	mov	x2, #4
> -	lsl	x1, x2, x1
> -
> -1:	dc	zva, x0
> -	add	x0, x0, x1
> -	tst	x0, #(PAGE_SIZE - 1)
> -	b.ne	1b
> -	ret
> -
> -2:	stnp	xzr, xzr, [x0]
> -	stnp	xzr, xzr, [x0, #16]
> -	stnp	xzr, xzr, [x0, #32]
> -	stnp	xzr, xzr, [x0, #48]
> -	add	x0, x0, #64
> -	tst	x0, #(PAGE_SIZE - 1)
> -	b.ne	2b
> -	ret
> -SYM_FUNC_END(__pi_clear_page)
> -SYM_FUNC_ALIAS(clear_page, __pi_clear_page)
> -EXPORT_SYMBOL(clear_page)
> 
> ---
> base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
> change-id: 20260305-aarch64-clear-pages-c-590dae98c333
> 
> Best regards,
> --  
> Linus Walleij <linusw@kernel.org>
> 
> 

Oh, cool!
You remove an in-kernel asm implementation to favor the standard memset(), 
which allows you to also have a batched clear_pages() version, which should 
also be a bit more optimized than the do-while loop running clear_page().

Last thing I would check is if there is any particular part of the current 
clear_page() that could have different behavior than the one in memset(), 
and if it would cause any kind of unexpected impact. 

Looking on that, I see that the memset() implementation uses setp, setm, 
sete, while the clear_page()'s uses setpn, setmn, setn for the case with 
MOPS. But then, reading into the docs, the instructions seem pretty much 
the same thing. 

The arguments seem the same idea, exept for clear_page() always being xzr 
instead of the memset value in xn, but should be the same thing.

The generic (no-MOPS) version is very different, though, which is expected 
as the purpose of clean_page() is a particular case of memset(). Was any 
test ran in machines without FEAT_MOPS?

Thanks!
Leo


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

* Re: [PATCH v2] arm64: clear_page[s] using memset
  2026-09-16 15:28 ` Leonardo Bras
@ 2026-09-16 15:54   ` Leonardo Bras
  2026-09-23 16:14     ` Catalin Marinas
  0 siblings, 1 reply; 5+ messages in thread
From: Leonardo Bras @ 2026-09-16 15:54 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Leonardo Bras, Catalin Marinas, Will Deacon, Marc Zyngier,
	Oliver Upton, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
	linux-arm-kernel, kvmarm, Usama Anjum

On Wed, Sep 16, 2026 at 04:28:51PM +0100, Leonardo Bras wrote:
> On Wed, Sep 16, 2026 at 12:03:56PM +0200, Linus Walleij wrote:
> > There is no need to try to second-guess the compiler when
> > clearing memory. Just call memset() like everyone else.
> > 
> > Since memset() already has an architecture-local MOPS
> > optimization, we do not need to do anything else to preserve
> > the MOPS optimization.
> > 
> > While at it, implement the shorthand for directly calling
> > the new prototype clear_pages() for larger page chunks.
> > 
> > No performance regressions can be seen, the fastpath
> > benchmarks differences are in the noise.
> > 
> > Usama Anjum tested next-20260821 with one warm-up and three repeats
> > in four sessions, for a total of 12 measured runs. The commands were:
> > 
> >   perf bench mem memset -k 1GB -f default -s 16GB
> >   perf bench mem mmap -p 1GB -f demand -s 32GB -l 5
> >   perf bench mem mmap -p 4KB -f demand -s 32GB -l 5
> > 
> > The results were:
> > 
> > aws-m7g.metal:
> >   Benchmark    Base bytes/sec   Change with patch
> >   memset 1GB   63932542232.12                1.92%
> >   mmap 1GB     63272579168.03               -0.57%
> >   mmap 4KB     49692830849.48               -1.11%
> > 
> > cesw-aarch64-ampereone-1s-a192-32x:
> >   Benchmark    Base bytes/sec   Change with patch
> >   memset 1GB   33895562998.71                0.25%
> >   mmap 1GB     34338454210.17                1.16%
> >   mmap 4KB     25687107580.90               -0.85%
> > 
> > Assisted-by: LLM
> > Suggested-by: Will Deacon <will@kernel.org>
> > Tested-by: Usama Anjum <usama.anjum@arm.com>
> > Link: https://lore.kernel.org/linux-arm-kernel/20260303-aarch64-clear-pages-v1-1-ad0c3ee9a555@kernel.org/
> > Link: https://lore.kernel.org/linux-arm-kernel/67ab6c51-4511-46ab-931b-a4323551147a@arm.com/
> > Signed-off-by: Linus Walleij <linusw@kernel.org>
> > ---
> > Changes in v2:
> > - Rebased on v7.3-rc1
> > - Include Usama Anjum's performance results and Tested-by tag.
> > - Link to v1: https://lore.kernel.org/r/20260306-aarch64-clear-pages-c-v1-1-77c1bb0f1c21@kernel.org
> > ---
> >  arch/arm64/include/asm/page.h    | 13 +++++++++-
> >  arch/arm64/kernel/image-vars.h   |  1 -
> >  arch/arm64/kvm/hyp/nvhe/Makefile |  2 +-
> >  arch/arm64/lib/Makefile          |  2 +-
> >  arch/arm64/lib/clear_page.S      | 53 ----------------------------------------
> >  5 files changed, 14 insertions(+), 57 deletions(-)
> > 
> > diff --git a/arch/arm64/include/asm/page.h b/arch/arm64/include/asm/page.h
> > index 58200de8a221..b2eaa63d9056 100644
> > --- a/arch/arm64/include/asm/page.h
> > +++ b/arch/arm64/include/asm/page.h
> > @@ -13,6 +13,7 @@
> >  #ifndef __ASSEMBLER__
> >  
> >  #include <linux/personality.h> /* for READ_IMPLIES_EXEC */
> > +#include <linux/string.h> /* for memset() */
> >  #include <linux/types.h> /* for gfp_t */
> >  #include <asm/pgtable-types.h>
> >  
> > @@ -20,7 +21,17 @@ struct page;
> >  struct vm_area_struct;
> >  
> >  extern void copy_page(void *to, const void *from);
> > -extern void clear_page(void *to);
> > +
> > +static inline void clear_pages(void *addr, unsigned int npages)
> > +{
> > +	memset(addr, 0, npages * PAGE_SIZE);
> > +}
> > +#define clear_pages clear_pages
> > +
> > +static inline void clear_page(void *addr)
> > +{
> > +	clear_pages(addr, 1);
> > +}
> >  
> >  void copy_user_highpage(struct page *to, struct page *from,
> >  			unsigned long vaddr, struct vm_area_struct *vma);
> > diff --git a/arch/arm64/kernel/image-vars.h b/arch/arm64/kernel/image-vars.h
> > index 14beb7b9d304..facda8137d9b 100644
> > --- a/arch/arm64/kernel/image-vars.h
> > +++ b/arch/arm64/kernel/image-vars.h
> > @@ -119,7 +119,6 @@ KVM_NVHE_ALIAS(__start___kvm_ex_table);
> >  KVM_NVHE_ALIAS(__stop___kvm_ex_table);
> >  
> >  /* Position-independent library routines */
> > -KVM_NVHE_ALIAS_HYP(clear_page, __pi_clear_page);
> >  KVM_NVHE_ALIAS_HYP(copy_page, __pi_copy_page);
> >  KVM_NVHE_ALIAS_HYP(memcpy, __pi_memcpy);
> >  KVM_NVHE_ALIAS_HYP(memset, __pi_memset);
> > diff --git a/arch/arm64/kvm/hyp/nvhe/Makefile b/arch/arm64/kvm/hyp/nvhe/Makefile
> > index f57450ebcb49..2ed489bf544e 100644
> > --- a/arch/arm64/kvm/hyp/nvhe/Makefile
> > +++ b/arch/arm64/kvm/hyp/nvhe/Makefile
> > @@ -17,7 +17,7 @@ ccflags-y += -fno-stack-protector	\
> >  hostprogs := gen-hyprel
> >  HOST_EXTRACFLAGS += -I$(objtree)/include
> >  
> > -lib-objs := clear_page.o copy_page.o memcpy.o memset.o tishift.o
> > +lib-objs := copy_page.o memcpy.o memset.o tishift.o
> >  lib-objs := $(addprefix ../../../lib/, $(lib-objs))
> >  
> >  CFLAGS_switch.nvhe.o += -Wno-override-init
> > diff --git a/arch/arm64/lib/Makefile b/arch/arm64/lib/Makefile
> > index b33e1ca4a781..38ffd359123c 100644
> > --- a/arch/arm64/lib/Makefile
> > +++ b/arch/arm64/lib/Makefile
> > @@ -5,7 +5,7 @@ KCSAN_SANITIZE_delay.o := n
> >  
> >  lib-y		:= clear_user.o delay.o copy_from_user.o		\
> >  		   copy_to_user.o copy_page.o				\
> > -		   clear_page.o csum.o insn.o memchr.o memcpy.o		\
> > +		   csum.o insn.o memchr.o memcpy.o			\
> >  		   memset.o memcmp.o strcmp.o strncmp.o strlen.o	\
> >  		   strnlen.o strchr.o strrchr.o tishift.o
> >  
> > diff --git a/arch/arm64/lib/clear_page.S b/arch/arm64/lib/clear_page.S
> > deleted file mode 100644
> > index bd6f7d5eb6eb..000000000000
> > --- a/arch/arm64/lib/clear_page.S
> > +++ /dev/null
> > @@ -1,53 +0,0 @@
> > -/* SPDX-License-Identifier: GPL-2.0-only */
> > -/*
> > - * Copyright (C) 2012 ARM Ltd.
> > - */
> > -
> > -#include <linux/linkage.h>
> > -#include <linux/const.h>
> > -#include <asm/assembler.h>
> > -#include <asm/page.h>
> > -
> > -/*
> > - * Clear page @dest
> > - *
> > - * Parameters:
> > - *	x0 - dest
> > - */
> > -SYM_FUNC_START(__pi_clear_page)
> > -#ifdef CONFIG_AS_HAS_MOPS
> > -	.arch_extension mops
> > -alternative_if_not ARM64_HAS_MOPS
> > -	b	.Lno_mops
> > -alternative_else_nop_endif
> > -
> > -	mov	x1, #PAGE_SIZE
> > -	setpn	[x0]!, x1!, xzr
> > -	setmn	[x0]!, x1!, xzr
> > -	seten	[x0]!, x1!, xzr
> > -	ret
> > -.Lno_mops:
> > -#endif
> > -	mrs	x1, dczid_el0
> > -	tbnz	x1, #4, 2f	/* Branch if DC ZVA is prohibited */
> > -	and	w1, w1, #0xf
> > -	mov	x2, #4
> > -	lsl	x1, x2, x1
> > -
> > -1:	dc	zva, x0
> > -	add	x0, x0, x1
> > -	tst	x0, #(PAGE_SIZE - 1)
> > -	b.ne	1b
> > -	ret
> > -
> > -2:	stnp	xzr, xzr, [x0]
> > -	stnp	xzr, xzr, [x0, #16]
> > -	stnp	xzr, xzr, [x0, #32]
> > -	stnp	xzr, xzr, [x0, #48]
> > -	add	x0, x0, #64
> > -	tst	x0, #(PAGE_SIZE - 1)
> > -	b.ne	2b
> > -	ret
> > -SYM_FUNC_END(__pi_clear_page)
> > -SYM_FUNC_ALIAS(clear_page, __pi_clear_page)
> > -EXPORT_SYMBOL(clear_page)
> > 
> > ---
> > base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
> > change-id: 20260305-aarch64-clear-pages-c-590dae98c333
> > 
> > Best regards,
> > --  
> > Linus Walleij <linusw@kernel.org>
> > 
> > 
> 
> Oh, cool!
> You remove an in-kernel asm implementation to favor the standard memset(), 
> which allows you to also have a batched clear_pages() version, which should 
> also be a bit more optimized than the do-while loop running clear_page().
> 
> Last thing I would check is if there is any particular part of the current 
> clear_page() that could have different behavior than the one in memset(), 
> and if it would cause any kind of unexpected impact. 
> 
> Looking on that, I see that the memset() implementation uses setp, setm, 
> sete, while the clear_page()'s uses setpn, setmn, setn for the case with 
> MOPS. But then, reading into the docs, the instructions seem pretty much 
> the same thing. 
> 
> The arguments seem the same idea, exept for clear_page() always being xzr 
> instead of the memset value in xn, but should be the same thing.
> 
> The generic (no-MOPS) version is very different, though, which is expected 
> as the purpose of clean_page() is a particular case of memset(). Was any 
> test ran in machines without FEAT_MOPS?
> 
> Thanks!
> Leo

Oh, I browsed a bit here, and IIUC none of the tested machines have 
FEAT_MOPS, is that right?

If that's the case, the tests are exactly to what is different between 
patched and current versions. There should be no impact on MOPS version as 
the instructions are basically the same.

If I got that right, then, FWIW:
Reviewed-by: Leonardo Bras <leo.bras@arm.com>

Thanks!
Leo


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

* Re: [PATCH v2] arm64: clear_page[s] using memset
  2026-09-16 15:54   ` Leonardo Bras
@ 2026-09-23 16:14     ` Catalin Marinas
  2026-10-08 13:30       ` Muhammad Usama Anjum
  0 siblings, 1 reply; 5+ messages in thread
From: Catalin Marinas @ 2026-09-23 16:14 UTC (permalink / raw)
  To: Leonardo Bras
  Cc: Linus Walleij, Will Deacon, Marc Zyngier, Oliver Upton,
	Joey Gouly, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
	kvmarm, Usama Anjum

On Wed, Sep 16, 2026 at 04:54:09PM +0100, Leonardo Bras wrote:
> On Wed, Sep 16, 2026 at 04:28:51PM +0100, Leonardo Bras wrote:
> > On Wed, Sep 16, 2026 at 12:03:56PM +0200, Linus Walleij wrote:
> > > There is no need to try to second-guess the compiler when
> > > clearing memory. Just call memset() like everyone else.
> > > 
> > > Since memset() already has an architecture-local MOPS
> > > optimization, we do not need to do anything else to preserve
> > > the MOPS optimization.
> > > 
> > > While at it, implement the shorthand for directly calling
> > > the new prototype clear_pages() for larger page chunks.
> > > 
> > > No performance regressions can be seen, the fastpath
> > > benchmarks differences are in the noise.
> > > 
> > > Usama Anjum tested next-20260821 with one warm-up and three repeats
> > > in four sessions, for a total of 12 measured runs. The commands were:
> > > 
> > >   perf bench mem memset -k 1GB -f default -s 16GB
> > >   perf bench mem mmap -p 1GB -f demand -s 32GB -l 5
> > >   perf bench mem mmap -p 4KB -f demand -s 32GB -l 5
> > > 
> > > The results were:
> > > 
> > > aws-m7g.metal:
> > >   Benchmark    Base bytes/sec   Change with patch
> > >   memset 1GB   63932542232.12                1.92%
> > >   mmap 1GB     63272579168.03               -0.57%
> > >   mmap 4KB     49692830849.48               -1.11%
> > > 
> > > cesw-aarch64-ampereone-1s-a192-32x:
> > >   Benchmark    Base bytes/sec   Change with patch
> > >   memset 1GB   33895562998.71                0.25%
> > >   mmap 1GB     34338454210.17                1.16%
> > >   mmap 4KB     25687107580.90               -0.85%
[...]
> > Looking on that, I see that the memset() implementation uses setp, setm, 
> > sete, while the clear_page()'s uses setpn, setmn, setn for the case with 
> > MOPS. But then, reading into the docs, the instructions seem pretty much 
> > the same thing. 
[...]
> Oh, I browsed a bit here, and IIUC none of the tested machines have 
> FEAT_MOPS, is that right?
> 
> If that's the case, the tests are exactly to what is different between 
> patched and current versions. There should be no impact on MOPS version as 
> the instructions are basically the same.

Logically, yes, they are the same. From a performance perspective, there
may be a difference between the temporal and non-temporal variants,
depending on the usage.

I think it would be good to run the benchmarks with the current
implementation without DC ZVA. I don't think we have an easy way to do
this on the command line, so we can simply hard-code the DZP=1 check and
fall back to the STP or STNP in both cases.

Otherwise I'm fine with the patch as well, good clean-up.

-- 
Catalin


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

* Re: [PATCH v2] arm64: clear_page[s] using memset
  2026-09-23 16:14     ` Catalin Marinas
@ 2026-10-08 13:30       ` Muhammad Usama Anjum
  0 siblings, 0 replies; 5+ messages in thread
From: Muhammad Usama Anjum @ 2026-10-08 13:30 UTC (permalink / raw)
  To: Catalin Marinas, Leonardo Bras
  Cc: usama.anjum, Linus Walleij, Will Deacon, Marc Zyngier,
	Oliver Upton, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
	linux-arm-kernel, kvmarm

On 23/09/2026 5:14 pm, Catalin Marinas wrote:
> On Wed, Sep 16, 2026 at 04:54:09PM +0100, Leonardo Bras wrote:
>> On Wed, Sep 16, 2026 at 04:28:51PM +0100, Leonardo Bras wrote:
>>> On Wed, Sep 16, 2026 at 12:03:56PM +0200, Linus Walleij wrote:
>>>> There is no need to try to second-guess the compiler when
>>>> clearing memory. Just call memset() like everyone else.
>>>>
>>>> Since memset() already has an architecture-local MOPS
>>>> optimization, we do not need to do anything else to preserve
>>>> the MOPS optimization.
>>>>
>>>> While at it, implement the shorthand for directly calling
>>>> the new prototype clear_pages() for larger page chunks.
>>>>
>>>> No performance regressions can be seen, the fastpath
>>>> benchmarks differences are in the noise.
>>>>
>>>> Usama Anjum tested next-20260821 with one warm-up and three repeats
>>>> in four sessions, for a total of 12 measured runs. The commands were:
>>>>
>>>>   perf bench mem memset -k 1GB -f default -s 16GB
>>>>   perf bench mem mmap -p 1GB -f demand -s 32GB -l 5
>>>>   perf bench mem mmap -p 4KB -f demand -s 32GB -l 5
>>>>
>>>> The results were:
>>>>
>>>> aws-m7g.metal:
>>>>   Benchmark    Base bytes/sec   Change with patch
>>>>   memset 1GB   63932542232.12                1.92%
>>>>   mmap 1GB     63272579168.03               -0.57%
>>>>   mmap 4KB     49692830849.48               -1.11%
>>>>
>>>> cesw-aarch64-ampereone-1s-a192-32x:
>>>>   Benchmark    Base bytes/sec   Change with patch
>>>>   memset 1GB   33895562998.71                0.25%
>>>>   mmap 1GB     34338454210.17                1.16%
>>>>   mmap 4KB     25687107580.90               -0.85%
> [...]
>>> Looking on that, I see that the memset() implementation uses setp, setm, 
>>> sete, while the clear_page()'s uses setpn, setmn, setn for the case with 
>>> MOPS. But then, reading into the docs, the instructions seem pretty much 
>>> the same thing. 
> [...]
>> Oh, I browsed a bit here, and IIUC none of the tested machines have 
>> FEAT_MOPS, is that right?
>>
>> If that's the case, the tests are exactly to what is different between 
>> patched and current versions. There should be no impact on MOPS version as 
>> the instructions are basically the same.
> 
> Logically, yes, they are the same. From a performance perspective, there
> may be a difference between the temporal and non-temporal variants,
> depending on the usage.
> 
> I think it would be good to run the benchmarks with the current
> implementation without DC ZVA. I don't think we have an easy way to do
> this on the command line, so we can simply hard-code the DZP=1 check and
> fall back to the STP or STNP in both cases.
> 
> Otherwise I'm fine with the patch as well, good clean-up.


With DC ZVA bypassed, clear_page() STNP and patched memset() STP showed only
2 statistically significant regressions on AmpereOne machine.

Results for SUT Class aws-m7g.metal:

+-----------+------------------------------------------------+-----------------------+---------------------+--------------------+-------------------------+
| Benchmark | Result Class                                   | next-20260922-vanilla | next-20260922-patch | next-20260922-stnp | next-20260922-patch_stp |
+===========+================================================+=======================+=====================+====================+=========================+
| perf/mem  | memset -k 1GB -f default -s 16GB (bytes/sec)   |        65367617364.09 |              -0.32% |              0.70% |                  -0.80% |
|           | mmap -p 1GB -f demand -s 32GB -l 5 (bytes/sec) |        63750796288.65 |               0.59% |             -1.56% |                  -1.15% |
|           | mmap -p 4KB -f demand -s 32GB -l 5 (bytes/sec) |        49588573632.22 |              -0.39% |             -0.40% |                  -0.93% |
+-----------+------------------------------------------------+-----------------------+---------------------+--------------------+-------------------------+

Results for SUT Class cesw-aarch64-ampereone-1s-a192-32x:

+-----------+------------------------------------------------+-----------------------+---------------------+--------------------+-------------------------+
| Benchmark | Result Class                                   | next-20260922-vanilla | next-20260922-patch | next-20260922-stnp | next-20260922-patch_stp |
+===========+================================================+=======================+=====================+====================+=========================+
| perf/mem  | memset -k 1GB -f default -s 16GB (bytes/sec)   |        35882584735.80 |              -0.36% |              0.43% |                  -0.00% |
|           | mmap -p 1GB -f demand -s 32GB -l 5 (bytes/sec) |        36204572635.77 |              -1.17% |         (R) -2.07% |                   0.14% |
|           | mmap -p 4KB -f demand -s 32GB -l 5 (bytes/sec) |        27149223683.79 |               0.54% |         (R) -2.48% |                  -0.13% |
+-----------+------------------------------------------------+-----------------------+---------------------+--------------------+-------------------------+

Patches
-------

Forced STNP:

diff --git a/arch/arm64/lib/clear_page.S b/arch/arm64/lib/clear_page.S
index bd6f7d5eb6eb6..29796024b3254 100644
--- a/arch/arm64/lib/clear_page.S
+++ b/arch/arm64/lib/clear_page.S
@@ -28,8 +28,7 @@ alternative_else_nop_endif
 	ret
 .Lno_mops:
 #endif
-	mrs	x1, dczid_el0
-	tbnz	x1, #4, 2f	/* Branch if DC ZVA is prohibited */
+	b	2f		/* Test-only: disable DC ZVA. */
 	and	w1, w1, #0xf
 	mov	x2, #4
 	lsl	x1, x2, x1

Forced STP:

diff --git a/arch/arm64/lib/memset.S b/arch/arm64/lib/memset.S
index 97157da65ec6b..9a51511dc57f7 100644
--- a/arch/arm64/lib/memset.S
+++ b/arch/arm64/lib/memset.S
@@ -146,8 +146,7 @@ SYM_FUNC_START_LOCAL(__pi_memset_generic)
 	cmp	count, #128
 	b.lt	.Lnot_short /*count is at least  128 bytes*/
 
-	mrs	tmp1, dczid_el0
-	tbnz	tmp1, #4, .Lnot_short
+	b	.Lnot_short	/* Test-only: disable DC ZVA. */
 	mov	tmp3w, #4
 	and	zva_len, tmp1w, #15	/* Safety: other bits reserved.  */
 	lsl	zva_len, tmp3w, zva_len

-- 
Thanks,
Usama


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

end of thread, other threads:[~2026-10-08 13:31 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-16 10:03 [PATCH v2] arm64: clear_page[s] using memset Linus Walleij
2026-09-16 15:28 ` Leonardo Bras
2026-09-16 15:54   ` Leonardo Bras
2026-09-23 16:14     ` Catalin Marinas
2026-10-08 13:30       ` Muhammad Usama Anjum

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