* [PATCH v3 0/5] Add two-byte cmpxchg emulation and wire it into the architectures
@ 2026-09-17 16:38 Bradley Morgan
2026-09-17 16:38 ` [PATCH v3 1/5] lib: Add two-byte cmpxchg emulation function Bradley Morgan
` (5 more replies)
0 siblings, 6 replies; 9+ messages in thread
From: Bradley Morgan @ 2026-09-17 16:38 UTC (permalink / raw)
To: Andrew Morton, Vineet Gupta, Guo Ren, Yoshinori Sato,
Rich Felker, Chris Zankel, Max Filippov
Cc: Arnd Bergmann, Paul E . McKenney, David Laight,
John Paul Adrian Glaubitz, linux-snps-arc, linux-csky, linux-sh,
linux-kernel, Bradley Morgan
This is v3 of the two byte cmpxchg emulation series, wiring
cmpxchg_emu_u16() into arc, csky, sh and xtensa.
v2 tried u16 as the parameter type. David Laight pointed out that
this does not compile warning free when exchanging a pointer type,
because the switch statements in the architecture macros instantiate
every size case, so a pointer cmpxchg() type checks the two byte
case, and the (u16) casts there warn. v3 takes the old and new
values as unsigned long and casts to u16 inside the function, so the
call sites need no narrowing casts and pointer exchanges compile
clean. The function still compares and returns exactly the 16 bits
of the emulated halfword, which matches hardware cmpxchg r16
behaviour, and a host test of 972 cases across both halfword offsets
against a byte level reference model still passes.
David also noted the missing pointer to integer type check in some
of the macros, so cmpxchg(&p, 4, 5) compiled silently. The csky
macros typed __old and __new through __typeof__(old), which skips
the check, and sh had none either. Both now type check through
(unsigned long)(0 ? *(ptr) : (old)), the idiom David suggested,
which keeps the pointer to integer conversion explicit while making
the compiler reject mismatched types. Both of his test cases now
fail to compile on every macro shape.
The ARMv6 wiring stays dropped from v1, per Arnd Bergmann's offer to
take the INTEGRATOR_CM1136JFS cleanup in his platform removal series.
Thank you, Paul McKenney for suggesting this, and David Laight and
Arnd Bergmann for the review that shaped all three revisions.
Bradley Morgan (5):
lib: Add two-byte cmpxchg emulation function
ARC: Emulate two-byte cmpxchg
csky: Emulate two-byte cmpxchg
sh: Emulate two-byte cmpxchg
xtensa: Emulate two-byte cmpxchg
--
2.47.3
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v3 1/5] lib: Add two-byte cmpxchg emulation function
2026-09-17 16:38 [PATCH v3 0/5] Add two-byte cmpxchg emulation and wire it into the architectures Bradley Morgan
@ 2026-09-17 16:38 ` Bradley Morgan
2026-09-18 8:59 ` David Laight
2026-09-17 16:38 ` [PATCH v3 2/5] ARC: Emulate two-byte cmpxchg Bradley Morgan
` (4 subsequent siblings)
5 siblings, 1 reply; 9+ messages in thread
From: Bradley Morgan @ 2026-09-17 16:38 UTC (permalink / raw)
To: Andrew Morton, Vineet Gupta, Guo Ren, Yoshinori Sato,
Rich Felker, Chris Zankel, Max Filippov
Cc: Arnd Bergmann, Paul E . McKenney, David Laight,
John Paul Adrian Glaubitz, linux-snps-arc, linux-csky, linux-sh,
linux-kernel, Bradley Morgan
cmpxchg_emu_u8() emulates one-byte cmpxchg() in terms of four-byte
cmpxchg() for the architectures lacking native one-byte atomics.
The same architectures also lack native two-byte cmpxchg(), where
such an operation is not supported and either fails to compile via
BUILD_BUG() or fails to link, because the bad pointer sentinels
these architectures declare are never defined.
Add cmpxchg_emu_u16(), the two-byte sibling. It reads the enclosing
word with READ_ONCE(), splices the two target bytes through a union
and loops on cmpxchg() of the full word until the compare succeeds.
Like cmpxchg_emu_u8() it is fully ordered.
Unlike cmpxchg_emu_u8() it casts the old and new values to u16
internally and returns unsigned long, taking the old and new values
as unsigned long, per the suggestion from David Laight. The switch
statements in the architecture macros instantiate every size case,
so a cmpxchg() on a pointer type checks the two-byte case as well,
and a u16 parameter or return would make the macro casts and return
conversions warn there. With unsigned long parameters and return the
call sites need no narrowing casts, pointer exchanges compile warning
free, and the function still compares and returns exactly the 16 bits
the caller asked for, which matches the hardware cmpxchg r16
behaviour where a 16-bit compare only looks at the low 16 bits of
the register.
The Kconfig symbol gating this file is renamed from
ARCH_NEED_CMPXCHG_1_EMU to ARCH_NEED_CMPXCHG_1_2_EMU, as it now
selects both the one-byte and the two-byte emulation.
Suggested-by: Paul E. McKenney <paulmck@kernel.org>
Suggested-by: David Laight <david.laight.linux@gmail.com>
Signed-off-by: Bradley Morgan <brads@mainlining.org>
---
arch/Kconfig | 2 +-
arch/arc/Kconfig | 2 +-
arch/csky/Kconfig | 2 +-
arch/sh/Kconfig | 2 +-
arch/xtensa/Kconfig | 2 +-
include/linux/cmpxchg-emu.h | 4 +++-
lib/Makefile | 2 +-
lib/cmpxchg-emu.c | 40 ++++++++++++++++++++++++++++++++-----
8 files changed, 44 insertions(+), 12 deletions(-)
diff --git a/arch/Kconfig b/arch/Kconfig
index 3bb2e568f5b1..d56064797e09 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -1817,7 +1817,7 @@ config CC_HAS_SANE_FUNCTION_ALIGNMENT
# strict alignment always, even with -falign-functions.
def_bool CC_HAS_MIN_FUNCTION_ALIGNMENT || CC_IS_CLANG
-config ARCH_NEED_CMPXCHG_1_EMU
+config ARCH_NEED_CMPXCHG_1_2_EMU
bool
config ARCH_WANTS_PRE_LINK_VMLINUX
diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig
index 80e61175bf52..0e91d254eccb 100644
--- a/arch/arc/Kconfig
+++ b/arch/arc/Kconfig
@@ -14,7 +14,7 @@ config ARC
select ARCH_HAS_SETUP_DMA_OPS
select ARCH_HAS_SYNC_DMA_FOR_CPU
select ARCH_HAS_SYNC_DMA_FOR_DEVICE
- select ARCH_NEED_CMPXCHG_1_EMU
+ select ARCH_NEED_CMPXCHG_1_2_EMU
select ARCH_SUPPORTS_ATOMIC_RMW if ARC_HAS_LLSC
select ARCH_32BIT_OFF_T
select BUILDTIME_TABLE_SORT
diff --git a/arch/csky/Kconfig b/arch/csky/Kconfig
index 4331313a42ff..5f26e8615d7d 100644
--- a/arch/csky/Kconfig
+++ b/arch/csky/Kconfig
@@ -37,7 +37,7 @@ config CSKY
select ARCH_INLINE_SPIN_UNLOCK_BH if !PREEMPTION
select ARCH_INLINE_SPIN_UNLOCK_IRQ if !PREEMPTION
select ARCH_INLINE_SPIN_UNLOCK_IRQRESTORE if !PREEMPTION
- select ARCH_NEED_CMPXCHG_1_EMU
+ select ARCH_NEED_CMPXCHG_1_2_EMU
select ARCH_WANT_FRAME_POINTERS if !CPU_CK610 && $(cc-option,-mbacktrace)
select ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT
select COMMON_CLK
diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig
index d60f1d5a94c0..09e556fced01 100644
--- a/arch/sh/Kconfig
+++ b/arch/sh/Kconfig
@@ -14,7 +14,7 @@ config SUPERH
select ARCH_HIBERNATION_POSSIBLE if MMU
select ARCH_MIGHT_HAVE_PC_PARPORT
select ARCH_WANT_IPC_PARSE_VERSION
- select ARCH_NEED_CMPXCHG_1_EMU
+ select ARCH_NEED_CMPXCHG_1_2_EMU
select CPU_NO_EFFICIENT_FFS
select DMA_DECLARE_COHERENT
select GENERIC_ATOMIC64
diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig
index f2f9cd9cde50..62019fd444d4 100644
--- a/arch/xtensa/Kconfig
+++ b/arch/xtensa/Kconfig
@@ -14,7 +14,7 @@ config XTENSA
select ARCH_HAS_DMA_SET_UNCACHED if MMU
select ARCH_HAS_STRNCPY_FROM_USER if !KASAN
select ARCH_HAS_STRNLEN_USER
- select ARCH_NEED_CMPXCHG_1_EMU
+ select ARCH_NEED_CMPXCHG_1_2_EMU
select ARCH_USE_MEMTEST
select ARCH_USE_QUEUED_RWLOCKS
select ARCH_USE_QUEUED_SPINLOCKS
diff --git a/include/linux/cmpxchg-emu.h b/include/linux/cmpxchg-emu.h
index 998deec67740..2db70f1e3925 100644
--- a/include/linux/cmpxchg-emu.h
+++ b/include/linux/cmpxchg-emu.h
@@ -4,12 +4,14 @@
* lacking direct support for these sizes. These are implemented in terms
* of 4-byte cmpxchg operations.
*
- * Copyright (C) 2024 Paul E. McKenney.
+ * Copyright (C) 2024 Paul E. McKenney <paulmck@kernel.org>
+ * Copyright (C) 2026 Bradley Morgan <brads@mainlining.org>
*/
#ifndef __LINUX_CMPXCHG_EMU_H
#define __LINUX_CMPXCHG_EMU_H
uintptr_t cmpxchg_emu_u8(volatile u8 *p, uintptr_t old, uintptr_t new);
+unsigned long cmpxchg_emu_u16(volatile u16 *p, unsigned long old, unsigned long new);
#endif /* __LINUX_CMPXCHG_EMU_H */
diff --git a/lib/Makefile b/lib/Makefile
index 43421c39d21b..d0e69312176a 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -202,7 +202,7 @@ obj-$(CONFIG_CODE_TAGGING) += codetag.o
lib-$(CONFIG_GENERIC_BUG) += bug.o
obj-$(CONFIG_HAVE_ARCH_TRACEHOOK) += syscall.o
-obj-$(CONFIG_ARCH_NEED_CMPXCHG_1_EMU) += cmpxchg-emu.o
+obj-$(CONFIG_ARCH_NEED_CMPXCHG_1_2_EMU) += cmpxchg-emu.o
obj-$(CONFIG_DYNAMIC_DEBUG_CORE) += dynamic_debug.o
#ensure exported functions have prototypes
diff --git a/lib/cmpxchg-emu.c b/lib/cmpxchg-emu.c
index 27f6f97cb60d..ac1c84383887 100644
--- a/lib/cmpxchg-emu.c
+++ b/lib/cmpxchg-emu.c
@@ -1,10 +1,11 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * Emulated 1-byte cmpxchg operation for architectures lacking direct
- * support for this size. This is implemented in terms of 4-byte cmpxchg
- * operations.
+ * Emulated 1-byte and 2-byte cmpxchg operations for architectures lacking
+ * direct support for these sizes. These are implemented in terms of
+ * 4-byte cmpxchg operations.
*
- * Copyright (C) 2024 Paul E. McKenney.
+ * Copyright (C) 2024 Paul E. McKenney <paulmck@kernel.org>
+ * Copyright (C) 2026 Bradley Morgan <brads@mainlining.org>
*/
#include <linux/types.h>
@@ -40,6 +41,35 @@ uintptr_t cmpxchg_emu_u8(volatile u8 *p, uintptr_t old, uintptr_t new)
instrument_atomic_read_write(p, 1);
ret = data_race(cmpxchg(p32, old32.w, new32.w)); // Overridden above.
} while (ret != old32.w);
- return old;
+ return (u16)old;
}
EXPORT_SYMBOL_GPL(cmpxchg_emu_u8);
+
+union u16_32 {
+ u16 h[2];
+ u32 w;
+};
+
+/* Emulate two-byte cmpxchg() in terms of 4-byte cmpxchg. */
+unsigned long cmpxchg_emu_u16(volatile u16 *p, unsigned long old, unsigned long new)
+{
+ u32 *p32 = (u32 *)(((uintptr_t)p) & ~0x3);
+ int i = (((uintptr_t)p) & 0x2) / 2;
+ union u16_32 old32;
+ union u16_32 new32;
+ u32 ret;
+
+ WARN_ON_ONCE(((uintptr_t)p) & 0x1);
+ ret = READ_ONCE(*p32);
+ do {
+ old32.w = ret;
+ if (old32.h[i] != (u16)old)
+ return (unsigned long)old32.h[i];
+ new32.w = old32.w;
+ new32.h[i] = (u16)new;
+ instrument_atomic_read_write(p, 2);
+ ret = data_race(cmpxchg(p32, old32.w, new32.w)); // Overridden above.
+ } while (ret != old32.w);
+ return (u16)old;
+}
+EXPORT_SYMBOL_GPL(cmpxchg_emu_u16);
--
2.47.3
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v3 2/5] ARC: Emulate two-byte cmpxchg
2026-09-17 16:38 [PATCH v3 0/5] Add two-byte cmpxchg emulation and wire it into the architectures Bradley Morgan
2026-09-17 16:38 ` [PATCH v3 1/5] lib: Add two-byte cmpxchg emulation function Bradley Morgan
@ 2026-09-17 16:38 ` Bradley Morgan
2026-09-17 16:38 ` [PATCH v3 3/5] csky: " Bradley Morgan
` (3 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Bradley Morgan @ 2026-09-17 16:38 UTC (permalink / raw)
To: Andrew Morton, Vineet Gupta, Guo Ren, Yoshinori Sato,
Rich Felker, Chris Zankel, Max Filippov
Cc: Arnd Bergmann, Paul E . McKenney, David Laight,
John Paul Adrian Glaubitz, linux-snps-arc, linux-csky, linux-sh,
linux-kernel, Bradley Morgan
ARC has no two-byte atomic compare and swap, so the __cmpxchg() macro
switch lets case 2 fall through to BUILD_BUG() via default. Route case
2 through the new cmpxchg_emu_u16(), which takes the old and new
values as unsigned long and narrows them itself, so the (uintptr_t)
casts on _o_ and _n_ are no longer needed and the case 2 call passes
them straight.
Signed-off-by: Bradley Morgan <brads@mainlining.org>
---
arch/arc/include/asm/cmpxchg.h | 3 +++
1 file changed, 3 insertions(+)
diff --git a/arch/arc/include/asm/cmpxchg.h b/arch/arc/include/asm/cmpxchg.h
index 76f43db0890f..9b48b8fe0db2 100644
--- a/arch/arc/include/asm/cmpxchg.h
+++ b/arch/arc/include/asm/cmpxchg.h
@@ -50,6 +50,9 @@
case 1: \
_prev_ = (__typeof__(*(ptr)))cmpxchg_emu_u8((volatile u8 *__force)_p_, (uintptr_t)_o_, (uintptr_t)_n_); \
break; \
+ case 2: \
+ _prev_ = (__typeof__(*(ptr)))cmpxchg_emu_u16((volatile u16 *__force)_p_, (uintptr_t)_o_, (uintptr_t)_n_); \
+ break; \
case 4: \
_prev_ = __cmpxchg(_p_, _o_, _n_); \
break; \
--
2.47.3
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v3 3/5] csky: Emulate two-byte cmpxchg
2026-09-17 16:38 [PATCH v3 0/5] Add two-byte cmpxchg emulation and wire it into the architectures Bradley Morgan
2026-09-17 16:38 ` [PATCH v3 1/5] lib: Add two-byte cmpxchg emulation function Bradley Morgan
2026-09-17 16:38 ` [PATCH v3 2/5] ARC: Emulate two-byte cmpxchg Bradley Morgan
@ 2026-09-17 16:38 ` Bradley Morgan
2026-09-17 16:38 ` [PATCH v3 4/5] sh: " Bradley Morgan
` (2 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Bradley Morgan @ 2026-09-17 16:38 UTC (permalink / raw)
To: Andrew Morton, Vineet Gupta, Guo Ren, Yoshinori Sato,
Rich Felker, Chris Zankel, Max Filippov
Cc: Arnd Bergmann, Paul E . McKenney, David Laight,
John Paul Adrian Glaubitz, linux-snps-arc, linux-csky, linux-sh,
linux-kernel, Bradley Morgan
CSKY has no two-byte atomic compare and swap, so the __cmpxchg()
switches in cmpxchg.h let case 2 fall through to the undefined
__cmpxchg_called_with_bad_pointer(), failing at link time. Route case
2 through the new cmpxchg_emu_u16(), which takes the old and new
values as unsigned long and narrows them itself, so the (uintptr_t)
casts on __old and __new come off in all three switch instances.
The (u16) casts are gone for the same reason.
The __old and __new declarations were typed __typeof__(old) and
__typeof__(new), which skips the pointer-integer type check, so
cmpxchg(&p, 4, 5) compiled silently. Typing them through
(unsigned long)(0 ? *(ptr) : (old)) keeps the value conversion while
making the compiler reject mismatched types, the idiom David
Laight suggested. The same check is added for new.
Signed-off-by: Bradley Morgan <brads@mainlining.org>
---
arch/csky/include/asm/cmpxchg.h | 33 +++++++++++++++++++++------------
1 file changed, 21 insertions(+), 12 deletions(-)
diff --git a/arch/csky/include/asm/cmpxchg.h b/arch/csky/include/asm/cmpxchg.h
index db6dda47184e..29dc56e4b7f1 100644
--- a/arch/csky/include/asm/cmpxchg.h
+++ b/arch/csky/include/asm/cmpxchg.h
@@ -57,13 +57,16 @@
#define __cmpxchg_relaxed(ptr, old, new, size) \
({ \
__typeof__(ptr) __ptr = (ptr); \
- __typeof__(new) __new = (new); \
- __typeof__(new) __tmp; \
- __typeof__(old) __old = (old); \
+ unsigned long __old = (unsigned long)(0 ? *(ptr) : (old)); \
+ unsigned long __new = (unsigned long)(0 ? *(ptr) : (new)); \
+ unsigned long __tmp; \
__typeof__(*(ptr)) __ret; \
switch (size) { \
case 1: \
- __ret = (__typeof__(*(ptr)))cmpxchg_emu_u8((volatile u8 *)__ptr, (uintptr_t)__old, (uintptr_t)__new); \
+ __ret = (__typeof__(*(ptr)))cmpxchg_emu_u8((volatile u8 *)__ptr, __old, __new); \
+ break; \
+ case 2: \
+ __ret = (__typeof__(*(ptr)))cmpxchg_emu_u16((volatile u16 *)__ptr, __old, __new); \
break; \
case 4: \
asm volatile ( \
@@ -90,13 +93,16 @@
#define __cmpxchg_acquire(ptr, old, new, size) \
({ \
__typeof__(ptr) __ptr = (ptr); \
- __typeof__(new) __new = (new); \
- __typeof__(new) __tmp; \
- __typeof__(old) __old = (old); \
+ unsigned long __old = (unsigned long)(0 ? *(ptr) : (old)); \
+ unsigned long __new = (unsigned long)(0 ? *(ptr) : (new)); \
+ unsigned long __tmp; \
__typeof__(*(ptr)) __ret; \
switch (size) { \
case 1: \
- __ret = (__typeof__(*(ptr)))cmpxchg_emu_u8((volatile u8 *)__ptr, (uintptr_t)__old, (uintptr_t)__new); \
+ __ret = (__typeof__(*(ptr)))cmpxchg_emu_u8((volatile u8 *)__ptr, __old, __new); \
+ break; \
+ case 2: \
+ __ret = (__typeof__(*(ptr)))cmpxchg_emu_u16((volatile u16 *)__ptr, __old, __new); \
break; \
case 4: \
asm volatile ( \
@@ -124,13 +130,16 @@
#define __cmpxchg(ptr, old, new, size) \
({ \
__typeof__(ptr) __ptr = (ptr); \
- __typeof__(new) __new = (new); \
- __typeof__(new) __tmp; \
- __typeof__(old) __old = (old); \
+ unsigned long __old = (unsigned long)(0 ? *(ptr) : (old)); \
+ unsigned long __new = (unsigned long)(0 ? *(ptr) : (new)); \
+ unsigned long __tmp; \
__typeof__(*(ptr)) __ret; \
switch (size) { \
case 1: \
- __ret = (__typeof__(*(ptr)))cmpxchg_emu_u8((volatile u8 *)__ptr, (uintptr_t)__old, (uintptr_t)__new); \
+ __ret = (__typeof__(*(ptr)))cmpxchg_emu_u8((volatile u8 *)__ptr, __old, __new); \
+ break; \
+ case 2: \
+ __ret = (__typeof__(*(ptr)))cmpxchg_emu_u16((volatile u16 *)__ptr, __old, __new); \
break; \
case 4: \
asm volatile ( \
--
2.47.3
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v3 4/5] sh: Emulate two-byte cmpxchg
2026-09-17 16:38 [PATCH v3 0/5] Add two-byte cmpxchg emulation and wire it into the architectures Bradley Morgan
` (2 preceding siblings ...)
2026-09-17 16:38 ` [PATCH v3 3/5] csky: " Bradley Morgan
@ 2026-09-17 16:38 ` Bradley Morgan
2026-09-17 16:38 ` [PATCH v3 5/5] xtensa: " Bradley Morgan
2026-09-18 9:10 ` [PATCH v3 0/5] Add two-byte cmpxchg emulation and wire it into the architectures David Laight
5 siblings, 0 replies; 9+ messages in thread
From: Bradley Morgan @ 2026-09-17 16:38 UTC (permalink / raw)
To: Andrew Morton, Vineet Gupta, Guo Ren, Yoshinori Sato,
Rich Felker, Chris Zankel, Max Filippov
Cc: Arnd Bergmann, Paul E . McKenney, David Laight,
John Paul Adrian Glaubitz, linux-snps-arc, linux-csky, linux-sh,
linux-kernel, Bradley Morgan
SH has no byte or halfword atomic memory operations, so the __cmpxchg()
switch routes case 1 through cmpxchg_emu_u8() and lets case 2 fall
through to __cmpxchg_called_with_bad_pointer(), which is declared but
never defined, so a two-byte cmpxchg() fails at link time. Route case
2 through the new cmpxchg_emu_u16(), which takes the old and new
values as unsigned long, so the (unsigned long) casts move off the
call and into _old_ and _new_ declarations that type check the old
and new arguments against *ptr through (unsigned long)(0 ? *ptr : _o_),
the idiom David Laight suggested, so cmpxchg(&p, 4, 5) no longer
compiles silently.
Acked-by: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
Signed-off-by: Bradley Morgan <brads@mainlining.org>
---
arch/sh/include/asm/cmpxchg.h | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/arch/sh/include/asm/cmpxchg.h b/arch/sh/include/asm/cmpxchg.h
index 1e5dc5ccf7bf..b87f59107b4e 100644
--- a/arch/sh/include/asm/cmpxchg.h
+++ b/arch/sh/include/asm/cmpxchg.h
@@ -59,6 +59,8 @@ static inline unsigned long __cmpxchg(volatile void * ptr, unsigned long old,
switch (size) {
case 1:
return cmpxchg_emu_u8(ptr, old, new);
+ case 2:
+ return cmpxchg_emu_u16(ptr, old, new);
case 4:
return __cmpxchg_u32(ptr, old, new);
}
@@ -70,8 +72,10 @@ static inline unsigned long __cmpxchg(volatile void * ptr, unsigned long old,
({ \
__typeof__(*(ptr)) _o_ = (o); \
__typeof__(*(ptr)) _n_ = (n); \
- (__typeof__(*(ptr))) __cmpxchg((ptr), (unsigned long)_o_, \
- (unsigned long)_n_, sizeof(*(ptr))); \
+ unsigned long _old_ = (unsigned long)(0 ? *ptr : _o_); \
+ unsigned long _new_ = (unsigned long)(0 ? *ptr : _n_); \
+ (__typeof__(*(ptr))) __cmpxchg((ptr), _old_, \
+ _new_, sizeof(*(ptr))); \
})
#include <asm-generic/cmpxchg-local.h>
--
2.47.3
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v3 5/5] xtensa: Emulate two-byte cmpxchg
2026-09-17 16:38 [PATCH v3 0/5] Add two-byte cmpxchg emulation and wire it into the architectures Bradley Morgan
` (3 preceding siblings ...)
2026-09-17 16:38 ` [PATCH v3 4/5] sh: " Bradley Morgan
@ 2026-09-17 16:38 ` Bradley Morgan
2026-09-17 16:42 ` Bradley Morgan
2026-09-18 9:10 ` [PATCH v3 0/5] Add two-byte cmpxchg emulation and wire it into the architectures David Laight
5 siblings, 1 reply; 9+ messages in thread
From: Bradley Morgan @ 2026-09-17 16:38 UTC (permalink / raw)
To: Andrew Morton, Vineet Gupta, Guo Ren, Yoshinori Sato,
Rich Felker, Chris Zankel, Max Filippov
Cc: Arnd Bergmann, Paul E . McKenney, David Laight,
John Paul Adrian Glaubitz, linux-snps-arc, linux-csky, linux-sh,
linux-kernel, Bradley Morgan
Xtensa has no two-byte atomic compare and swap, so the __cmpxchg()
switch lets case 2 fall through to __cmpxchg_called_with_bad_pointer(),
which is declared but never defined, so a two-byte cmpxchg() fails at
link time. Route case 2 through the new cmpxchg_emu_u16(), which takes
the old and new values as unsigned long, so the (unsigned long) casts
on _o_ and _n_ at the call move into the function and the macro's
__typeof__(*(ptr)) typing of _o_ and _n_ remains the pointer-integer
type check.
Signed-off-by: Bradley Morgan <brads@mainlining.org>
---
arch/xtensa/include/asm/cmpxchg.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/xtensa/include/asm/cmpxchg.h b/arch/xtensa/include/asm/cmpxchg.h
index b6db4838b175..8dea8e357fc0 100644
--- a/arch/xtensa/include/asm/cmpxchg.h
+++ b/arch/xtensa/include/asm/cmpxchg.h
@@ -76,6 +76,7 @@ __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size)
{
switch (size) {
case 1: return cmpxchg_emu_u8(ptr, old, new);
+ case 2: return cmpxchg_emu_u16(ptr, old, new);
case 4: return __cmpxchg_u32(ptr, old, new);
default: __cmpxchg_called_with_bad_pointer();
return old;
--
2.47.3
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v3 5/5] xtensa: Emulate two-byte cmpxchg
2026-09-17 16:38 ` [PATCH v3 5/5] xtensa: " Bradley Morgan
@ 2026-09-17 16:42 ` Bradley Morgan
0 siblings, 0 replies; 9+ messages in thread
From: Bradley Morgan @ 2026-09-17 16:42 UTC (permalink / raw)
To: Andrew Morton, Vineet Gupta, Guo Ren, Yoshinori Sato,
Rich Felker, Chris Zankel, Max Filippov
Cc: Arnd Bergmann, Paul E . McKenney, David Laight,
John Paul Adrian Glaubitz, linux-snps-arc, linux-csky, linux-sh,
linux-kernel
On 17 September 2026 17:38:30 BST, Bradley Morgan <brads@mainlining.org>
wrote:
>Xtensa has no two-byte atomic compare and swap, so the __cmpxchg()
>switch lets case 2 fall through to __cmpxchg_called_with_bad_pointer(),
>which is declared but never defined, so a two-byte cmpxchg() fails at
>link time. Route case 2 through the new cmpxchg_emu_u16(), which takes
>the old and new values as unsigned long, so the (unsigned long) casts
>on _o_ and _n_ at the call move into the function and the macro's
>__typeof__(*(ptr)) typing of _o_ and _n_ remains the pointer-integer
>type check.
>
>Signed-off-by: Bradley Morgan <brads@mainlining.org>
>---
> arch/xtensa/include/asm/cmpxchg.h | 1 +
> 1 file changed, 1 insertion(+)
>
>diff --git a/arch/xtensa/include/asm/cmpxchg.h b/arch/xtensa/include/asm/cmpxchg.h
>index b6db4838b175..8dea8e357fc0 100644
>--- a/arch/xtensa/include/asm/cmpxchg.h
>+++ b/arch/xtensa/include/asm/cmpxchg.h
>@@ -76,6 +76,7 @@ __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size)
> {
> switch (size) {
> case 1: return cmpxchg_emu_u8(ptr, old, new);
>+ case 2: return cmpxchg_emu_u16(ptr, old, new);
> case 4: return __cmpxchg_u32(ptr, old, new);
> default: __cmpxchg_called_with_bad_pointer();
> return old;
>
Oops! Forgot to add R-B tag! Merger, could you add this when it's ready to
merge?
--- Thanks!
https://lore.kernel.org/all/EE579805-42F2-4C58-B752-F28779EEB717@grrlz.net/
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v3 1/5] lib: Add two-byte cmpxchg emulation function
2026-09-17 16:38 ` [PATCH v3 1/5] lib: Add two-byte cmpxchg emulation function Bradley Morgan
@ 2026-09-18 8:59 ` David Laight
0 siblings, 0 replies; 9+ messages in thread
From: David Laight @ 2026-09-18 8:59 UTC (permalink / raw)
To: Bradley Morgan
Cc: Andrew Morton, Vineet Gupta, Guo Ren, Yoshinori Sato,
Rich Felker, Chris Zankel, Max Filippov, Arnd Bergmann,
Paul E . McKenney, John Paul Adrian Glaubitz, linux-snps-arc,
linux-csky, linux-sh, linux-kernel
On Thu, 17 Sep 2026 16:38:26 +0000
Bradley Morgan <brads@mainlining.org> wrote:
> cmpxchg_emu_u8() emulates one-byte cmpxchg() in terms of four-byte
> cmpxchg() for the architectures lacking native one-byte atomics.
> The same architectures also lack native two-byte cmpxchg(), where
> such an operation is not supported and either fails to compile via
> BUILD_BUG() or fails to link, because the bad pointer sentinels
> these architectures declare are never defined.
>
> Add cmpxchg_emu_u16(), the two-byte sibling. It reads the enclosing
> word with READ_ONCE(), splices the two target bytes through a union
> and loops on cmpxchg() of the full word until the compare succeeds.
> Like cmpxchg_emu_u8() it is fully ordered.
>
> Unlike cmpxchg_emu_u8() it casts the old and new values to u16
> internally and returns unsigned long, taking the old and new values
> as unsigned long, per the suggestion from David Laight. The switch
> statements in the architecture macros instantiate every size case,
> so a cmpxchg() on a pointer type checks the two-byte case as well,
> and a u16 parameter or return would make the macro casts and return
> conversions warn there. With unsigned long parameters and return the
> call sites need no narrowing casts, pointer exchanges compile warning
> free, and the function still compares and returns exactly the 16 bits
> the caller asked for, which matches the hardware cmpxchg r16
> behaviour where a 16-bit compare only looks at the low 16 bits of
> the register.
>
> The Kconfig symbol gating this file is renamed from
> ARCH_NEED_CMPXCHG_1_EMU to ARCH_NEED_CMPXCHG_1_2_EMU, as it now
> selects both the one-byte and the two-byte emulation.
>
> Suggested-by: Paul E. McKenney <paulmck@kernel.org>
> Suggested-by: David Laight <david.laight.linux@gmail.com>
> Signed-off-by: Bradley Morgan <brads@mainlining.org>
> ---
...
> diff --git a/lib/cmpxchg-emu.c b/lib/cmpxchg-emu.c
> index 27f6f97cb60d..ac1c84383887 100644
> --- a/lib/cmpxchg-emu.c
> +++ b/lib/cmpxchg-emu.c
> @@ -1,10 +1,11 @@
> // SPDX-License-Identifier: GPL-2.0+
> /*
> - * Emulated 1-byte cmpxchg operation for architectures lacking direct
> - * support for this size. This is implemented in terms of 4-byte cmpxchg
> - * operations.
> + * Emulated 1-byte and 2-byte cmpxchg operations for architectures lacking
> + * direct support for these sizes. These are implemented in terms of
> + * 4-byte cmpxchg operations.
> *
> - * Copyright (C) 2024 Paul E. McKenney.
> + * Copyright (C) 2024 Paul E. McKenney <paulmck@kernel.org>
> + * Copyright (C) 2026 Bradley Morgan <brads@mainlining.org>
> */
>
> #include <linux/types.h>
> @@ -40,6 +41,35 @@ uintptr_t cmpxchg_emu_u8(volatile u8 *p, uintptr_t old, uintptr_t new)
> instrument_atomic_read_write(p, 1);
> ret = data_race(cmpxchg(p32, old32.w, new32.w)); // Overridden above.
> } while (ret != old32.w);
> - return old;
> + return (u16)old;
> }
> EXPORT_SYMBOL_GPL(cmpxchg_emu_u8);
Where did the (u16) cast come from?
If you want to mask 'old' to 8 bits it would be more reasonable to change
the function prototype.
(Although I can never remember whether the caller or called code is
responsible for masking the value.)
David
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v3 0/5] Add two-byte cmpxchg emulation and wire it into the architectures
2026-09-17 16:38 [PATCH v3 0/5] Add two-byte cmpxchg emulation and wire it into the architectures Bradley Morgan
` (4 preceding siblings ...)
2026-09-17 16:38 ` [PATCH v3 5/5] xtensa: " Bradley Morgan
@ 2026-09-18 9:10 ` David Laight
5 siblings, 0 replies; 9+ messages in thread
From: David Laight @ 2026-09-18 9:10 UTC (permalink / raw)
To: Bradley Morgan
Cc: Andrew Morton, Vineet Gupta, Guo Ren, Yoshinori Sato,
Rich Felker, Chris Zankel, Max Filippov, Arnd Bergmann,
Paul E . McKenney, John Paul Adrian Glaubitz, linux-snps-arc,
linux-csky, linux-sh, linux-kernel
On Thu, 17 Sep 2026 16:38:25 +0000
Bradley Morgan <brads@mainlining.org> wrote:
> This is v3 of the two byte cmpxchg emulation series, wiring
> cmpxchg_emu_u16() into arc, csky, sh and xtensa.
>
> v2 tried u16 as the parameter type. David Laight pointed out that
> this does not compile warning free when exchanging a pointer type,
> because the switch statements in the architecture macros instantiate
> every size case, so a pointer cmpxchg() type checks the two byte
> case, and the (u16) casts there warn. v3 takes the old and new
> values as unsigned long and casts to u16 inside the function, so the
> call sites need no narrowing casts and pointer exchanges compile
> clean. The function still compares and returns exactly the 16 bits
> of the emulated halfword, which matches hardware cmpxchg r16
> behaviour, and a host test of 972 cases across both halfword offsets
> against a byte level reference model still passes.
>
> David also noted the missing pointer to integer type check in some
> of the macros, so cmpxchg(&p, 4, 5) compiled silently. The csky
> macros typed __old and __new through __typeof__(old), which skips
> the check, and sh had none either. Both now type check through
> (unsigned long)(0 ? *(ptr) : (old)), the idiom David suggested,
> which keeps the pointer to integer conversion explicit while making
> the compiler reject mismatched types. Both of his test cases now
> fail to compile on every macro shape.
You fixed csky but not the others.
I guess that could be a separate patch (maybe series).
David
>
> The ARMv6 wiring stays dropped from v1, per Arnd Bergmann's offer to
> take the INTEGRATOR_CM1136JFS cleanup in his platform removal series.
>
> Thank you, Paul McKenney for suggesting this, and David Laight and
> Arnd Bergmann for the review that shaped all three revisions.
>
> Bradley Morgan (5):
> lib: Add two-byte cmpxchg emulation function
> ARC: Emulate two-byte cmpxchg
> csky: Emulate two-byte cmpxchg
> sh: Emulate two-byte cmpxchg
> xtensa: Emulate two-byte cmpxchg
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-09-18 9:10 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-17 16:38 [PATCH v3 0/5] Add two-byte cmpxchg emulation and wire it into the architectures Bradley Morgan
2026-09-17 16:38 ` [PATCH v3 1/5] lib: Add two-byte cmpxchg emulation function Bradley Morgan
2026-09-18 8:59 ` David Laight
2026-09-17 16:38 ` [PATCH v3 2/5] ARC: Emulate two-byte cmpxchg Bradley Morgan
2026-09-17 16:38 ` [PATCH v3 3/5] csky: " Bradley Morgan
2026-09-17 16:38 ` [PATCH v3 4/5] sh: " Bradley Morgan
2026-09-17 16:38 ` [PATCH v3 5/5] xtensa: " Bradley Morgan
2026-09-17 16:42 ` Bradley Morgan
2026-09-18 9:10 ` [PATCH v3 0/5] Add two-byte cmpxchg emulation and wire it into the architectures David Laight
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®