[PATCH v10 8/9] riscv: implement preemptible this_cpu cmpxchg128
Yunhui Cui
cuiyunhui at bytedance.com
Thu Nov 12 02:41:48 PST 2026
Extend the preemptible compare-and-exchange support to 128-bit values on
systems with native AMOCAS.Q support.
AMOCAS.Q implicitly consumes register pairs, so keep both halves of the new
value in fixed registers even when either half is zero. Retain the
local-IRQ-protected fallback when native cmpxchg128 is unavailable.
Signed-off-by: Yunhui Cui <cuiyunhui at bytedance.com>
---
arch/riscv/include/asm/percpu.h | 66 +++++++++++++++++++++++++++++++++
1 file changed, 66 insertions(+)
diff --git a/arch/riscv/include/asm/percpu.h b/arch/riscv/include/asm/percpu.h
index 4d2fc10cbdab1..1d35fc89eeeb2 100644
--- a/arch/riscv/include/asm/percpu.h
+++ b/arch/riscv/include/asm/percpu.h
@@ -9,10 +9,12 @@
#include <linux/thread_info.h>
#include <asm/asm.h>
+#include <asm/cmpxchg.h>
#include <asm/cpufeature-macros.h>
#include <asm/current.h>
#include <asm/gpr-num.h>
#include <asm/hwcap.h>
+#include <asm/irqflags.h>
#define __my_cpu_offset (((struct thread_info *)current)->pcpu_offset)
@@ -518,6 +520,65 @@ __percpu_cmpxchg_case_##sz(void __percpu *pcp, unsigned long old, \
__PERCPU_8_16_CMPXCHG_CASE(8, .b)
__PERCPU_8_16_CMPXCHG_CASE(16, .h)
+
+#ifdef system_has_cmpxchg128
+static __always_inline u128
+__percpu_cmpxchg128_case(void __percpu *pcp, u128 old, u128 new)
+{
+ __PCPU_OP_CONTEXT();
+ union __u128_halves old_halves = { .full = old };
+ union __u128_halves new_halves = { .full = new };
+
+ register unsigned long new_lo asm("t1") = new_halves.low;
+ register unsigned long new_hi asm("t2") = new_halves.high;
+ register unsigned long old_lo asm("t3") = old_halves.low;
+ register unsigned long old_hi asm("t4") = old_halves.high;
+
+ asm volatile (__PCPU_GPRS_BEGIN
+ ".option push\n"
+ ".option arch, +zacas\n"
+ "amocas.q %[old_lo], %[new_lo], 0(%[addr])\n"
+ ".option pop\n"
+ __PCPU_GPRS_END
+ : [gprs] "=m" (__ti->pcpu_gprs),
+ [off] "=&r" (__off), [addr] "=&r" (__addr),
+ [old_lo] "+&r" (old_lo), [old_hi] "+&r" (old_hi)
+ : [pcpu_offset] "m" (__ti->pcpu_offset),
+ [pcp] "r" (pcp), [new_lo] "r" (new_lo),
+ [new_hi] "r" (new_hi)
+ : "memory");
+
+ return ((u128)old_hi << 64) | old_lo;
+}
+
+static __always_inline u128
+__percpu_generic_cmpxchg128(void __percpu *pcp, u128 old, u128 new)
+{
+ unsigned long flags;
+ u128 *ptr;
+ u128 ret;
+
+ flags = arch_local_irq_save();
+ ptr = (u128 *)((__force unsigned long)pcp +
+ __my_cpu_offset);
+ ret = *ptr;
+ if (ret == old)
+ *ptr = new;
+ arch_local_irq_restore(flags);
+
+ return ret;
+}
+
+static __always_inline u128
+__percpu_cmpxchg128(void __percpu *pcp, u128 old, u128 new)
+{
+ if (system_has_cmpxchg128())
+ return __percpu_cmpxchg128_case(pcp, old, new);
+
+ return __percpu_generic_cmpxchg128(pcp, old, new);
+}
+#endif
+
#define _pcp_wrap(op, pcp, ...) \
({ \
op(&(pcp), __VA_ARGS__); \
@@ -618,6 +679,11 @@ __PERCPU_8_16_CMPXCHG_CASE(16, .h)
#define this_cpu_cmpxchg64(pcp, o, n) this_cpu_cmpxchg_8(pcp, o, n)
#endif
+#ifdef system_has_cmpxchg128
+#define this_cpu_cmpxchg128(pcp, o, n) \
+ _pcp_wrap_return(__percpu_cmpxchg128, pcp, (u128)(o), (u128)(n))
+#endif
+
#undef PERCPU_RW_OPS
#undef __PERCPU_AMO_OP_CASE
#undef __PERCPU_8_16_OP_CASE
--
2.39.5
More information about the linux-riscv
mailing list