From: Mark Rutland <mark.rutland@arm.com>
To: linux-arm-kernel@lists.infradead.org
Cc: mark.rutland@arm.com, vladimir.murzin@arm.com,
usama.anjum@arm.com, peterz@infradead.org,
catalin.marinas@arm.com, david.laight.linux@gmail.com,
stable@vger.kernel.org, ruanjinjie@huawei.com,
james.morse@arm.com, yang@os.amperecomputing.com, cl@gentwo.org,
maz@kernel.org, will@kernel.org, ardb@kernel.org
Subject: [PATCH v3 04/21] arm64: cmpxchg: LL/SC: Avoid redundant extension
Date: Fri, 4 Sep 2026 17:56:49 +0100 [thread overview]
Message-ID: <20260904165649.376930-1-mark.rutland@arm.com> (raw)
In-Reply-To: <20260904161758.376504-1-mark.rutland@arm.com>
The __ll_sc__cmpxchg_case_##name##sz() template always takes 'old' as an
unsigned long. When 'sz' is less than 32 bits, the 'old' is explicitly
truncated so that old[31:sz-1] is zero, which is necessary so that the
W-register EOR+CBNZ sequence doesn't fail spuriously.
For 32-bit types specifically, no extension is necessary, but as 'old'
is 64 bits, the caller must extend 'old' to 64 bits. This leads to a
redundant instruction (typically a MOV) to extend the value, as can be
seen from disassembly of the test case below.
Avoid this by always taking 'old' as a 'u##sz' type, and explicitly
zero-extend this to a 64-bit or 32-bit type matching the X or W register
used by the assembly. To make this work, the table of cases now
explicitly lists 'x' for the cases where an X register is used.
For 32-bit types this removes the redundant instruction. For 64-bit
types there is no truncation, and hence no change. For {16,8}-bit types,
the existing zero-extension to 64-bit is equivalent to the new
zero-extension to 32-bit, and compilers happen to use the same
instructions for this.
I've placed the logic for zero extension into a new <asm/xwreg.h> header
as it will be used by other logic in subsequent patches.
Test case:
| u32 outline_cmpxchg_u32(u32 *p, u32 o, u32 n)
| {
| return cmpxchg(p, o, n);
| }
Before this patch:
| <outline_cmpxchg_u32>:
| b 1f
| casal w1, w2, [x0]
| mov w0, w1
| ret
| 1: mov w3, w1
| prfm pstl1strm, [x0]
| 2: ldxr w1, [x0]
| eor w4, w1, w3
| cbnz w4, 3f
| stlxr w4, w2, [x0]
| cbnz w4, 2b
| dmb ish
| 3: mov w0, w1
| ret
After this patch:
| <outline_cmpxchg_u32>:
| b 1f
| casal w1, w2, [x0]
| mov w0, w1
| ret
| 1: prfm pstl1strm, [x0]
| 2: ldxr w3, [x0]
| eor w4, w3, w1
| cbnz w4, 3f
| stlxr w4, w2, [x0]
| cbnz w4, 2b
| dmb ish
| 3: mov w0, w3
| ret
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Tested-by: Muhammad Usama Anjum <usama.anjum@arm.com>
Cc: Ada Couprie Diaz <ada.coupriediaz@arm.com>
Cc: Ard Biesheuvel <ardb@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: James Morse <james.morse@arm.com>
Cc: Jinjie Ruan <ruanjinjie@huawei.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Vladimir Murzin <vladimir.murzin@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Yang Shi <yang@os.amperecomputing.com>
---
arch/arm64/include/asm/atomic_ll_sc.h | 27 +++++++++++++--------------
arch/arm64/include/asm/xwreg.h | 16 ++++++++++++++++
2 files changed, 29 insertions(+), 14 deletions(-)
create mode 100644 arch/arm64/include/asm/xwreg.h
diff --git a/arch/arm64/include/asm/atomic_ll_sc.h b/arch/arm64/include/asm/atomic_ll_sc.h
index 89d2ba2723590..160c1251f0ec9 100644
--- a/arch/arm64/include/asm/atomic_ll_sc.h
+++ b/arch/arm64/include/asm/atomic_ll_sc.h
@@ -11,6 +11,7 @@
#define __ASM_ATOMIC_LL_SC_H
#include <linux/stringify.h>
+#include <asm/xwreg.h>
#ifndef CONFIG_CC_HAS_K_CONSTRAINT
#define K
@@ -239,20 +240,17 @@ __ll_sc_atomic64_dec_if_positive(atomic64_t *v)
#define __CMPXCHG_CASE(w, sfx, name, sz, mb, acq, rel, cl, constraint) \
static __always_inline u##sz \
__ll_sc__cmpxchg_case_##name##sz(volatile void *ptr, \
- unsigned long old, \
+ u##sz old, \
u##sz new) \
{ \
+ /* \
+ * Sub-word sizes require zero extension so that EOR+CBNZ won't \
+ * consume non-zero upper bits of the register containing "old".\
+ */ \
+ xwreg_t(w) cmpval = xwreg_zero_extend(old, w, sz); \
unsigned long tmp; \
u##sz oldval; \
\
- /* \
- * Sub-word sizes require explicit casting so that the compare \
- * part of the cmpxchg doesn't end up interpreting non-zero \
- * upper bits of the register containing "old". \
- */ \
- if (sz < 32) \
- old = (u##sz)old; \
- \
asm volatile( \
" prfm pstl1strm, %[v]\n" \
"1: ld" #acq "xr" #sfx "\t%" #w "[oldval], %[v]\n" \
@@ -264,7 +262,8 @@ __ll_sc__cmpxchg_case_##name##sz(volatile void *ptr, \
"2:" \
: [tmp] "=&r" (tmp), [oldval] "=&r" (oldval), \
[v] "+Q" (*(u##sz *)ptr) \
- : [old] __stringify(constraint) "r" (old), [new] "r" (new) \
+ : [old] __stringify(constraint) "r" (cmpval), \
+ [new] "r" (new) \
: cl); \
\
return oldval; \
@@ -278,19 +277,19 @@ __ll_sc__cmpxchg_case_##name##sz(volatile void *ptr, \
__CMPXCHG_CASE(w, b, , 8, , , , , K)
__CMPXCHG_CASE(w, h, , 16, , , , , K)
__CMPXCHG_CASE(w, , , 32, , , , , K)
-__CMPXCHG_CASE( , , , 64, , , , , L)
+__CMPXCHG_CASE(x, , , 64, , , , , L)
__CMPXCHG_CASE(w, b, acq_, 8, , a, , "memory", K)
__CMPXCHG_CASE(w, h, acq_, 16, , a, , "memory", K)
__CMPXCHG_CASE(w, , acq_, 32, , a, , "memory", K)
-__CMPXCHG_CASE( , , acq_, 64, , a, , "memory", L)
+__CMPXCHG_CASE(x, , acq_, 64, , a, , "memory", L)
__CMPXCHG_CASE(w, b, rel_, 8, , , l, "memory", K)
__CMPXCHG_CASE(w, h, rel_, 16, , , l, "memory", K)
__CMPXCHG_CASE(w, , rel_, 32, , , l, "memory", K)
-__CMPXCHG_CASE( , , rel_, 64, , , l, "memory", L)
+__CMPXCHG_CASE(x, , rel_, 64, , , l, "memory", L)
__CMPXCHG_CASE(w, b, mb_, 8, dmb ish, , l, "memory", K)
__CMPXCHG_CASE(w, h, mb_, 16, dmb ish, , l, "memory", K)
__CMPXCHG_CASE(w, , mb_, 32, dmb ish, , l, "memory", K)
-__CMPXCHG_CASE( , , mb_, 64, dmb ish, , l, "memory", L)
+__CMPXCHG_CASE(x, , mb_, 64, dmb ish, , l, "memory", L)
#undef __CMPXCHG_CASE
diff --git a/arch/arm64/include/asm/xwreg.h b/arch/arm64/include/asm/xwreg.h
new file mode 100644
index 0000000000000..d55e3ac68f7eb
--- /dev/null
+++ b/arch/arm64/include/asm/xwreg.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef __ASM_XWREG_H
+#define __ASM_XWREG_H
+
+#include <asm/types.h>
+
+#define __xwreg_t_x u64
+#define __xwreg_t_w u32
+#define xwreg_t(xw) __xwreg_t_##xw
+
+/*
+ * Zero extend 'v' from 'sz' bits (8/16/32/64) to fill an X or W register.
+ */
+#define xwreg_zero_extend(v, xw, sz) ((xwreg_t(xw))(u##sz)(v))
+
+#endif /* __ASM_XWREG_H */
--
2.30.2
next prev parent reply other threads:[~2026-09-04 16:57 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-04 16:17 [PATCH v3 00/21] arm64: Preemptible this_cpu_*() operations Mark Rutland
2026-09-04 16:17 ` [PATCH v3 03/21] arm64: percpu: Fix LSE operations on {8,16}-bit types Mark Rutland
2026-09-07 8:48 ` Jinjie Ruan
2026-09-04 16:17 ` [PATCH v3 05/21] arm64: cmpxchg128: LSE: Remove redundant operands Mark Rutland
2026-09-04 16:17 ` [PATCH v3 07/21] arm64: preempt: Treat should_resched() as unlikely Mark Rutland
2026-09-04 16:17 ` [PATCH v3 08/21] arm64: ptrace: Always inline pt_regs_[read,write}_reg() Mark Rutland
2026-09-04 16:17 ` [PATCH v3 09/21] arm64: percpu: Factor out percpu offset asm Mark Rutland
2026-09-04 16:17 ` [PATCH v3 10/21] arm64: gpr-num: Add wxN aliases for wN registers Mark Rutland
2026-09-04 16:17 ` [PATCH v3 12/21] arm64: entry: sdei: Restore all clobberable GPRs Mark Rutland
2026-09-04 16:17 ` [PATCH v3 14/21] arm64: percpu: Add infrastructure for preemptible this_cpu_*() ops Mark Rutland
2026-09-04 16:17 ` [PATCH v3 15/21] arm64: percpu: Implement preemptible read/write ops Mark Rutland
2026-09-04 16:17 ` [PATCH v3 16/21] arm64: percpu: Implement preemptible void RMW ops Mark Rutland
2026-09-05 9:03 ` David Laight
2026-09-08 15:07 ` Mark Rutland
2026-09-08 20:52 ` David Laight
2026-09-04 16:17 ` [PATCH v3 17/21] arm64: percpu: Implement preemptible return " Mark Rutland
2026-09-04 16:17 ` [PATCH v3 18/21] arm64: percpu: Implement preemptible XCHG ops Mark Rutland
2026-09-04 16:17 ` [PATCH v3 19/21] arm64: percpu: Implement preemptible CMPXCHG ops Mark Rutland
2026-09-04 16:17 ` [PATCH v3 20/21] arm64: percpu: Implement preemptible CMPXCHG128 ops Mark Rutland
2026-09-04 16:17 ` [PATCH v3 21/21] arm64: percpu: Remove _pcp_protect*() wrappers Mark Rutland
2026-09-04 16:54 ` [PATCH v3 01/21] arm64: percpu: Fix this_cpu_write() casting Mark Rutland
2026-09-04 16:56 ` [PATCH v3 02/21] arm64: percpu: Fix this_cpu_and() mask generation Mark Rutland
2026-09-04 16:56 ` Mark Rutland [this message]
2026-09-04 16:57 ` [PATCH v3 06/21] arm64: preempt: Simplify and optimize __preempt_count_dec_and_test() Mark Rutland
2026-09-04 16:59 ` [PATCH v3 13/21] arm64: entry: sdei: Make 'tsk' available Mark Rutland
2026-09-04 17:43 ` [PATCH v3 00/21] arm64: Preemptible this_cpu_*() operations Mark Rutland
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260904165649.376930-1-mark.rutland@arm.com \
--to=mark.rutland@arm.com \
--cc=ardb@kernel.org \
--cc=catalin.marinas@arm.com \
--cc=cl@gentwo.org \
--cc=david.laight.linux@gmail.com \
--cc=james.morse@arm.com \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=maz@kernel.org \
--cc=peterz@infradead.org \
--cc=ruanjinjie@huawei.com \
--cc=stable@vger.kernel.org \
--cc=usama.anjum@arm.com \
--cc=vladimir.murzin@arm.com \
--cc=will@kernel.org \
--cc=yang@os.amperecomputing.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.