* [PATCH v9 0/3] riscv: optimize percpu access
@ 2026-11-08 11:04 Yunhui Cui
2026-11-08 11:04 ` [PATCH v9 1/3] riscv: implement this_cpu operations Yunhui Cui
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Yunhui Cui @ 2026-11-08 11:04 UTC (permalink / raw)
To: pjw, palmer, aou, alex, dennis, tj, cl, ast, daniel, andrii,
martin.lau, eddyz87, memxor, song, yonghong.song, jolsa, bjorn,
pulehui, puranjay, thuth, ajones, ben.dooks, rkrcmar,
samuel.holland, zong.li, conor.dooley, tglx, debug,
seanwascoding, andybnac, menglong8.dong, cyrilbur, wangruikang,
atishp, apatel, linux-riscv, linux-kernel, linux-mm, bpf, arnd,
nathan, nick.desaulniers+lkml, morbo, justinstitt, qingfang.deng,
linux-arch, llvm, dave, emil, ihor.solodrai, broonie, cuiyunhui
RISC-V has no asm/percpu.h, so this_cpu_*() read-modify-write
operations use generic fallbacks that disable local interrupts. Per-CPU
addressing also derives the current offset from the CPU number and
__per_cpu_offset[].
Implement RISC-V-specific this_cpu operations using relaxed AMOs with
preemption disabled. Native-width read-modify-write operations use AMOs,
while 8-bit and 16-bit read-modify-write operations use Zabha when
available or a local 32-bit LR/SC sequence otherwise. Cache the current
per-CPU offset in thread_info, keep it valid across CPU bringup, hotplug
and context switches, and select HAVE_CMPXCHG_LOCAL so vmstat and percpu
counters can use the this_cpu_try_cmpxchg() fast path.
On a SiFive P550, Davidlohr Bueso measured about 15-20% lower
this_cpu_inc() cost in an ad-hoc microbenchmark.
Changes since v8:
- Drop the PIO helper patch, which has already been merged.
- Rename the first patch and update its changelog as suggested by
Davidlohr Bueso.
- Use value-returning AMOs instead of rd=x0 for CPU-local operations.
- Remove unnecessary memory clobbers from the new relaxed AMO and LR/SC
code.
- Use .option arch,+zabha for the Zabha inline asm, matching the
existing RISC-V cmpxchg implementation.
- Isolate arch_cmpxchg_relaxed() behind typed always-inline helpers to
avoid macro variable capture while preserving atomic instrumentation.
- Make the cmpxchg128 fallback use the already resolved per-CPU pointer,
avoiding the same macro variable capture.
- Copy the cached per-CPU offset from prev to next during context switch,
as suggested by Thomas Gleixner.
- Initialize secondary idle tasks in the common startup path, covering
parallel bringup and CPU hotplug, and remove the duplicate initialization
from smp_callin(), as reported by Sashiko.
- Select HAVE_CMPXCHG_LOCAL in a separate patch.
Yunhui Cui (3):
riscv: implement this_cpu operations
riscv: store percpu offset into thread_info
riscv: select HAVE_CMPXCHG_LOCAL
arch/riscv/Kconfig | 1 +
arch/riscv/include/asm/asm.h | 6 +-
arch/riscv/include/asm/percpu.h | 574 +++++++++++++++++++++++++++
arch/riscv/include/asm/switch_to.h | 9 +
arch/riscv/include/asm/thread_info.h | 3 +-
arch/riscv/kernel/asm-offsets.c | 2 +-
arch/riscv/kernel/smpboot.c | 10 +-
arch/riscv/net/bpf_jit_comp64.c | 9 +-
8 files changed, 598 insertions(+), 16 deletions(-)
create mode 100644 arch/riscv/include/asm/percpu.h
base-commit: ddfe51b0a0f5dc9e415cc2c8ad8db17532dd426b
--
2.39.5
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v9 1/3] riscv: implement this_cpu operations
2026-11-08 11:04 [PATCH v9 0/3] riscv: optimize percpu access Yunhui Cui
@ 2026-11-08 11:04 ` Yunhui Cui
2026-11-08 11:58 ` bot+bpf-ci
2026-11-08 11:04 ` [PATCH v9 2/3] riscv: store percpu offset into thread_info Yunhui Cui
2026-11-08 11:04 ` [PATCH v9 3/3] riscv: select HAVE_CMPXCHG_LOCAL Yunhui Cui
2 siblings, 1 reply; 5+ messages in thread
From: Yunhui Cui @ 2026-11-08 11:04 UTC (permalink / raw)
To: pjw, palmer, aou, alex, dennis, tj, cl, ast, daniel, andrii,
martin.lau, eddyz87, memxor, song, yonghong.song, jolsa, bjorn,
pulehui, puranjay, thuth, ajones, ben.dooks, rkrcmar,
samuel.holland, zong.li, conor.dooley, tglx, debug,
seanwascoding, andybnac, menglong8.dong, cyrilbur, wangruikang,
atishp, apatel, linux-riscv, linux-kernel, linux-mm, bpf, arnd,
nathan, nick.desaulniers+lkml, morbo, justinstitt, qingfang.deng,
linux-arch, llvm, dave, emil, ihor.solodrai, broonie, cuiyunhui
RISC-V has no asm/percpu.h, so this_cpu_*() read-modify-write
operations use generic implementations that disable local interrupts.
Implement RISC-V-specific this_cpu operations with preemption disabled
to keep accesses on the current CPU. Native-width read-modify-write
operations use relaxed AMOs, while 8-bit and 16-bit read-modify-write
operations use Zabha when available or a local 32-bit LR/SC sequence
otherwise.
Link: https://lore.kernel.org/r/20260826010748.zphu3wh2hlm4t4o2@offworld
Tested-by: Davidlohr Bueso <dave@stgolabs.net>
Signed-off-by: Yunhui Cui <cuiyunhui@bytedance.com>
---
arch/riscv/include/asm/percpu.h | 569 ++++++++++++++++++++++++++++++++
1 file changed, 569 insertions(+)
create mode 100644 arch/riscv/include/asm/percpu.h
diff --git a/arch/riscv/include/asm/percpu.h b/arch/riscv/include/asm/percpu.h
new file mode 100644
index 0000000000000..280b122b9fe69
--- /dev/null
+++ b/arch/riscv/include/asm/percpu.h
@@ -0,0 +1,569 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef __ASM_PERCPU_H
+#define __ASM_PERCPU_H
+
+#include <linux/bits.h>
+#include <linux/preempt.h>
+
+#include <asm/alternative-macros.h>
+#include <asm/cmpxchg.h>
+#include <asm/cpufeature-macros.h>
+#include <asm/hwcap.h>
+#include <asm/irqflags.h>
+
+#define PERCPU_RW_OPS(sz) \
+static inline unsigned long __percpu_read_##sz(void *ptr) \
+{ \
+ return READ_ONCE(*(u##sz *)ptr); \
+} \
+ \
+static inline void __percpu_write_##sz(void *ptr, unsigned long val) \
+{ \
+ WRITE_ONCE(*(u##sz *)ptr, (u##sz)val); \
+}
+
+PERCPU_RW_OPS(8)
+PERCPU_RW_OPS(16)
+PERCPU_RW_OPS(32)
+
+#ifdef CONFIG_64BIT
+PERCPU_RW_OPS(64)
+#endif
+
+/*
+ * Use a real destination register instead of x0 even when the result is
+ * unused. Implementations may execute rd=x0 AMOs at a memory controller
+ * without returning the old value to the CPU. Value-returning AMOs are
+ * more likely to execute near the CPU, for example in the L1 cache. This
+ * better suits per-CPU data, which is typically accessed repeatedly by
+ * the same CPU.
+ *
+ * https://lore.kernel.org/r/e7d539ed-ced0-4b96-8ecd-048a5b803b85@paulmck-laptop
+ */
+#define __PERCPU_AMO_OP_CASE(sfx, name, sz, amo_insn) \
+static inline void \
+__percpu_##name##_amo_case_##sz(void *ptr, unsigned long val) \
+{ \
+ u##sz tmp; \
+ \
+ asm volatile ( \
+ "amo" #amo_insn #sfx " %[tmp], %[val], %[ptr]" \
+ : [ptr] "+A" (*(u##sz *)ptr), [tmp] "=r" (tmp) \
+ : [val] "r" ((u##sz)(val))); \
+}
+
+__PERCPU_AMO_OP_CASE(.w, add, 32, add)
+__PERCPU_AMO_OP_CASE(.w, and, 32, and)
+__PERCPU_AMO_OP_CASE(.w, or, 32, or)
+
+#ifdef CONFIG_64BIT
+__PERCPU_AMO_OP_CASE(.d, add, 64, add)
+__PERCPU_AMO_OP_CASE(.d, and, 64, and)
+__PERCPU_AMO_OP_CASE(.d, or, 64, or)
+#endif
+
+static inline u32 __percpu_add_return_amo_case_32(void *ptr, unsigned long val)
+{
+ register u32 ret;
+
+ asm volatile ("amoadd.w %[ret], %[val], %[ptr]"
+ : [ptr] "+A" (*(u32 *)ptr), [ret] "=r" (ret)
+ : [val] "r" ((u32)(val)));
+
+ return ret + val;
+}
+
+#ifdef CONFIG_64BIT
+static inline u64 __percpu_add_return_amo_case_64(void *ptr, unsigned long val)
+{
+ register u64 ret;
+
+ asm volatile ("amoadd.d %[ret], %[val], %[ptr]"
+ : [ptr] "+A" (*(u64 *)ptr), [ret] "=r" (ret)
+ : [val] "r" ((u64)(val)));
+
+ return ret + val;
+}
+#endif
+
+#define PERCPU_8_16_GET_SHIFT(ptr) (((unsigned long)(ptr) & 0x3) * BITS_PER_BYTE)
+#define PERCPU_8_16_GET_MASK(sz) GENMASK((sz) - 1, 0)
+#define PERCPU_8_16_GET_PTR32(ptr) ((u32 *)((unsigned long)(ptr) & ~0x3))
+
+static inline void __percpu_add_amo_case_8(void *ptr, unsigned long val)
+{
+ u8 tmp;
+
+ if (IS_ENABLED(CONFIG_RISCV_ISA_ZABHA) &&
+ riscv_has_extension_unlikely(RISCV_ISA_EXT_ZABHA)) {
+ asm volatile (".option push\n"
+ ".option arch, +zabha\n"
+ "amoadd.b %[tmp], %[val], %[ptr]\n"
+ ".option pop"
+ : [ptr] "+A" (*(u8 *)ptr), [tmp] "=r" (tmp)
+ : [val] "r" ((u8)(val & PERCPU_8_16_GET_MASK(8))));
+ } else {
+ u32 *ptr32 = PERCPU_8_16_GET_PTR32(ptr);
+ const unsigned long shift = PERCPU_8_16_GET_SHIFT(ptr);
+ const u32 mask = PERCPU_8_16_GET_MASK(8) << shift;
+ const u8 val_trunc = val & PERCPU_8_16_GET_MASK(8);
+ u32 old, new;
+ u8 field;
+
+ asm volatile ("0: lr.w %0, %2\n"
+ "and %3, %0, %4\n"
+ "srl %3, %3, %5\n"
+ "add %3, %3, %6\n"
+ "and %3, %3, %8\n"
+ "sll %3, %3, %5\n"
+ "and %1, %0, %7\n"
+ "or %1, %1, %3\n"
+ "sc.w %1, %1, %2\n"
+ "bnez %1, 0b\n"
+ : "=&r"(old), "=&r"(new), "+A"(*ptr32), "=&r"(field)
+ : "r"(mask), "r"(shift), "r"(val_trunc), "r"(~mask),
+ "r"(PERCPU_8_16_GET_MASK(8)));
+ }
+}
+
+static inline void __percpu_add_amo_case_16(void *ptr, unsigned long val)
+{
+ u16 tmp;
+
+ if (IS_ENABLED(CONFIG_RISCV_ISA_ZABHA) &&
+ riscv_has_extension_unlikely(RISCV_ISA_EXT_ZABHA)) {
+ asm volatile (".option push\n"
+ ".option arch, +zabha\n"
+ "amoadd.h %[tmp], %[val], %[ptr]\n"
+ ".option pop"
+ : [ptr] "+A" (*(u16 *)ptr), [tmp] "=r" (tmp)
+ : [val] "r" ((u16)(val & PERCPU_8_16_GET_MASK(16))));
+ } else {
+ u32 *ptr32 = PERCPU_8_16_GET_PTR32(ptr);
+ const unsigned long shift = PERCPU_8_16_GET_SHIFT(ptr);
+ const u32 mask = PERCPU_8_16_GET_MASK(16) << shift;
+ const u16 val_trunc = val & PERCPU_8_16_GET_MASK(16);
+ u32 old, new;
+ u16 field;
+
+ asm volatile ("0: lr.w %0, %2\n"
+ "and %3, %0, %4\n"
+ "srl %3, %3, %5\n"
+ "add %3, %3, %6\n"
+ "and %3, %3, %8\n"
+ "sll %3, %3, %5\n"
+ "and %1, %0, %7\n"
+ "or %1, %1, %3\n"
+ "sc.w %1, %1, %2\n"
+ "bnez %1, 0b\n"
+ : "=&r"(old), "=&r"(new), "+A"(*ptr32), "=&r"(field)
+ : "r"(mask), "r"(shift), "r"(val_trunc), "r"(~mask),
+ "r"(PERCPU_8_16_GET_MASK(16)));
+ }
+}
+
+static inline void __percpu_and_amo_case_8(void *ptr, unsigned long val)
+{
+ u8 tmp;
+
+ if (IS_ENABLED(CONFIG_RISCV_ISA_ZABHA) &&
+ riscv_has_extension_unlikely(RISCV_ISA_EXT_ZABHA)) {
+ asm volatile (".option push\n"
+ ".option arch, +zabha\n"
+ "amoand.b %[tmp], %[val], %[ptr]\n"
+ ".option pop"
+ : [ptr] "+A" (*(u8 *)ptr), [tmp] "=r" (tmp)
+ : [val] "r" ((u8)(val & PERCPU_8_16_GET_MASK(8))));
+ } else {
+ u32 *ptr32 = PERCPU_8_16_GET_PTR32(ptr);
+ const unsigned long shift = PERCPU_8_16_GET_SHIFT(ptr);
+ const u32 mask = PERCPU_8_16_GET_MASK(8) << shift;
+ const u8 val_trunc = val & PERCPU_8_16_GET_MASK(8);
+ u32 old, new;
+ u8 field;
+
+ asm volatile ("0: lr.w %0, %2\n"
+ "and %3, %0, %4\n"
+ "srl %3, %3, %5\n"
+ "and %3, %3, %6\n"
+ "and %3, %3, %8\n"
+ "sll %3, %3, %5\n"
+ "and %1, %0, %7\n"
+ "or %1, %1, %3\n"
+ "sc.w %1, %1, %2\n"
+ "bnez %1, 0b\n"
+ : "=&r"(old), "=&r"(new), "+A"(*ptr32), "=&r"(field)
+ : "r"(mask), "r"(shift), "r"(val_trunc), "r"(~mask),
+ "r"(PERCPU_8_16_GET_MASK(8)));
+ }
+}
+
+static inline void __percpu_and_amo_case_16(void *ptr, unsigned long val)
+{
+ u16 tmp;
+
+ if (IS_ENABLED(CONFIG_RISCV_ISA_ZABHA) &&
+ riscv_has_extension_unlikely(RISCV_ISA_EXT_ZABHA)) {
+ asm volatile (".option push\n"
+ ".option arch, +zabha\n"
+ "amoand.h %[tmp], %[val], %[ptr]\n"
+ ".option pop"
+ : [ptr] "+A" (*(u16 *)ptr), [tmp] "=r" (tmp)
+ : [val] "r" ((u16)(val & PERCPU_8_16_GET_MASK(16))));
+ } else {
+ u32 *ptr32 = PERCPU_8_16_GET_PTR32(ptr);
+ const unsigned long shift = PERCPU_8_16_GET_SHIFT(ptr);
+ const u32 mask = PERCPU_8_16_GET_MASK(16) << shift;
+ const u16 val_trunc = val & PERCPU_8_16_GET_MASK(16);
+ u32 old, new;
+ u16 field;
+
+ asm volatile ("0: lr.w %0, %2\n"
+ "and %3, %0, %4\n"
+ "srl %3, %3, %5\n"
+ "and %3, %3, %6\n"
+ "and %3, %3, %8\n"
+ "sll %3, %3, %5\n"
+ "and %1, %0, %7\n"
+ "or %1, %1, %3\n"
+ "sc.w %1, %1, %2\n"
+ "bnez %1, 0b\n"
+ : "=&r"(old), "=&r"(new), "+A"(*ptr32), "=&r"(field)
+ : "r"(mask), "r"(shift), "r"(val_trunc), "r"(~mask),
+ "r"(PERCPU_8_16_GET_MASK(16)));
+ }
+}
+
+static inline void __percpu_or_amo_case_8(void *ptr, unsigned long val)
+{
+ u8 tmp;
+
+ if (IS_ENABLED(CONFIG_RISCV_ISA_ZABHA) &&
+ riscv_has_extension_unlikely(RISCV_ISA_EXT_ZABHA)) {
+ asm volatile (".option push\n"
+ ".option arch, +zabha\n"
+ "amoor.b %[tmp], %[val], %[ptr]\n"
+ ".option pop"
+ : [ptr] "+A" (*(u8 *)ptr), [tmp] "=r" (tmp)
+ : [val] "r" ((u8)(val & PERCPU_8_16_GET_MASK(8))));
+ } else {
+ u32 *ptr32 = PERCPU_8_16_GET_PTR32(ptr);
+ const unsigned long shift = PERCPU_8_16_GET_SHIFT(ptr);
+ const u32 mask = PERCPU_8_16_GET_MASK(8) << shift;
+ const u8 val_trunc = val & PERCPU_8_16_GET_MASK(8);
+ u32 old, new;
+ u8 field;
+
+ asm volatile ("0: lr.w %0, %2\n"
+ "and %3, %0, %4\n"
+ "srl %3, %3, %5\n"
+ "or %3, %3, %6\n"
+ "and %3, %3, %8\n"
+ "sll %3, %3, %5\n"
+ "and %1, %0, %7\n"
+ "or %1, %1, %3\n"
+ "sc.w %1, %1, %2\n"
+ "bnez %1, 0b\n"
+ : "=&r"(old), "=&r"(new), "+A"(*ptr32), "=&r"(field)
+ : "r"(mask), "r"(shift), "r"(val_trunc), "r"(~mask),
+ "r"(PERCPU_8_16_GET_MASK(8)));
+ }
+}
+
+static inline void __percpu_or_amo_case_16(void *ptr, unsigned long val)
+{
+ u16 tmp;
+
+ if (IS_ENABLED(CONFIG_RISCV_ISA_ZABHA) &&
+ riscv_has_extension_unlikely(RISCV_ISA_EXT_ZABHA)) {
+ asm volatile (".option push\n"
+ ".option arch, +zabha\n"
+ "amoor.h %[tmp], %[val], %[ptr]\n"
+ ".option pop"
+ : [ptr] "+A" (*(u16 *)ptr), [tmp] "=r" (tmp)
+ : [val] "r" ((u16)(val & PERCPU_8_16_GET_MASK(16))));
+ } else {
+ u32 *ptr32 = PERCPU_8_16_GET_PTR32(ptr);
+ const unsigned long shift = PERCPU_8_16_GET_SHIFT(ptr);
+ const u32 mask = PERCPU_8_16_GET_MASK(16) << shift;
+ const u16 val_trunc = val & PERCPU_8_16_GET_MASK(16);
+ u32 old, new;
+ u16 field;
+
+ asm volatile ("0: lr.w %0, %2\n"
+ "and %3, %0, %4\n"
+ "srl %3, %3, %5\n"
+ "or %3, %3, %6\n"
+ "and %3, %3, %8\n"
+ "sll %3, %3, %5\n"
+ "and %1, %0, %7\n"
+ "or %1, %1, %3\n"
+ "sc.w %1, %1, %2\n"
+ "bnez %1, 0b\n"
+ : "=&r"(old), "=&r"(new), "+A"(*ptr32), "=&r"(field)
+ : "r"(mask), "r"(shift), "r"(val_trunc), "r"(~mask),
+ "r"(PERCPU_8_16_GET_MASK(16)));
+ }
+}
+
+static inline u8 __percpu_add_return_amo_case_8(void *ptr, unsigned long val)
+{
+ if (IS_ENABLED(CONFIG_RISCV_ISA_ZABHA) &&
+ riscv_has_extension_unlikely(RISCV_ISA_EXT_ZABHA)) {
+ register u8 ret;
+
+ asm volatile (".option push\n"
+ ".option arch, +zabha\n"
+ "amoadd.b %[ret], %[val], %[ptr]\n"
+ ".option pop"
+ : [ptr] "+A"(*(u8 *)ptr), [ret] "=r"(ret)
+ : [val] "r"((u8)(val & PERCPU_8_16_GET_MASK(8))));
+ return ret + (u8)(val & PERCPU_8_16_GET_MASK(8));
+ }
+
+ {
+ u32 *ptr32 = PERCPU_8_16_GET_PTR32(ptr);
+ const unsigned long shift = PERCPU_8_16_GET_SHIFT(ptr);
+ const u32 mask = PERCPU_8_16_GET_MASK(8) << shift;
+ const u32 inv_mask = ~mask;
+ const u8 val_trunc = val & PERCPU_8_16_GET_MASK(8);
+ u32 old, new, field;
+
+ asm volatile ("0: lr.w %0, %3\n"
+ "and %1, %0, %4\n"
+ "srl %1, %1, %5\n"
+ "add %1, %1, %6\n"
+ "and %1, %1, %7\n"
+ "sll %1, %1, %5\n"
+ "and %2, %0, %8\n"
+ "or %2, %2, %1\n"
+ "sc.w %2, %2, %3\n"
+ "bnez %2, 0b\n"
+ : "=&r"(old), "=&r"(field), "=&r"(new), "+A"(*ptr32)
+ : "r"(mask), "r"(shift), "r"(val_trunc),
+ "r"(PERCPU_8_16_GET_MASK(8)), "r"(inv_mask));
+ return field >> shift;
+ }
+}
+
+static inline u16 __percpu_add_return_amo_case_16(void *ptr, unsigned long val)
+{
+ if (IS_ENABLED(CONFIG_RISCV_ISA_ZABHA) &&
+ riscv_has_extension_unlikely(RISCV_ISA_EXT_ZABHA)) {
+ register u16 ret;
+
+ asm volatile (".option push\n"
+ ".option arch, +zabha\n"
+ "amoadd.h %[ret], %[val], %[ptr]\n"
+ ".option pop"
+ : [ptr] "+A"(*(u16 *)ptr), [ret] "=r"(ret)
+ : [val] "r"((u16)(val & PERCPU_8_16_GET_MASK(16))));
+ return ret + (u16)(val & PERCPU_8_16_GET_MASK(16));
+ }
+
+ {
+ u32 *ptr32 = PERCPU_8_16_GET_PTR32(ptr);
+ const unsigned long shift = PERCPU_8_16_GET_SHIFT(ptr);
+ const u32 mask = PERCPU_8_16_GET_MASK(16) << shift;
+ const u32 inv_mask = ~mask;
+ const u16 val_trunc = val & PERCPU_8_16_GET_MASK(16);
+ u32 old, new, field;
+
+ asm volatile ("0: lr.w %0, %3\n"
+ "and %1, %0, %4\n"
+ "srl %1, %1, %5\n"
+ "add %1, %1, %6\n"
+ "and %1, %1, %7\n"
+ "sll %1, %1, %5\n"
+ "and %2, %0, %8\n"
+ "or %2, %2, %1\n"
+ "sc.w %2, %2, %3\n"
+ "bnez %2, 0b\n"
+ : "=&r"(old), "=&r"(field), "=&r"(new), "+A"(*ptr32)
+ : "r"(mask), "r"(shift), "r"(val_trunc),
+ "r"(PERCPU_8_16_GET_MASK(16)), "r"(inv_mask));
+ return field >> shift;
+ }
+}
+
+/*
+ * Put arch_cmpxchg_relaxed() behind a function boundary to isolate its macro
+ * locals from those at the call site. asm-generic implements
+ * this_cpu_try_cmpxchg_N() using this_cpu_cmpxchg_N() and a local named __old.
+ * RISC-V's _arch_cmpxchg() also declares __old, so expanding it in the caller's
+ * scope would turn its initializer into "__old = (__old)" and read the
+ * uninitialized inner variable.
+ *
+ * __always_inline avoids an out-of-line call.
+ * _pcp_protect_cmpxchg_return() provides the instrument_atomic_read_write()
+ * hook that cmpxchg_relaxed() would otherwise provide.
+ */
+#define PERCPU_CMPXCHG_OP(sz) \
+static __always_inline unsigned long \
+__percpu_cmpxchg_case_##sz(void *ptr, unsigned long old, \
+ unsigned long new) \
+{ \
+ return arch_cmpxchg_relaxed((u##sz *)ptr, (u##sz)old, \
+ (u##sz)new); \
+}
+
+PERCPU_CMPXCHG_OP(8)
+PERCPU_CMPXCHG_OP(16)
+PERCPU_CMPXCHG_OP(32)
+
+#ifdef CONFIG_64BIT
+PERCPU_CMPXCHG_OP(64)
+#endif
+
+#define _pcp_protect(op, pcp, ...) \
+({ \
+ preempt_disable_notrace(); \
+ op(raw_cpu_ptr(&(pcp)), __VA_ARGS__); \
+ preempt_enable_notrace(); \
+})
+
+#define _pcp_protect_return(op, pcp, args...) \
+({ \
+ typeof(pcp) __retval; \
+ preempt_disable_notrace(); \
+ __retval = (typeof(pcp))op(raw_cpu_ptr(&(pcp)), ##args); \
+ preempt_enable_notrace(); \
+ __retval; \
+})
+
+#define _pcp_protect_cmpxchg_return(op, pcp, old, new) \
+({ \
+ typeof(pcp) __retval; \
+ typeof(pcp) *__pcp_ptr; \
+ \
+ preempt_disable_notrace(); \
+ __pcp_ptr = raw_cpu_ptr(&(pcp)); \
+ instrument_atomic_read_write(__pcp_ptr, sizeof(*__pcp_ptr)); \
+ __retval = (typeof(pcp))op(__pcp_ptr, (unsigned long)(old), \
+ (unsigned long)(new)); \
+ preempt_enable_notrace(); \
+ __retval; \
+})
+
+#define this_cpu_read_1(pcp) _pcp_protect_return(__percpu_read_8, pcp)
+#define this_cpu_read_2(pcp) _pcp_protect_return(__percpu_read_16, pcp)
+#define this_cpu_read_4(pcp) _pcp_protect_return(__percpu_read_32, pcp)
+
+#ifdef CONFIG_64BIT
+#define this_cpu_read_8(pcp) _pcp_protect_return(__percpu_read_64, pcp)
+#endif
+
+#define this_cpu_write_1(pcp, val) _pcp_protect(__percpu_write_8, pcp, (unsigned long)val)
+#define this_cpu_write_2(pcp, val) _pcp_protect(__percpu_write_16, pcp, (unsigned long)val)
+#define this_cpu_write_4(pcp, val) _pcp_protect(__percpu_write_32, pcp, (unsigned long)val)
+
+#ifdef CONFIG_64BIT
+#define this_cpu_write_8(pcp, val) _pcp_protect(__percpu_write_64, pcp, (unsigned long)val)
+#endif
+
+#define this_cpu_add_1(pcp, val) _pcp_protect(__percpu_add_amo_case_8, pcp, val)
+#define this_cpu_add_2(pcp, val) _pcp_protect(__percpu_add_amo_case_16, pcp, val)
+#define this_cpu_add_4(pcp, val) _pcp_protect(__percpu_add_amo_case_32, pcp, val)
+
+#ifdef CONFIG_64BIT
+#define this_cpu_add_8(pcp, val) _pcp_protect(__percpu_add_amo_case_64, pcp, val)
+#endif
+
+#define this_cpu_add_return_1(pcp, val) \
+_pcp_protect_return(__percpu_add_return_amo_case_8, pcp, val)
+
+#define this_cpu_add_return_2(pcp, val) \
+_pcp_protect_return(__percpu_add_return_amo_case_16, pcp, val)
+
+#define this_cpu_add_return_4(pcp, val) \
+_pcp_protect_return(__percpu_add_return_amo_case_32, pcp, val)
+
+#ifdef CONFIG_64BIT
+#define this_cpu_add_return_8(pcp, val) \
+_pcp_protect_return(__percpu_add_return_amo_case_64, pcp, val)
+#endif
+
+#define this_cpu_and_1(pcp, val) _pcp_protect(__percpu_and_amo_case_8, pcp, val)
+#define this_cpu_and_2(pcp, val) _pcp_protect(__percpu_and_amo_case_16, pcp, val)
+#define this_cpu_and_4(pcp, val) _pcp_protect(__percpu_and_amo_case_32, pcp, val)
+
+#ifdef CONFIG_64BIT
+#define this_cpu_and_8(pcp, val) _pcp_protect(__percpu_and_amo_case_64, pcp, val)
+#endif
+
+#define this_cpu_or_1(pcp, val) _pcp_protect(__percpu_or_amo_case_8, pcp, val)
+#define this_cpu_or_2(pcp, val) _pcp_protect(__percpu_or_amo_case_16, pcp, val)
+#define this_cpu_or_4(pcp, val) _pcp_protect(__percpu_or_amo_case_32, pcp, val)
+
+#ifdef CONFIG_64BIT
+#define this_cpu_or_8(pcp, val) _pcp_protect(__percpu_or_amo_case_64, pcp, val)
+#endif
+
+#define this_cpu_xchg_1(pcp, val) _pcp_protect_return(xchg_relaxed, pcp, val)
+#define this_cpu_xchg_2(pcp, val) _pcp_protect_return(xchg_relaxed, pcp, val)
+#define this_cpu_xchg_4(pcp, val) _pcp_protect_return(xchg_relaxed, pcp, val)
+
+#ifdef CONFIG_64BIT
+#define this_cpu_xchg_8(pcp, val) _pcp_protect_return(xchg_relaxed, pcp, val)
+#endif
+
+#define this_cpu_cmpxchg_1(pcp, o, n) \
+ _pcp_protect_cmpxchg_return(__percpu_cmpxchg_case_8, pcp, o, n)
+
+#define this_cpu_cmpxchg_2(pcp, o, n) \
+ _pcp_protect_cmpxchg_return(__percpu_cmpxchg_case_16, pcp, o, n)
+
+#define this_cpu_cmpxchg_4(pcp, o, n) \
+ _pcp_protect_cmpxchg_return(__percpu_cmpxchg_case_32, pcp, o, n)
+
+#ifdef CONFIG_64BIT
+#define this_cpu_cmpxchg_8(pcp, o, n) \
+ _pcp_protect_cmpxchg_return(__percpu_cmpxchg_case_64, pcp, o, n)
+
+#define this_cpu_cmpxchg64(pcp, o, n) this_cpu_cmpxchg_8(pcp, o, n)
+#endif
+
+#ifdef system_has_cmpxchg128
+static inline u128 __percpu_generic_cmpxchg128(void *ptr, u128 old_val,
+ u128 new_val)
+{
+ unsigned long flags;
+ u128 *p = ptr;
+ u128 ret;
+
+ flags = arch_local_irq_save();
+ ret = *p;
+ if (ret == old_val)
+ *p = new_val;
+ arch_local_irq_restore(flags);
+
+ return ret;
+}
+
+#define this_cpu_cmpxchg128(pcp, o, n) \
+({ \
+ u128 old__ = (o), new__ = (n), ret__; \
+ typeof(pcp) *ptr__; \
+ \
+ preempt_disable_notrace(); \
+ ptr__ = raw_cpu_ptr(&(pcp)); \
+ if (system_has_cmpxchg128()) \
+ ret__ = cmpxchg128_local(ptr__, old__, new__); \
+ else \
+ ret__ = __percpu_generic_cmpxchg128(ptr__, old__, new__); \
+ preempt_enable_notrace(); \
+ ret__; \
+})
+#endif
+
+#undef PERCPU_RW_OPS
+#undef __PERCPU_AMO_OP_CASE
+#undef PERCPU_8_16_GET_SHIFT
+#undef PERCPU_8_16_GET_MASK
+#undef PERCPU_8_16_GET_PTR32
+#undef PERCPU_CMPXCHG_OP
+
+#include <asm-generic/percpu.h>
+
+#endif /* __ASM_PERCPU_H */
base-commit: ddfe51b0a0f5dc9e415cc2c8ad8db17532dd426b
--
2.39.5
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v9 2/3] riscv: store percpu offset into thread_info
2026-11-08 11:04 [PATCH v9 0/3] riscv: optimize percpu access Yunhui Cui
2026-11-08 11:04 ` [PATCH v9 1/3] riscv: implement this_cpu operations Yunhui Cui
@ 2026-11-08 11:04 ` Yunhui Cui
2026-11-08 11:04 ` [PATCH v9 3/3] riscv: select HAVE_CMPXCHG_LOCAL Yunhui Cui
2 siblings, 0 replies; 5+ messages in thread
From: Yunhui Cui @ 2026-11-08 11:04 UTC (permalink / raw)
To: pjw, palmer, aou, alex, dennis, tj, cl, ast, daniel, andrii,
martin.lau, eddyz87, memxor, song, yonghong.song, jolsa, bjorn,
pulehui, puranjay, thuth, ajones, ben.dooks, rkrcmar,
samuel.holland, zong.li, conor.dooley, tglx, debug,
seanwascoding, andybnac, menglong8.dong, cyrilbur, wangruikang,
atishp, apatel, linux-riscv, linux-kernel, linux-mm, bpf, arnd,
nathan, nick.desaulniers+lkml, morbo, justinstitt, qingfang.deng,
linux-arch, llvm, dave, emil, ihor.solodrai, broonie, cuiyunhui
RISC-V currently derives the current per-CPU offset from the CPU number
and __per_cpu_offset[]. Cache it in thread_info so per-CPU accesses can
load the offset directly.
Initialize the cached offset for the boot and secondary CPUs, and
propagate it across context switches. Set it before each secondary CPU
startup, including parallel bringup and CPU hotplug.
Link: https://lists.riscv.org/g/tech-privileged/topic/risc_v_tech_arch_review/113437553?page=2
Link: https://lore.kernel.org/r/20260826012457.ig3rfmji7vywf6kc@offworld
Signed-off-by: Yunhui Cui <cuiyunhui@bytedance.com>
---
arch/riscv/include/asm/asm.h | 6 +-----
arch/riscv/include/asm/percpu.h | 5 +++++
arch/riscv/include/asm/switch_to.h | 9 +++++++++
arch/riscv/include/asm/thread_info.h | 3 ++-
arch/riscv/kernel/asm-offsets.c | 2 +-
arch/riscv/kernel/smpboot.c | 10 +++++++++-
arch/riscv/net/bpf_jit_comp64.c | 9 +--------
7 files changed, 28 insertions(+), 16 deletions(-)
diff --git a/arch/riscv/include/asm/asm.h b/arch/riscv/include/asm/asm.h
index b8bf842d4c136..06ac38f733b0a 100644
--- a/arch/riscv/include/asm/asm.h
+++ b/arch/riscv/include/asm/asm.h
@@ -92,11 +92,7 @@
#ifdef CONFIG_SMP
.macro asm_per_cpu dst sym tmp
- lw \tmp, TASK_TI_CPU_NUM(tp)
- slli \tmp, \tmp, RISCV_LGPTR
- la \dst, __per_cpu_offset
- add \dst, \dst, \tmp
- REG_L \tmp, 0(\dst)
+ REG_L \tmp, TASK_TI_PCPU_OFFSET(tp)
la \dst, \sym
add \dst, \dst, \tmp
.endm
diff --git a/arch/riscv/include/asm/percpu.h b/arch/riscv/include/asm/percpu.h
index 280b122b9fe69..2a3ddc3538869 100644
--- a/arch/riscv/include/asm/percpu.h
+++ b/arch/riscv/include/asm/percpu.h
@@ -9,9 +9,12 @@
#include <asm/alternative-macros.h>
#include <asm/cmpxchg.h>
#include <asm/cpufeature-macros.h>
+#include <asm/current.h>
#include <asm/hwcap.h>
#include <asm/irqflags.h>
+#include <linux/thread_info.h>
+
#define PERCPU_RW_OPS(sz) \
static inline unsigned long __percpu_read_##sz(void *ptr) \
{ \
@@ -557,6 +560,8 @@ static inline u128 __percpu_generic_cmpxchg128(void *ptr, u128 old_val,
})
#endif
+#define __my_cpu_offset (((struct thread_info *)current)->pcpu_offset)
+
#undef PERCPU_RW_OPS
#undef __PERCPU_AMO_OP_CASE
#undef PERCPU_8_16_GET_SHIFT
diff --git a/arch/riscv/include/asm/switch_to.h b/arch/riscv/include/asm/switch_to.h
index 123c89b694e87..b4a4d4a44f25d 100644
--- a/arch/riscv/include/asm/switch_to.h
+++ b/arch/riscv/include/asm/switch_to.h
@@ -89,6 +89,14 @@ static inline void __switch_to_envcfg(struct task_struct *next)
:: "r" (next->thread.envcfg) : "memory");
}
+static inline void __switch_to_pcpu_offset(struct task_struct *prev,
+ struct task_struct *next)
+{
+#ifdef CONFIG_SMP
+ next->thread_info.pcpu_offset = prev->thread_info.pcpu_offset;
+#endif
+}
+
extern struct task_struct *__switch_to(struct task_struct *,
struct task_struct *);
@@ -125,6 +133,7 @@ do { \
if (switch_to_should_flush_icache(__next)) \
local_flush_icache_all(); \
__switch_to_envcfg(__next); \
+ __switch_to_pcpu_offset(__prev, __next); \
((last) = __switch_to(__prev, __next)); \
} while (0)
diff --git a/arch/riscv/include/asm/thread_info.h b/arch/riscv/include/asm/thread_info.h
index 55019fdfa9eca..f10ba62b61016 100644
--- a/arch/riscv/include/asm/thread_info.h
+++ b/arch/riscv/include/asm/thread_info.h
@@ -53,6 +53,7 @@
struct thread_info {
unsigned long flags; /* low level flags */
int preempt_count; /* 0=>preemptible, <0=>BUG */
+ int cpu;
/*
* These stack pointers are overwritten on every system call or
* exception. SP is also saved to the stack it can be recovered when
@@ -60,8 +61,8 @@ struct thread_info {
*/
long kernel_sp; /* Kernel stack pointer */
long user_sp; /* User stack pointer */
- int cpu;
unsigned long syscall_work; /* SYSCALL_WORK_ flags */
+ unsigned long pcpu_offset;
#ifdef CONFIG_SHADOW_CALL_STACK
void *scs_base;
void *scs_sp;
diff --git a/arch/riscv/kernel/asm-offsets.c b/arch/riscv/kernel/asm-offsets.c
index a75f0cfea1e9f..f4f7ee976cd94 100644
--- a/arch/riscv/kernel/asm-offsets.c
+++ b/arch/riscv/kernel/asm-offsets.c
@@ -38,6 +38,7 @@ void asm_offsets(void)
OFFSET(TASK_THREAD_SUM, task_struct, thread.sum);
OFFSET(TASK_TI_CPU, task_struct, thread_info.cpu);
+ OFFSET(TASK_TI_PCPU_OFFSET, task_struct, thread_info.pcpu_offset);
OFFSET(TASK_TI_PREEMPT_COUNT, task_struct, thread_info.preempt_count);
OFFSET(TASK_TI_KERNEL_SP, task_struct, thread_info.kernel_sp);
OFFSET(TASK_TI_USER_SP, task_struct, thread_info.user_sp);
@@ -50,7 +51,6 @@ void asm_offsets(void)
OFFSET(TASK_TI_A2, task_struct, thread_info.a2);
#endif
- OFFSET(TASK_TI_CPU_NUM, task_struct, thread_info.cpu);
#ifdef CONFIG_RISCV_USER_CFI
OFFSET(TASK_TI_CFI_STATE, task_struct, thread_info.user_cfi_state);
OFFSET(TASK_TI_USER_SSP, task_struct, thread_info.user_cfi_state.user_shdw_stk);
diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c
index a9de2dae804c6..6431914d21165 100644
--- a/arch/riscv/kernel/smpboot.c
+++ b/arch/riscv/kernel/smpboot.c
@@ -175,6 +175,10 @@ void __init setup_smp(void)
static int start_secondary_cpu(int cpu, struct task_struct *tidle)
{
+ /* The idle task is reused across CPU hotplug. */
+ tidle->thread_info.cpu = cpu;
+ tidle->thread_info.pcpu_offset = per_cpu_offset(cpu);
+
if (cpu_ops->cpu_start)
return cpu_ops->cpu_start(cpu, tidle);
@@ -190,7 +194,6 @@ int arch_cpuhp_kick_ap_alive(unsigned int cpu, struct task_struct *tidle)
int __cpu_up(unsigned int cpu, struct task_struct *tidle)
{
int ret;
- tidle->thread_info.cpu = cpu;
ret = start_secondary_cpu(cpu, tidle);
if (!ret) {
@@ -208,6 +211,11 @@ int __cpu_up(unsigned int cpu, struct task_struct *tidle)
}
#endif
+void __init smp_prepare_boot_cpu(void)
+{
+ __my_cpu_offset = per_cpu_offset(smp_processor_id());
+}
+
void __init smp_cpus_done(unsigned int max_cpus)
{
}
diff --git a/arch/riscv/net/bpf_jit_comp64.c b/arch/riscv/net/bpf_jit_comp64.c
index 371405598faa7..fa6ce5613c4d6 100644
--- a/arch/riscv/net/bpf_jit_comp64.c
+++ b/arch/riscv/net/bpf_jit_comp64.c
@@ -1466,15 +1466,8 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
if (rd != rs)
emit_mv(rd, rs, ctx);
#ifdef CONFIG_SMP
- /* Load current CPU number in T1 */
- emit_lw(RV_REG_T1, offsetof(struct thread_info, cpu),
+ emit_ld(RV_REG_T1, offsetof(struct thread_info, pcpu_offset),
RV_REG_TP, ctx);
- /* Load address of __per_cpu_offset array in T2 */
- emit_addr(RV_REG_T2, (u64)&__per_cpu_offset, extra_pass, ctx);
- /* Get address of __per_cpu_offset[cpu] in T1 */
- emit_sh3add(RV_REG_T1, RV_REG_T1, RV_REG_T2, ctx);
- /* Load __per_cpu_offset[cpu] in T1 */
- emit_ld(RV_REG_T1, 0, RV_REG_T1, ctx);
/* Add the offset to Rd */
emit_add(rd, rd, RV_REG_T1, ctx);
#endif
--
2.39.5
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v9 3/3] riscv: select HAVE_CMPXCHG_LOCAL
2026-11-08 11:04 [PATCH v9 0/3] riscv: optimize percpu access Yunhui Cui
2026-11-08 11:04 ` [PATCH v9 1/3] riscv: implement this_cpu operations Yunhui Cui
2026-11-08 11:04 ` [PATCH v9 2/3] riscv: store percpu offset into thread_info Yunhui Cui
@ 2026-11-08 11:04 ` Yunhui Cui
2 siblings, 0 replies; 5+ messages in thread
From: Yunhui Cui @ 2026-11-08 11:04 UTC (permalink / raw)
To: pjw, palmer, aou, alex, dennis, tj, cl, ast, daniel, andrii,
martin.lau, eddyz87, memxor, song, yonghong.song, jolsa, bjorn,
pulehui, puranjay, thuth, ajones, ben.dooks, rkrcmar,
samuel.holland, zong.li, conor.dooley, tglx, debug,
seanwascoding, andybnac, menglong8.dong, cyrilbur, wangruikang,
atishp, apatel, linux-riscv, linux-kernel, linux-mm, bpf, arnd,
nathan, nick.desaulniers+lkml, morbo, justinstitt, qingfang.deng,
linux-arch, llvm, dave, emil, ihor.solodrai, broonie, cuiyunhui
Now that RISC-V provides this_cpu_cmpxchg(), select
HAVE_CMPXCHG_LOCAL so mm/vmstat.c and lib/percpu_counter.c can use
this_cpu_try_cmpxchg() instead of serializing per-CPU counter updates
with local_irq_save() and local_irq_restore().
Suggested-by: Davidlohr Bueso <dave@stgolabs.net>
Link: https://lore.kernel.org/r/20260826010748.zphu3wh2hlm4t4o2@offworld
Signed-off-by: Yunhui Cui <cuiyunhui@bytedance.com>
---
arch/riscv/Kconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index bde1f1885038f..458ef01e926a4 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -155,6 +155,7 @@ config RISCV
select HAVE_ARCH_VMAP_STACK if MMU && 64BIT
select HAVE_ASM_MODVERSIONS
select HAVE_BUILDTIME_MCOUNT_SORT
+ select HAVE_CMPXCHG_LOCAL
select HAVE_CONTEXT_TRACKING_USER
select HAVE_DEBUG_KMEMLEAK
select HAVE_DMA_CONTIGUOUS if MMU
--
2.39.5
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v9 1/3] riscv: implement this_cpu operations
2026-11-08 11:04 ` [PATCH v9 1/3] riscv: implement this_cpu operations Yunhui Cui
@ 2026-11-08 11:58 ` bot+bpf-ci
0 siblings, 0 replies; 5+ messages in thread
From: bot+bpf-ci @ 2026-11-08 11:58 UTC (permalink / raw)
To: cuiyunhui, pjw, palmer, aou, alex, dennis, tj, cl, ast, daniel,
andrii, martin.lau, eddyz87, memxor, song, yonghong.song, jolsa,
bjorn, pulehui, puranjay, thuth, ajones, ben.dooks, rkrcmar,
samuel.holland, zong.li, conor.dooley, tglx, debug,
seanwascoding, andybnac, menglong8.dong, cyrilbur, wangruikang,
atishp, apatel, linux-riscv, linux-kernel, linux-mm, bpf, arnd,
nathan, nick.desaulniers+lkml, morbo, justinstitt, qingfang.deng,
linux-arch, llvm, dave, emil, ihor.solodrai, broonie, cuiyunhui
Cc: ast, andrii, daniel, martin.lau, eddyz87, yonghong.song, mason,
ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 1826 bytes --]
> riscv: implement this_cpu operations
TL;DR: this_cpu_write_N casts val without parentheses, so expressions like
"zero - 1" or "a < 0" may store different values than the generic code on
RISC-V; no in-tree caller found that triggers it.
> diff --git a/arch/riscv/include/asm/percpu.h b/arch/riscv/include/asm/percpu.h
> new file mode 100644
> --- /dev/null
> +++ b/arch/riscv/include/asm/percpu.h
[ ... ]
> +#define this_cpu_write_1(pcp, val) _pcp_protect(__percpu_write_8, pcp, (unsigned long)val)
> +#define this_cpu_write_2(pcp, val) _pcp_protect(__percpu_write_16, pcp, (unsigned long)val)
> +#define this_cpu_write_4(pcp, val) _pcp_protect(__percpu_write_32, pcp, (unsigned long)val)
> +
> +#ifdef CONFIG_64BIT
> +#define this_cpu_write_8(pcp, val) _pcp_protect(__percpu_write_64, pcp, (unsigned long)val)
> +#endif
Should these casts be (unsigned long)(val)? this_cpu_write() passes val
through unparenthesized, so the cast binds only to the first operand of an
expression, which changes the stored value compared to the generic
implementation:
u64 pcp; u32 zero = 0;
this_cpu_write(pcp, zero - 1);
generic: 0x00000000ffffffff
here: (unsigned long)zero - 1 = 0xffffffffffffffff
int a = -1;
this_cpu_write(int_pcp, a < 0);
generic: 1
here: (unsigned long)a < 0, which is always 0
This is the same problem arm64 fixed in 885bff055a0f ("arm64: percpu: Fix
this_cpu_write() casting"). I did not find an in-tree caller that
currently triggers it, but a future caller passing a binary expression
would get a different value on RISC-V only.
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/42475504268
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-11-08 11:58 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-08 11:04 [PATCH v9 0/3] riscv: optimize percpu access Yunhui Cui
2026-11-08 11:04 ` [PATCH v9 1/3] riscv: implement this_cpu operations Yunhui Cui
2026-11-08 11:58 ` bot+bpf-ci
2026-11-08 11:04 ` [PATCH v9 2/3] riscv: store percpu offset into thread_info Yunhui Cui
2026-11-08 11:04 ` [PATCH v9 3/3] riscv: select HAVE_CMPXCHG_LOCAL Yunhui Cui
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®