* [PATCH v2] cmpxchg: allow const-qualified old value in cmpxchg()
@ 2026-04-03 1:19 Hangbin Liu
0 siblings, 0 replies; only message in thread
From: Hangbin Liu @ 2026-04-03 1:19 UTC (permalink / raw)
To: Richard Henderson, Matt Turner, Magnus Lindholm, Vineet Gupta,
Brian Cain, James E.J. Bottomley, Helge Deller,
Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
Christophe Leroy (CS GROUP), Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Yoshinori Sato, Rich Felker,
John Paul Adrian Glaubitz, David S. Miller, Andreas Larsson,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Chris Zankel, Max Filippov, David Laight
Cc: linux-alpha, linux-kernel, linux-snps-arc, linux-hexagon,
linux-parisc, linuxppc-dev, linux-riscv, linux-sh, sparclinux,
Jakub Kicinski, Hangbin Liu
The old value passed to cmpxchg() is semantically read-only: it is
only loaded into a register as a comparand and is never written back.
However, the macro currently assigns it implicitly to a local variable
of type __typeof__(*(ptr)), which triggers -Werror=discarded-qualifiers
when old is a const-qualified pointer and ptr points to a non-const type.
Fix this by using __auto_type with the conditional expression trick:
__auto_type _o_ = 1 ? (o) : *(ptr);
The __auto_type deduces the type from the expression, so _o_ naturally
takes the const-qualified type when o is const-qualified, avoiding the
discarded-qualifiers warning. The conditional expression forces the
compiler to verify that o and *(ptr) have compatible types, preserving
the type-safety check that a bare __auto_type _o_ = (o) would lose.
Since the condition is the constant 1, the compiler optimizes away the
*(ptr) branch entirely with no runtime cost.
For macros where ptr is already captured in a local variable (arc's
_p_, hexagon's __ptr, riscv's __ptr), the captured variable is used
in the conditional expression to avoid re-evaluating ptr.
The new value is intentionally left without this treatment: new will
be stored into *ptr, so silently accepting a const-qualified new would
allow callers to store a pointer-to-const into a non-const location
without any compiler warning.
Suggested-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
---
Changes in v2:
- Use __auto_type as the previous way will loose ptr type checking (David Laight)
- Link to v1: https://lore.kernel.org/r/20260402-cmpxchg-v1-1-4561e2c05d2c@gmail.com
---
arch/alpha/include/asm/cmpxchg.h | 4 ++--
arch/arc/include/asm/cmpxchg.h | 4 ++--
arch/hexagon/include/asm/cmpxchg.h | 2 +-
arch/parisc/include/asm/cmpxchg.h | 2 +-
arch/powerpc/include/asm/cmpxchg.h | 8 ++++----
arch/riscv/include/asm/cmpxchg.h | 4 ++--
arch/sh/include/asm/cmpxchg.h | 2 +-
arch/sparc/include/asm/cmpxchg_32.h | 2 +-
arch/sparc/include/asm/cmpxchg_64.h | 2 +-
arch/x86/include/asm/cmpxchg.h | 2 +-
arch/xtensa/include/asm/cmpxchg.h | 2 +-
tools/arch/x86/include/asm/cmpxchg.h | 2 +-
12 files changed, 18 insertions(+), 18 deletions(-)
diff --git a/arch/alpha/include/asm/cmpxchg.h b/arch/alpha/include/asm/cmpxchg.h
index ae1b96479d0c..e6cb60ca35bf 100644
--- a/arch/alpha/include/asm/cmpxchg.h
+++ b/arch/alpha/include/asm/cmpxchg.h
@@ -234,7 +234,7 @@ ____cmpxchg(volatile void *ptr, unsigned long old, unsigned long new,
#define arch_cmpxchg_local(ptr, o, n) \
({ \
- __typeof__(*(ptr)) _o_ = (o); \
+ __auto_type _o_ = 1 ? (o) : *(ptr); \
__typeof__(*(ptr)) _n_ = (n); \
(__typeof__(*(ptr))) ____cmpxchg((ptr), (unsigned long)_o_, \
(unsigned long)_n_, \
@@ -265,7 +265,7 @@ ____cmpxchg(volatile void *ptr, unsigned long old, unsigned long new,
#define arch_cmpxchg(ptr, o, n) \
({ \
__typeof__(*(ptr)) __ret; \
- __typeof__(*(ptr)) _o_ = (o); \
+ __auto_type _o_ = 1 ? (o) : *(ptr); \
__typeof__(*(ptr)) _n_ = (n); \
smp_mb(); \
__ret = (__typeof__(*(ptr))) ____cmpxchg((ptr), \
diff --git a/arch/arc/include/asm/cmpxchg.h b/arch/arc/include/asm/cmpxchg.h
index 76f43db0890f..e18609174133 100644
--- a/arch/arc/include/asm/cmpxchg.h
+++ b/arch/arc/include/asm/cmpxchg.h
@@ -42,7 +42,7 @@
#define arch_cmpxchg_relaxed(ptr, old, new) \
({ \
__typeof__(ptr) _p_ = (ptr); \
- __typeof__(*(ptr)) _o_ = (old); \
+ __auto_type _o_ = 1 ? (old) : *(_p_); \
__typeof__(*(ptr)) _n_ = (new); \
__typeof__(*(ptr)) _prev_; \
\
@@ -64,7 +64,7 @@
#define arch_cmpxchg(ptr, old, new) \
({ \
volatile __typeof__(ptr) _p_ = (ptr); \
- __typeof__(*(ptr)) _o_ = (old); \
+ __auto_type _o_ = 1 ? (old) : *(_p_); \
__typeof__(*(ptr)) _n_ = (new); \
__typeof__(*(ptr)) _prev_; \
unsigned long __flags; \
diff --git a/arch/hexagon/include/asm/cmpxchg.h b/arch/hexagon/include/asm/cmpxchg.h
index 9c58fb81f7fd..88986fe04072 100644
--- a/arch/hexagon/include/asm/cmpxchg.h
+++ b/arch/hexagon/include/asm/cmpxchg.h
@@ -54,7 +54,7 @@ __arch_xchg(unsigned long x, volatile void *ptr, int size)
#define arch_cmpxchg(ptr, old, new) \
({ \
__typeof__(ptr) __ptr = (ptr); \
- __typeof__(*(ptr)) __old = (old); \
+ __auto_type __old = 1 ? (old) : *(__ptr); \
__typeof__(*(ptr)) __new = (new); \
__typeof__(*(ptr)) __oldval = (__typeof__(*(ptr))) 0; \
\
diff --git a/arch/parisc/include/asm/cmpxchg.h b/arch/parisc/include/asm/cmpxchg.h
index bf0a0f1189eb..30748e31aa17 100644
--- a/arch/parisc/include/asm/cmpxchg.h
+++ b/arch/parisc/include/asm/cmpxchg.h
@@ -78,7 +78,7 @@ __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new_, int size)
#define arch_cmpxchg(ptr, o, n) \
({ \
- __typeof__(*(ptr)) _o_ = (o); \
+ __auto_type _o_ = 1 ? (o) : *(ptr); \
__typeof__(*(ptr)) _n_ = (n); \
(__typeof__(*(ptr))) __cmpxchg((ptr), (unsigned long)_o_, \
(unsigned long)_n_, sizeof(*(ptr))); \
diff --git a/arch/powerpc/include/asm/cmpxchg.h b/arch/powerpc/include/asm/cmpxchg.h
index dbb50c06f0bf..eaf88b7d0443 100644
--- a/arch/powerpc/include/asm/cmpxchg.h
+++ b/arch/powerpc/include/asm/cmpxchg.h
@@ -698,7 +698,7 @@ __cmpxchg_acquire(void *ptr, unsigned long old, unsigned long new,
}
#define arch_cmpxchg(ptr, o, n) \
({ \
- __typeof__(*(ptr)) _o_ = (o); \
+ __auto_type _o_ = 1 ? (o) : *(ptr); \
__typeof__(*(ptr)) _n_ = (n); \
(__typeof__(*(ptr))) __cmpxchg((ptr), (unsigned long)_o_, \
(unsigned long)_n_, sizeof(*(ptr))); \
@@ -707,7 +707,7 @@ __cmpxchg_acquire(void *ptr, unsigned long old, unsigned long new,
#define arch_cmpxchg_local(ptr, o, n) \
({ \
- __typeof__(*(ptr)) _o_ = (o); \
+ __auto_type _o_ = 1 ? (o) : *(ptr); \
__typeof__(*(ptr)) _n_ = (n); \
(__typeof__(*(ptr))) __cmpxchg_local((ptr), (unsigned long)_o_, \
(unsigned long)_n_, sizeof(*(ptr))); \
@@ -715,7 +715,7 @@ __cmpxchg_acquire(void *ptr, unsigned long old, unsigned long new,
#define arch_cmpxchg_relaxed(ptr, o, n) \
({ \
- __typeof__(*(ptr)) _o_ = (o); \
+ __auto_type _o_ = 1 ? (o) : *(ptr); \
__typeof__(*(ptr)) _n_ = (n); \
(__typeof__(*(ptr))) __cmpxchg_relaxed((ptr), \
(unsigned long)_o_, (unsigned long)_n_, \
@@ -724,7 +724,7 @@ __cmpxchg_acquire(void *ptr, unsigned long old, unsigned long new,
#define arch_cmpxchg_acquire(ptr, o, n) \
({ \
- __typeof__(*(ptr)) _o_ = (o); \
+ __auto_type _o_ = 1 ? (o) : *(ptr); \
__typeof__(*(ptr)) _n_ = (n); \
(__typeof__(*(ptr))) __cmpxchg_acquire((ptr), \
(unsigned long)_o_, (unsigned long)_n_, \
diff --git a/arch/riscv/include/asm/cmpxchg.h b/arch/riscv/include/asm/cmpxchg.h
index 8712cf9c69dc..30ade671298f 100644
--- a/arch/riscv/include/asm/cmpxchg.h
+++ b/arch/riscv/include/asm/cmpxchg.h
@@ -215,7 +215,7 @@
cas_prepend, cas_append) \
({ \
__typeof__(ptr) __ptr = (ptr); \
- __typeof__(*(__ptr)) __old = (old); \
+ __auto_type __old = 1 ? (old) : *(__ptr); \
__typeof__(*(__ptr)) __new = (new); \
__typeof__(*(__ptr)) __ret; \
\
@@ -331,7 +331,7 @@ union __u128_halves {
#define __arch_cmpxchg128(p, o, n, cas_sfx) \
({ \
- __typeof__(*(p)) __o = (o); \
+ __auto_type __o = 1 ? (o) : *(p); \
union __u128_halves __hn = { .full = (n) }; \
union __u128_halves __ho = { .full = (__o) }; \
register unsigned long t1 asm ("t1") = __hn.low; \
diff --git a/arch/sh/include/asm/cmpxchg.h b/arch/sh/include/asm/cmpxchg.h
index 1e5dc5ccf7bf..6c1bfb367f11 100644
--- a/arch/sh/include/asm/cmpxchg.h
+++ b/arch/sh/include/asm/cmpxchg.h
@@ -68,7 +68,7 @@ static inline unsigned long __cmpxchg(volatile void * ptr, unsigned long old,
#define arch_cmpxchg(ptr,o,n) \
({ \
- __typeof__(*(ptr)) _o_ = (o); \
+ __auto_type _o_ = 1 ? (o) : *(ptr); \
__typeof__(*(ptr)) _n_ = (n); \
(__typeof__(*(ptr))) __cmpxchg((ptr), (unsigned long)_o_, \
(unsigned long)_n_, sizeof(*(ptr))); \
diff --git a/arch/sparc/include/asm/cmpxchg_32.h b/arch/sparc/include/asm/cmpxchg_32.h
index 8c1a3ca34eeb..df40a16dd021 100644
--- a/arch/sparc/include/asm/cmpxchg_32.h
+++ b/arch/sparc/include/asm/cmpxchg_32.h
@@ -55,7 +55,7 @@ __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new_, int size)
#define arch_cmpxchg(ptr, o, n) \
({ \
- __typeof__(*(ptr)) _o_ = (o); \
+ __auto_type _o_ = 1 ? (o) : *(ptr); \
__typeof__(*(ptr)) _n_ = (n); \
(__typeof__(*(ptr))) __cmpxchg((ptr), (unsigned long)_o_, \
(unsigned long)_n_, sizeof(*(ptr))); \
diff --git a/arch/sparc/include/asm/cmpxchg_64.h b/arch/sparc/include/asm/cmpxchg_64.h
index 3de25262c411..4b540405fcc7 100644
--- a/arch/sparc/include/asm/cmpxchg_64.h
+++ b/arch/sparc/include/asm/cmpxchg_64.h
@@ -170,7 +170,7 @@ __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size)
#define arch_cmpxchg(ptr,o,n) \
({ \
- __typeof__(*(ptr)) _o_ = (o); \
+ __auto_type _o_ = 1 ? (o) : *(ptr); \
__typeof__(*(ptr)) _n_ = (n); \
(__typeof__(*(ptr))) __cmpxchg((ptr), (unsigned long)_o_, \
(unsigned long)_n_, sizeof(*(ptr))); \
diff --git a/arch/x86/include/asm/cmpxchg.h b/arch/x86/include/asm/cmpxchg.h
index a88b06f1c35e..e0340579ef69 100644
--- a/arch/x86/include/asm/cmpxchg.h
+++ b/arch/x86/include/asm/cmpxchg.h
@@ -85,7 +85,7 @@ extern void __add_wrong_size(void)
#define __raw_cmpxchg(ptr, old, new, size, lock) \
({ \
__typeof__(*(ptr)) __ret; \
- __typeof__(*(ptr)) __old = (old); \
+ __auto_type __old = 1 ? (old) : *(ptr); \
__typeof__(*(ptr)) __new = (new); \
switch (size) { \
case __X86_CASE_B: \
diff --git a/arch/xtensa/include/asm/cmpxchg.h b/arch/xtensa/include/asm/cmpxchg.h
index b6db4838b175..057fd24ed125 100644
--- a/arch/xtensa/include/asm/cmpxchg.h
+++ b/arch/xtensa/include/asm/cmpxchg.h
@@ -83,7 +83,7 @@ __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size)
}
#define arch_cmpxchg(ptr,o,n) \
- ({ __typeof__(*(ptr)) _o_ = (o); \
+ ({ __auto_type _o_ = 1 ? (o) : *(ptr); \
__typeof__(*(ptr)) _n_ = (n); \
(__typeof__(*(ptr))) __cmpxchg((ptr), (unsigned long)_o_, \
(unsigned long)_n_, sizeof (*(ptr))); \
diff --git a/tools/arch/x86/include/asm/cmpxchg.h b/tools/arch/x86/include/asm/cmpxchg.h
index 0ed9ca2766ad..bbd3960c2218 100644
--- a/tools/arch/x86/include/asm/cmpxchg.h
+++ b/tools/arch/x86/include/asm/cmpxchg.h
@@ -35,7 +35,7 @@ extern void __cmpxchg_wrong_size(void)
#define __raw_cmpxchg(ptr, old, new, size, lock) \
({ \
__typeof__(*(ptr)) __ret; \
- __typeof__(*(ptr)) __old = (old); \
+ __auto_type __old = 1 ? (old) : *(ptr); \
__typeof__(*(ptr)) __new = (new); \
switch (size) { \
case __X86_CASE_B: \
---
base-commit: f8f5627a8aeab15183eef8930bf75ba88a51622f
change-id: 20260402-cmpxchg-2fd307f44a41
Best regards,
--
Hangbin Liu <liuhangbin@gmail.com>
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-04-03 1:26 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-03 1:19 [PATCH v2] cmpxchg: allow const-qualified old value in cmpxchg() Hangbin Liu
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox