* [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; 11+ 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] 11+ 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:43 ` sashiko-bot ` (2 more replies) 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, 3 replies; 11+ 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 related [flat|nested] 11+ 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:43 ` sashiko-bot 2026-11-08 11:58 ` bot+bpf-ci 2026-11-08 21:32 ` David Laight 2 siblings, 0 replies; 11+ messages in thread From: sashiko-bot @ 2026-11-08 11:43 UTC (permalink / raw) To: Yunhui Cui, 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-mm, bpf, arnd, nathan, nick.desaulniers+lkml, morbo, justinstitt, qingfang.deng, dave, emil Thank you for your contribution! Sashiko AI review found 1 potential issue to consider: - [Low] riscv: percpu: typeof(pcp) retains __percpu and const qualifiers in locals -- commit 9753271dee3a084b7bff8ad7ad29d76cb64e49f4 Author: Yunhui Cui <cuiyunhui@bytedance.com> riscv: implement this_cpu operations This commit implements RISC-V-specific this_cpu operations using preemption disablement and relaxed AMOs (or 32-bit LR/SC for 8-bit and 16-bit accesses when Zabha is unavailable). Link: https://lore.kernel.org/r/20260826010748.zphu3wh2hlm4t4o2@offworld > 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 [ ... ] > +#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_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__; \ > +}) [Severity: Low] Should _pcp_protect_return(), _pcp_protect_cmpxchg_return(), and this_cpu_cmpxchg128() use TYPEOF_UNQUAL(pcp) instead of typeof(pcp) for their local variables and pointers? typeof(pcp) retains the __percpu address-space and const qualifiers from pcp. Because raw_cpu_ptr() strips __percpu via PERCPU_PTR() (since commit 6a39fe05ecaa ("percpu: use TYPEOF_UNQUAL() in *_cpu_ptr() accessors")), declaring __pcp_ptr and ptr__ with typeof(pcp) * leaves them in the __percpu address space and triggers sparse address-space mismatch warnings on assignment from raw_cpu_ptr() and when passing them to helpers taking regular pointers. Retaining const on __retval also causes a build error if this_cpu_read() is used on a const per-CPU variable. -- Sashiko AI review · https://sashiko.dev/#/patchset/20261108110406.97495-1-cuiyunhui@bytedance.com?part=1 ^ permalink raw reply [flat|nested] 11+ 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:43 ` sashiko-bot @ 2026-11-08 11:58 ` bot+bpf-ci 2026-11-08 21:32 ` David Laight 2 siblings, 0 replies; 11+ 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, ll 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] 11+ 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:43 ` sashiko-bot 2026-11-08 11:58 ` bot+bpf-ci @ 2026-11-08 21:32 ` David Laight 2026-11-09 3:47 ` [External] " yunhui cui 2 siblings, 1 reply; 11+ messages in thread From: David Laight @ 2026-11-08 21:32 UTC (permalink / raw) To: Yunhui Cui Cc: 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 On Sun, 8 Nov 2026 19:04:04 +0800 Yunhui Cui <cuiyunhui@bytedance.com> wrote: > 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. Have you looked at some of the schemes (I think arm64 has the latest, but also s390) that do most per-cpu ops without disabling preemption. In particular they save the pre-emption check when pre-emption is enabled. Basically the code tells the scheduler which register contains the per-cpu data so that it can be 'magically' flipped during a context switch. David ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [External] Re: [PATCH v9 1/3] riscv: implement this_cpu operations 2026-11-08 21:32 ` David Laight @ 2026-11-09 3:47 ` yunhui cui 2026-11-09 4:37 ` Jisheng Zhang 0 siblings, 1 reply; 11+ messages in thread From: yunhui cui @ 2026-11-09 3:47 UTC (permalink / raw) To: David Laight Cc: 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 Hi David, On Mon, Nov 9, 2026 at 5:33 AM David Laight <david.laight.linux@gmail.com> wrote: > > On Sun, 8 Nov 2026 19:04:04 +0800 > Yunhui Cui <cuiyunhui@bytedance.com> wrote: > > > 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. > > Have you looked at some of the schemes (I think arm64 has the latest, > but also s390) that do most per-cpu ops without disabling preemption. > In particular they save the pre-emption check when pre-emption > is enabled. > > Basically the code tells the scheduler which register contains the > per-cpu data so that it can be 'magically' flipped during a context > switch. Many thanks for pointing this out and suggesting this optimization. This is a very useful suggestion. In the common case no preemption or migration occurs, so the preemption bookkeeping and reschedule check are unnecessary overhead. Implementing this scheme for RISC-V will require additional changes beyond the current series. If this series is otherwise acceptable, I would like to land the current this_cpu_* support first and then prepare a follow-up series using this approach. Thanks, Yunhui > > David ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [External] Re: [PATCH v9 1/3] riscv: implement this_cpu operations 2026-11-09 3:47 ` [External] " yunhui cui @ 2026-11-09 4:37 ` Jisheng Zhang 2026-11-09 5:49 ` yunhui cui 0 siblings, 1 reply; 11+ messages in thread From: Jisheng Zhang @ 2026-11-09 4:37 UTC (permalink / raw) To: yunhui cui Cc: David Laight, 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 On Mon, Nov 09, 2026 at 11:47:43AM +0800, yunhui cui wrote: > Hi David, > > On Mon, Nov 9, 2026 at 5:33 AM David Laight > <david.laight.linux@gmail.com> wrote: > > > > On Sun, 8 Nov 2026 19:04:04 +0800 > > Yunhui Cui <cuiyunhui@bytedance.com> wrote: > > > > > 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. > > > > Have you looked at some of the schemes (I think arm64 has the latest, > > but also s390) that do most per-cpu ops without disabling preemption. > > In particular they save the pre-emption check when pre-emption > > is enabled. > > > > Basically the code tells the scheduler which register contains the > > per-cpu data so that it can be 'magically' flipped during a context > > switch. > > Many thanks for pointing this out and suggesting this optimization. This > is a very useful suggestion. In the common case no preemption or migration > occurs, so the preemption bookkeeping and reschedule check are unnecessary > overhead. > > Implementing this scheme for RISC-V will require additional changes beyond > the current series. If this series is otherwise acceptable, I would like > to land the current this_cpu_* support first and then prepare a follow-up > series using this approach. Hi Yunhui and David, During the merge window but before rc1, I implemented a riscv POC following arm64's preemptible this cpu scheme. Each operation records its per-cpu pointer, offset and address registers. Exception entry saves and clears this state, and a noinstr c helper repairs the saved offset and address registers on exception return after any migration. However, my PoC is based on current percpu offset. I planed to sent it out last Sunday, but then I found Yunhui sent his percpu series(which is up to v9, I didn't know this before) on Sat. So I hold on the sending. Now would it make sense to rebase the PoC on Yunhui's series patch2 and patch3, especially the percpu offset optimization? I can post an RFC with this rebased implementation. Thanks > > > _______________________________________________ > linux-riscv mailing list > linux-riscv@lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-riscv ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [External] Re: [PATCH v9 1/3] riscv: implement this_cpu operations 2026-11-09 4:37 ` Jisheng Zhang @ 2026-11-09 5:49 ` yunhui cui 2026-11-09 11:56 ` Jisheng Zhang 0 siblings, 1 reply; 11+ messages in thread From: yunhui cui @ 2026-11-09 5:49 UTC (permalink / raw) To: Jisheng Zhang Cc: David Laight, 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 Hi Jisheng, On Mon, Nov 9, 2026 at 12:57 PM Jisheng Zhang <jszhang@kernel.org> wrote: > > On Mon, Nov 09, 2026 at 11:47:43AM +0800, yunhui cui wrote: > > Hi David, > > > > On Mon, Nov 9, 2026 at 5:33 AM David Laight > > <david.laight.linux@gmail.com> wrote: > > > > > > On Sun, 8 Nov 2026 19:04:04 +0800 > > > Yunhui Cui <cuiyunhui@bytedance.com> wrote: > > > > > > > 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. > > > > > > Have you looked at some of the schemes (I think arm64 has the latest, > > > but also s390) that do most per-cpu ops without disabling preemption. > > > In particular they save the pre-emption check when pre-emption > > > is enabled. > > > > > > Basically the code tells the scheduler which register contains the > > > per-cpu data so that it can be 'magically' flipped during a context > > > switch. > > > > Many thanks for pointing this out and suggesting this optimization. This > > is a very useful suggestion. In the common case no preemption or migration > > occurs, so the preemption bookkeeping and reschedule check are unnecessary > > overhead. > > > > Implementing this scheme for RISC-V will require additional changes beyond > > the current series. If this series is otherwise acceptable, I would like > > to land the current this_cpu_* support first and then prepare a follow-up > > series using this approach. > > Hi Yunhui and David, > > During the merge window but before rc1, I implemented a riscv POC > following arm64's preemptible this cpu scheme. Each operation records > its per-cpu pointer, offset and address registers. Exception entry saves > and clears this state, and a noinstr c helper repairs the saved offset > and address registers on exception return after any migration. > > However, my PoC is based on current percpu offset. > > I planed to sent it out last Sunday, but then I found Yunhui sent > his percpu series(which is up to v9, I didn't know this before) on Sat. > So I hold on the sending. > > Now would it make sense to rebase the PoC on Yunhui's series patch2 and > patch3, especially the percpu offset optimization? I can post an RFC > with this rebased implementation. > Given the substantial effort already invested in bringing this series to v9, my original plan was to land the current implementation first and add the preemptible scheme as a follow-up. However, if the consensus is to move directly to the preemptible scheme, I will implement this scheme in the next revision, building on the cached percpu offset introduced in patch 2. Thanks, Yunhui > Thanks > > > > > > _______________________________________________ > > linux-riscv mailing list > > linux-riscv@lists.infradead.org > > http://lists.infradead.org/mailman/listinfo/linux-riscv ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [External] Re: [PATCH v9 1/3] riscv: implement this_cpu operations 2026-11-09 5:49 ` yunhui cui @ 2026-11-09 11:56 ` Jisheng Zhang 0 siblings, 0 replies; 11+ messages in thread From: Jisheng Zhang @ 2026-11-09 11:56 UTC (permalink / raw) To: yunhui cui Cc: David Laight, 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 On Mon, Nov 09, 2026 at 01:49:05PM +0800, yunhui cui wrote: > Hi Jisheng, > > On Mon, Nov 9, 2026 at 12:57 PM Jisheng Zhang <jszhang@kernel.org> wrote: > > > > On Mon, Nov 09, 2026 at 11:47:43AM +0800, yunhui cui wrote: > > > Hi David, > > > > > > On Mon, Nov 9, 2026 at 5:33 AM David Laight > > > <david.laight.linux@gmail.com> wrote: > > > > > > > > On Sun, 8 Nov 2026 19:04:04 +0800 > > > > Yunhui Cui <cuiyunhui@bytedance.com> wrote: > > > > > > > > > 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. > > > > > > > > Have you looked at some of the schemes (I think arm64 has the latest, > > > > but also s390) that do most per-cpu ops without disabling preemption. > > > > In particular they save the pre-emption check when pre-emption > > > > is enabled. > > > > > > > > Basically the code tells the scheduler which register contains the > > > > per-cpu data so that it can be 'magically' flipped during a context > > > > switch. > > > > > > Many thanks for pointing this out and suggesting this optimization. This > > > is a very useful suggestion. In the common case no preemption or migration > > > occurs, so the preemption bookkeeping and reschedule check are unnecessary > > > overhead. > > > > > > Implementing this scheme for RISC-V will require additional changes beyond > > > the current series. If this series is otherwise acceptable, I would like > > > to land the current this_cpu_* support first and then prepare a follow-up > > > series using this approach. > > > > Hi Yunhui and David, > > > > During the merge window but before rc1, I implemented a riscv POC > > following arm64's preemptible this cpu scheme. Each operation records > > its per-cpu pointer, offset and address registers. Exception entry saves > > and clears this state, and a noinstr c helper repairs the saved offset > > and address registers on exception return after any migration. > > > > However, my PoC is based on current percpu offset. > > > > I planed to sent it out last Sunday, but then I found Yunhui sent > > his percpu series(which is up to v9, I didn't know this before) on Sat. > > So I hold on the sending. > > > > Now would it make sense to rebase the PoC on Yunhui's series patch2 and > > patch3, especially the percpu offset optimization? I can post an RFC > > with this rebased implementation. > > > > Given the substantial effort already invested in bringing this series > to v9, my original plan was to land the current implementation first > and add the preemptible scheme as a follow-up. > > However, if the consensus is to move directly to the preemptible scheme, > I will implement this scheme in the next revision, building on the > cached percpu offset introduced in patch 2. > yes, I suggest go with the preemptible design from the beginning. It avoids introducing preemption-disabled wrappers that would later be removed. But your preemption-disabled version isn't dropped, it can be used for performance comparison. We can do similar benchmark as arm64 does: https://lore.kernel.org/linux-arm-kernel/28978498-348e-4cfd-af72-1146a106e96b@arm.com/ ^ permalink raw reply [flat|nested] 11+ 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; 11+ 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 related [flat|nested] 11+ 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; 11+ 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 related [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-11-09 12:17 UTC | newest] Thread overview: 11+ 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:43 ` sashiko-bot 2026-11-08 11:58 ` bot+bpf-ci 2026-11-08 21:32 ` David Laight 2026-11-09 3:47 ` [External] " yunhui cui 2026-11-09 4:37 ` Jisheng Zhang 2026-11-09 5:49 ` yunhui cui 2026-11-09 11:56 ` Jisheng Zhang 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