* [PATCH v4 0/2] riscv: support CPUs with Zalrsc but not Zaamo
@ 2026-11-01 12:51 Vladimir Kondratiev
2026-11-01 12:51 ` [PATCH v4 1/2] riscv: build kernel atomics from LR/SC when Zaamo is disabled Vladimir Kondratiev
` (2 more replies)
0 siblings, 3 replies; 8+ messages in thread
From: Vladimir Kondratiev @ 2026-11-01 12:51 UTC (permalink / raw)
To: Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Will Deacon, Peter Zijlstra, Mark Rutland, Gary Guo, Yury Norov,
Rasmus Villemoes, cfu, torvalds, olof, aleksa.paunovic, arikalo,
Boqun Feng, Boqun Feng
Cc: linux-riscv, linux-kernel, Vladimir.Kondratiev, Vladimir Kondratiev
RISC-V split the A extension into Zaamo (AMO*.w/AMO*.d) and Zalrsc
(LR/SC). Linux has so far required the whole A extension.
MIPS P8700 [1] implements only Zalrsc. AMO instructions are emulated by a
trap into SBI. I measured typical userspace atomics on this CPU: an
LR/SC atomic takes about 40 cycles, an AMO atomic about 600. The same
ratio applies to kernel refcounts, bitops, xchg and futexes, so the
kernel needs to emit LR/SC when Zaamo is not implemented.
Mobileye is going to use the "eyeq7h" platform, built on this CPU, for
ADAS and autonomous driving. Kernel atomics are on the scheduling,
memory-management and driver paths of that product, so the emulated
AMO cost is not acceptable there.
This series keeps today's default: both extensions on, -march still
contains "a", generated code is unchanged. Turning Zaamo off is a
NONPORTABLE choice. The resulting kernel matches one -march, which
userspace has to use as well, so a runtime alternative would not
produce a portable image.
Zalrsc stays mandatory. cmpxchg, conditional atomics and futex CAS are
already LR/SC sequences; Zaamo cannot replace them. BPF atomic ops
still emit AMO and remain unsupported on a Zalrsc-only kernel.
[1] https://mips.com/products/hardware/p8700/
Changes in v4:
- Require a compiler that accepts _zalrsc in -march before Zaamo can
be disabled. GCC 15.1 introduces that support. Older compilers keep
Zaamo enabled and -march keeps "a".
- Link to v3: https://patch.msgid.link/20261026-lrsc-only-v3-0-ec29c8190fb1@mobileye.com
Changes in v3:
- Make Zalrsc mandatory. cmpxchg and the conditional atomics already
use LR/SC, so RISCV_ISA_ZALRSC can no longer be turned off.
- Check AMO and LR/SC support independently. A kernel that emits both
requires "a", or both "zaamo" and "zalrsc".
- Put the LR/SC bitop sequence in its own block so two inlined
set_bit or clear_bit calls compile.
- Remove the LR/SC loop that ran after the hart lottery and accessed
address 1. Build the lottery increment with add so RV32 assembles.
- Add an exception-table entry for sc as well as lr in the futex
LR/SC helper.
Changes in v2:
- Switch from a runtime choice of atomic implementation to a
compile-time one, gated by NONPORTABLE.
Signed-off-by: Vladimir Kondratiev <vladimir.kondratiev@mobileye.com>
---
Vladimir Kondratiev (2):
riscv: build kernel atomics from LR/SC when Zaamo is disabled
riscv: accept the atomic extensions this kernel was built with
arch/riscv/Kconfig | 32 +++++++++++++++++++++++
arch/riscv/Makefile | 15 ++++++++---
arch/riscv/include/asm/atomic.h | 56 +++++++++++++++++++++++++++++++++++++++-
arch/riscv/include/asm/bitops.h | 51 ++++++++++++++++++++++++++++++++++++
arch/riscv/include/asm/cmpxchg.h | 18 +++++++++++++
arch/riscv/include/asm/futex.h | 53 +++++++++++++++++++++++++++++++++++++
arch/riscv/kernel/cpu.c | 28 +++++++++++++++++---
arch/riscv/kernel/entry.S | 9 +++++++
arch/riscv/kernel/head.S | 14 ++++++++++
9 files changed, 267 insertions(+), 9 deletions(-)
---
base-commit: c03c1a14507dfaf8b856c2608f9ac216021fc222
change-id: 20260120-lrsc-only-cc06422f5bb1
Best regards,
--
Vladimir Kondratiev <vladimir.kondratiev@mobileye.com>
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH v4 1/2] riscv: build kernel atomics from LR/SC when Zaamo is disabled 2026-11-01 12:51 [PATCH v4 0/2] riscv: support CPUs with Zalrsc but not Zaamo Vladimir Kondratiev @ 2026-11-01 12:51 ` Vladimir Kondratiev 2026-11-03 21:09 ` Conor Dooley 2026-11-01 12:51 ` [PATCH v4 2/2] riscv: accept the atomic extensions this kernel was built with Vladimir Kondratiev 2026-11-03 22:27 ` [PATCH v4 0/2] riscv: support CPUs with Zalrsc but not Zaamo Palmer Dabbelt 2 siblings, 1 reply; 8+ messages in thread From: Vladimir Kondratiev @ 2026-11-01 12:51 UTC (permalink / raw) To: Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Will Deacon, Peter Zijlstra, Mark Rutland, Gary Guo, Yury Norov, Rasmus Villemoes, cfu, torvalds, olof, aleksa.paunovic, arikalo, Boqun Feng, Boqun Feng Cc: linux-riscv, linux-kernel, Vladimir.Kondratiev, Vladimir Kondratiev The A extension is Zaamo plus Zalrsc. MIPS P8700 implements only Zalrsc, and its AMO instructions trap to SBI. A typical atomic is about 40 cycles with LR/SC and about 600 cycles with an emulated AMO, so keep the existing AMO sequences when the toolchain defines __riscv_zaamo or __riscv_atomic, and use an LR/SC loop otherwise. Add RISCV_ISA_ZAAMO and RISCV_ISA_ZALRSC. Both default on, and that selection still passes "a" in -march. Zalrsc remains required because cmpxchg and the conditional atomics are LR/SC sequences. Zaamo can be turned off only with CONFIG_NONPORTABLE, and only when the compiler accepts _zalrsc in -march. GCC 15.1 introduces that support. Older compilers keep Zaamo enabled and -march keeps "a". An LR/SC-only build passes _zalrsc instead of "a". Suggested-by: Chao-ying Fu <cfu@wavecomp.com> Signed-off-by: Vladimir Kondratiev <vladimir.kondratiev@mobileye.com> --- arch/riscv/Kconfig | 32 +++++++++++++++++++++++ arch/riscv/Makefile | 15 ++++++++--- arch/riscv/include/asm/atomic.h | 56 +++++++++++++++++++++++++++++++++++++++- arch/riscv/include/asm/bitops.h | 51 ++++++++++++++++++++++++++++++++++++ arch/riscv/include/asm/cmpxchg.h | 18 +++++++++++++ arch/riscv/include/asm/futex.h | 53 +++++++++++++++++++++++++++++++++++++ arch/riscv/kernel/entry.S | 9 +++++++ arch/riscv/kernel/head.S | 14 ++++++++++ 8 files changed, 243 insertions(+), 5 deletions(-) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 5874ca30f4f6..925963a61f62 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -730,6 +730,38 @@ config RISCV_ISA_ZACAS If you don't know what to do here, say Y. +config TOOLCHAIN_HAS_ZALRSC + bool + default y + depends on !64BIT || $(cc-option,-mabi=lp64 -march=rv64im_zalrsc) + depends on !32BIT || $(cc-option,-mabi=ilp32 -march=rv32im_zalrsc) + +config RISCV_ISA_ZAAMO + bool "Zaamo extension support for AMO atomics" if NONPORTABLE && TOOLCHAIN_HAS_ZALRSC + default y + help + Use AMO instructions for kernel atomic memory operations. When + disabled, those operations are built from LR/SC and the kernel + is compiled with zalrsc instead of the A extension. + + Disable this only for a hart that implements Zalrsc but not + Zaamo, such as the MIPS P8700, and only when the compiler accepts + _zalrsc in -march. GCC 15.1 introduces that support. Without it this + option stays enabled and -march keeps the A extension. + + The resulting kernel cannot run on a hart without LR/SC. + + If you don't know what to do here, say Y. + +config RISCV_ISA_ZALRSC + def_bool y + help + Zalrsc provides LR/SC. cmpxchg and the conditional atomics + require it, so it is always enabled. + +config RISCV_ISA_A + def_bool RISCV_ISA_ZAAMO && RISCV_ISA_ZALRSC + config TOOLCHAIN_HAS_ZBB bool default y diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile index 44f5a439741a..1b0a459a0f74 100644 --- a/arch/riscv/Makefile +++ b/arch/riscv/Makefile @@ -58,8 +58,9 @@ ifeq ($(CONFIG_SHADOW_CALL_STACK),y) endif # ISA string setting -riscv-march-$(CONFIG_ARCH_RV32I) := rv32ima -riscv-march-$(CONFIG_ARCH_RV64I) := rv64ima +riscv-march-$(CONFIG_ARCH_RV32I) := rv32im +riscv-march-$(CONFIG_ARCH_RV64I) := rv64im +riscv-march-$(CONFIG_RISCV_ISA_A) := $(riscv-march-y)a riscv-march-$(CONFIG_FPU) := $(riscv-march-y)fd riscv-march-$(CONFIG_RISCV_ISA_C) := $(riscv-march-y)c riscv-march-$(CONFIG_RISCV_ISA_V) := $(riscv-march-y)v @@ -75,9 +76,15 @@ else riscv-march-$(CONFIG_TOOLCHAIN_NEEDS_EXPLICIT_ZICSR_ZIFENCEI) := $(riscv-march-y)_zicsr_zifencei endif -KBUILD_BASE_ISA = -march=$(shell echo $(riscv-march-y) | sed -E 's/(rv32ima|rv64ima)fd([^v_]*)v?/\1\2/') +KBUILD_BASE_ISA = -march=$(shell echo $(riscv-march-y) | sed -E 's/(rv32ima?|rv64ima?)fd([^v_]*)v?/\1\2/') export KBUILD_BASE_ISA +# Zaamo is off only when the compiler accepts _zalrsc in -march. +ifneq ($(CONFIG_RISCV_ISA_A),y) +riscv-march-$(CONFIG_RISCV_ISA_ZAAMO) := $(riscv-march-y)_zaamo +riscv-march-$(CONFIG_RISCV_ISA_ZALRSC) := $(riscv-march-y)_zalrsc +endif + # Remove F,D,V from isa string for all. Keep extensions between "fd" and "v" by # matching non-v and non-multi-letter extensions out with the filter ([^v_]*) KBUILD_CFLAGS += $(KBUILD_BASE_ISA) @@ -85,7 +92,7 @@ KBUILD_CFLAGS += $(KBUILD_BASE_ISA) KBUILD_AFLAGS += -march=$(riscv-march-y) # For C code built with floating-point support, exclude V but keep F and D. -CC_FLAGS_FPU := -march=$(shell echo $(riscv-march-y) | sed -E 's/(rv32ima|rv64ima)([^v_]*)v?/\1\2/') +CC_FLAGS_FPU := -march=$(shell echo $(riscv-march-y) | sed -E 's/(rv32ima?|rv64ima?)([^v_]*)v?/\1\2/') KBUILD_CFLAGS += -mno-save-restore diff --git a/arch/riscv/include/asm/atomic.h b/arch/riscv/include/asm/atomic.h index 616b8b332ac5..dc6a8c95c975 100644 --- a/arch/riscv/include/asm/atomic.h +++ b/arch/riscv/include/asm/atomic.h @@ -50,6 +50,7 @@ static __always_inline void arch_atomic64_set(atomic64_t *v, s64 i) * have the AQ or RL bits set. These don't return anything, so there's only * one version to worry about. */ +#if defined(__riscv_atomic) || defined(__riscv_zaamo) #define ATOMIC_OP(op, asm_op, I, asm_type, c_type, prefix) \ static __always_inline \ void arch_atomic##prefix##_##op(c_type i, atomic##prefix##_t *v) \ @@ -59,7 +60,25 @@ void arch_atomic##prefix##_##op(c_type i, atomic##prefix##_t *v) \ : "+A" (v->counter) \ : "r" (I) \ : "memory"); \ -} \ +} +#elif defined(__riscv_zalrsc) +#define ATOMIC_OP(op, asm_op, I, asm_type, c_type, prefix) \ +static __always_inline \ +void arch_atomic##prefix##_##op(c_type i, atomic##prefix##_t *v) \ +{ \ + register c_type ret, temp; \ + __asm__ __volatile__ ( \ + "1: lr." #asm_type " %1, %0\n" \ + " " #asm_op " %2, %1, %3\n" \ + " sc." #asm_type " %2, %2, %0\n" \ + " bnez %2, 1b\n" \ + : "+A" (v->counter), "=&r" (ret), "=&r" (temp) \ + : "r" (I) \ + : "memory"); \ +} +#else +#error "Need AMO or LR/SC atomics" +#endif #ifdef CONFIG_GENERIC_ATOMIC64 #define ATOMIC_OPS(op, asm_op, I) \ @@ -84,6 +103,7 @@ ATOMIC_OPS(xor, xor, i) * There's two flavors of these: the arithmetic ops have both fetch and return * versions, while the logical ops only have fetch versions. */ +#if defined(__riscv_atomic) || defined(__riscv_zaamo) #define ATOMIC_FETCH_OP(op, asm_op, I, asm_type, c_type, prefix) \ static __always_inline \ c_type arch_atomic##prefix##_fetch_##op##_relaxed(c_type i, \ @@ -108,6 +128,40 @@ c_type arch_atomic##prefix##_fetch_##op(c_type i, atomic##prefix##_t *v) \ : "memory"); \ return ret; \ } +#elif defined(__riscv_zalrsc) +#define ATOMIC_FETCH_OP(op, asm_op, I, asm_type, c_type, prefix) \ +static __always_inline \ +c_type arch_atomic##prefix##_fetch_##op##_relaxed(c_type i, \ + atomic##prefix##_t *v) \ +{ \ + register c_type ret, temp; \ + __asm__ __volatile__ ( \ + "1: lr." #asm_type " %1, %0\n" \ + " " #asm_op " %2, %1, %3\n" \ + " sc." #asm_type " %2, %2, %0\n" \ + " bnez %2, 1b\n" \ + : "+A" (v->counter), "=&r" (ret), "=&r" (temp) \ + : "r" (I) \ + : "memory"); \ + return ret; \ +} \ +static __always_inline \ +c_type arch_atomic##prefix##_fetch_##op(c_type i, atomic##prefix##_t *v) \ +{ \ + register c_type ret, temp; \ + __asm__ __volatile__ ( \ + "1: lr." #asm_type ".aqrl %1, %0\n" \ + " " #asm_op " %2, %1, %3\n" \ + " sc." #asm_type ".aqrl %2, %2, %0\n" \ + " bnez %2, 1b\n" \ + : "+A" (v->counter), "=&r" (ret), "=&r" (temp) \ + : "r" (I) \ + : "memory"); \ + return ret; \ +} +#else +#error "Need AMO or LR/SC atomics" +#endif #define ATOMIC_OP_RETURN(op, asm_op, c_op, I, asm_type, c_type, prefix) \ static __always_inline \ diff --git a/arch/riscv/include/asm/bitops.h b/arch/riscv/include/asm/bitops.h index 3c1a15be54d8..311547558ff5 100644 --- a/arch/riscv/include/asm/bitops.h +++ b/arch/riscv/include/asm/bitops.h @@ -170,12 +170,17 @@ static __always_inline int variable_fls(unsigned int x) #if (BITS_PER_LONG == 64) #define __AMO(op) "amo" #op ".d" +#define __LR "lr.d" +#define __SC "sc.d" #elif (BITS_PER_LONG == 32) #define __AMO(op) "amo" #op ".w" +#define __LR "lr.w" +#define __SC "sc.w" #else #error "Unexpected BITS_PER_LONG" #endif +#if defined(__riscv_atomic) || defined(__riscv_zaamo) #define __test_and_op_bit_ord(op, mod, nr, addr, ord) \ ({ \ unsigned long __res, __mask; \ @@ -194,6 +199,37 @@ static __always_inline int variable_fls(unsigned int x) : "+A" (addr[BIT_WORD(nr)]) \ : "r" (mod(BIT_MASK(nr))) \ : "memory"); +#elif defined(__riscv_zalrsc) +#define __test_and_op_bit_ord(op, mod, nr, addr, ord) \ +({ \ + unsigned long __res, __mask, __temp; \ + __mask = BIT_MASK(nr); \ + __asm__ __volatile__ ( \ + "1: " __LR #ord " %0, %1\n" \ + #op " %2, %0, %3\n" \ + __SC #ord " %2, %2, %1\n" \ + "bnez %2, 1b\n" \ + : "=&r" (__res), "+A" (addr[BIT_WORD(nr)]), "=&r" (__temp) \ + : "r" (mod(__mask)) \ + : "memory"); \ + ((__res & __mask) != 0); \ +}) + +#define __op_bit_ord(op, mod, nr, addr, ord) \ +do { \ + unsigned long __res, __temp; \ + __asm__ __volatile__ ( \ + "1: " __LR #ord " %0, %1\n" \ + #op " %2, %0, %3\n" \ + __SC #ord " %2, %2, %1\n" \ + "bnez %2, 1b\n" \ + : "=&r" (__res), "+A" (addr[BIT_WORD(nr)]), "=&r" (__temp) \ + : "r" (mod(BIT_MASK(nr))) \ + : "memory"); \ +} while (0) +#else +#error "Need AMO or LR/SC atomics" +#endif #define __test_and_op_bit(op, mod, nr, addr) \ __test_and_op_bit_ord(op, mod, nr, addr, .aqrl) @@ -337,12 +373,27 @@ static __always_inline void arch___clear_bit_unlock( static __always_inline bool arch_xor_unlock_is_negative_byte(unsigned long mask, volatile unsigned long *addr) { +#if defined(__riscv_atomic) || defined(__riscv_zaamo) unsigned long res; __asm__ __volatile__ ( __AMO(xor) ".rl %0, %2, %1" : "=r" (res), "+A" (*addr) : "r" (__NOP(mask)) : "memory"); +#elif defined(__riscv_zalrsc) + unsigned long res, temp; + + __asm__ __volatile__ ( + "1: " __LR ".rl %0, %1\n" + "xor %2, %0, %3\n" + __SC ".rl %2, %2, %1\n" + "bnez %2, 1b\n" + : "=&r" (res), "+A" (*addr), "=&r" (temp) + : "r" (__NOP(mask)) + : "memory"); +#else +#error "Need AMO or LR/SC atomics" +#endif return (res & BIT(7)) != 0; } diff --git a/arch/riscv/include/asm/cmpxchg.h b/arch/riscv/include/asm/cmpxchg.h index 662e160b0522..0399c5b4ef35 100644 --- a/arch/riscv/include/asm/cmpxchg.h +++ b/arch/riscv/include/asm/cmpxchg.h @@ -57,6 +57,7 @@ } \ }) +#if defined(__riscv_atomic) || defined(__riscv_zaamo) #define __arch_xchg(sfx, prepend, append, r, p, n) \ ({ \ __asm__ __volatile__ ( \ @@ -67,6 +68,23 @@ : "r" (n) \ : "memory"); \ }) +#elif defined(__riscv_zalrsc) +#define __arch_xchg(sfx, prepend, append, r, p, n) \ +({ \ + __typeof__(*(__ptr)) temp; \ + __asm__ __volatile__ ( \ + prepend \ + "1: lr" sfx " %0, %1\n" \ + " sc" sfx " %2, %3, %1\n" \ + " bnez %2, 1b\n" \ + append \ + : "=&r" (r), "+A" (*(p)), "=&r" (temp) \ + : "r" (n) \ + : "memory"); \ +}) +#else +#error "Need AMO or LR/SC atomics" +#endif #define _arch_xchg(ptr, new, sc_sfx, swap_sfx, prepend, \ sc_append, swap_append) \ diff --git a/arch/riscv/include/asm/futex.h b/arch/riscv/include/asm/futex.h index 6f3bc9bdb85e..06fe227197d9 100644 --- a/arch/riscv/include/asm/futex.h +++ b/arch/riscv/include/asm/futex.h @@ -19,6 +19,7 @@ #define __disable_user_access() do { } while (0) #endif +#if defined(__riscv_atomic) || defined(__riscv_zaamo) #define __futex_atomic_op(insn, ret, oldval, uaddr, oparg) \ { \ __enable_user_access(); \ @@ -32,17 +33,45 @@ : "memory"); \ __disable_user_access(); \ } +#elif defined(__riscv_zalrsc) +#define __futex_atomic_op(insn, ret, oldval, uaddr, oparg) \ +{ \ + __enable_user_access(); \ + __asm__ __volatile__ ( \ + "1: lr.w.aqrl %[ov], %[u] \n" \ + " " insn " \n" \ + "2: sc.w.aqrl %[t], %[t], %[u] \n" \ + " bnez %[t], 1b \n" \ + "3: \n" \ + _ASM_EXTABLE_UACCESS_ERR(1b, 3b, %[r]) \ + _ASM_EXTABLE_UACCESS_ERR(2b, 3b, %[r]) \ + : [r] "+r" (ret), [ov] "=&r" (oldval), \ + [t] "=&r" (temp), [u] "+m" (*(uaddr)) \ + : [op] "Jr" (oparg) \ + : "memory"); \ + __disable_user_access(); \ +} +#else +#error "Need AMO or LR/SC atomics" +#endif static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr) { +#if defined(__riscv_atomic) || defined(__riscv_zaamo) int oldval = 0, ret = 0; +#elif defined(__riscv_zalrsc) + int oldval = 0, ret = 0, temp = 0; +#else +#error "Need AMO or LR/SC atomics" +#endif if (!access_ok(uaddr, sizeof(u32))) return -EFAULT; uaddr = untagged_addr(uaddr); switch (op) { +#if defined(__riscv_atomic) || defined(__riscv_zaamo) case FUTEX_OP_SET: __futex_atomic_op("amoswap.w.aqrl %[ov],%z[op],%[u]", ret, oldval, uaddr, oparg); @@ -63,6 +92,30 @@ arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr) __futex_atomic_op("amoxor.w.aqrl %[ov],%z[op],%[u]", ret, oldval, uaddr, oparg); break; +#elif defined(__riscv_zalrsc) + case FUTEX_OP_SET: + __futex_atomic_op("mv %[t], %z[op]", + ret, oldval, uaddr, oparg); + break; + case FUTEX_OP_ADD: + __futex_atomic_op("add %[t], %[ov], %z[op]", + ret, oldval, uaddr, oparg); + break; + case FUTEX_OP_OR: + __futex_atomic_op("or %[t], %[ov], %z[op]", + ret, oldval, uaddr, oparg); + break; + case FUTEX_OP_ANDN: + __futex_atomic_op("and %[t], %[ov], %z[op]", + ret, oldval, uaddr, ~oparg); + break; + case FUTEX_OP_XOR: + __futex_atomic_op("xor %[t], %[ov], %z[op]", + ret, oldval, uaddr, oparg); + break; +#else +#error "Need AMO or LR/SC atomics" +#endif default: ret = -ENOSYS; } diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index 61b7a7716697..1c7bbd7fbff8 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -73,7 +73,16 @@ beq a2, zero, .Lnew_valid_map_cpus_restore_context /* Atomically reset the current cpu bit in new_valid_map_cpus */ +#if defined(__riscv_atomic) || defined(__riscv_zaamo) amoxor.d a0, a1, (a0) +#elif defined(__riscv_zalrsc) +1: lr.d a2, (a0) + xor a2, a1, a2 + sc.d a2, a2, (a0) + bnez a2, 1b +#else +#error "Need AMO or LR/SC atomics" +#endif /* * A sfence.vma is required here. Even if we had Svvptc, there's no diff --git a/arch/riscv/kernel/head.S b/arch/riscv/kernel/head.S index f6a8ca49e627..515669bbbc96 100644 --- a/arch/riscv/kernel/head.S +++ b/arch/riscv/kernel/head.S @@ -265,7 +265,21 @@ SYM_CODE_START(_start_kernel) /* Pick one hart to run the main boot sequence */ la a3, hart_lottery li a2, 1 +#if defined(__riscv_atomic) || defined(__riscv_zaamo) amoadd.w a3, a2, (a3) +#elif defined(__riscv_zalrsc) + /* + * hart_lottery is a small 32-bit counter. add is valid on RV32 + * and RV64; sc.w stores the low 32 bits. + */ +1: lr.w t0, (a3) + add t1, t0, a2 + sc.w t1, t1, (a3) + bnez t1, 1b + mv a3, t0 +#else +#error "Need AMO or LR/SC atomics" +#endif bnez a3, .Lsecondary_start #endif /* CONFIG_RISCV_BOOT_SPINWAIT */ -- 2.43.0 ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v4 1/2] riscv: build kernel atomics from LR/SC when Zaamo is disabled 2026-11-01 12:51 ` [PATCH v4 1/2] riscv: build kernel atomics from LR/SC when Zaamo is disabled Vladimir Kondratiev @ 2026-11-03 21:09 ` Conor Dooley 2026-11-04 11:41 ` Vladimir Kondratiev 0 siblings, 1 reply; 8+ messages in thread From: Conor Dooley @ 2026-11-03 21:09 UTC (permalink / raw) To: Vladimir Kondratiev Cc: Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Will Deacon, Peter Zijlstra, Mark Rutland, Gary Guo, Yury Norov, Rasmus Villemoes, cfu, torvalds, olof, aleksa.paunovic, arikalo, Boqun Feng, linux-riscv, linux-kernel [-- Attachment #1: Type: text/plain, Size: 11451 bytes --] Hey, Sorry about not noticing the v4, I had the v3 on my todo list for a few days and didn't check for an update. On Sun, Nov 01, 2026 at 02:51:39PM +0200, Vladimir Kondratiev wrote: > The A extension is Zaamo plus Zalrsc. MIPS P8700 implements only > Zalrsc, and its AMO instructions trap to SBI. A typical atomic is > about 40 cycles with LR/SC and about 600 cycles with an emulated AMO, > so keep the existing AMO sequences when the toolchain defines > __riscv_zaamo or __riscv_atomic, and use an LR/SC loop otherwise. > > Add RISCV_ISA_ZAAMO and RISCV_ISA_ZALRSC. Both default on, and that > selection still passes "a" in -march. Zalrsc remains required because > cmpxchg and the conditional atomics are LR/SC sequences. Zaamo can be > turned off only with CONFIG_NONPORTABLE, and only when the compiler > accepts _zalrsc in -march. GCC 15.1 introduces that support. Older compilers keep Zaamo > enabled and -march keeps "a". An LR/SC-only build passes _zalrsc > instead of "a". > > Suggested-by: Chao-ying Fu <cfu@wavecomp.com> > Signed-off-by: Vladimir Kondratiev <vladimir.kondratiev@mobileye.com> > --- > arch/riscv/Kconfig | 32 +++++++++++++++++++++++ > arch/riscv/Makefile | 15 ++++++++--- > arch/riscv/include/asm/atomic.h | 56 +++++++++++++++++++++++++++++++++++++++- > arch/riscv/include/asm/bitops.h | 51 ++++++++++++++++++++++++++++++++++++ > arch/riscv/include/asm/cmpxchg.h | 18 +++++++++++++ > arch/riscv/include/asm/futex.h | 53 +++++++++++++++++++++++++++++++++++++ > arch/riscv/kernel/entry.S | 9 +++++++ > arch/riscv/kernel/head.S | 14 ++++++++++ > 8 files changed, 243 insertions(+), 5 deletions(-) > > diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig > index 5874ca30f4f6..925963a61f62 100644 > --- a/arch/riscv/Kconfig > +++ b/arch/riscv/Kconfig > @@ -730,6 +730,38 @@ config RISCV_ISA_ZACAS > > If you don't know what to do here, say Y. > > +config TOOLCHAIN_HAS_ZALRSC > + bool > + default y > + depends on !64BIT || $(cc-option,-mabi=lp64 -march=rv64im_zalrsc) > + depends on !32BIT || $(cc-option,-mabi=ilp32 -march=rv32im_zalrsc) > + > +config RISCV_ISA_ZAAMO > + bool "Zaamo extension support for AMO atomics" if NONPORTABLE && TOOLCHAIN_HAS_ZALRSC Probably this option should be called something like TOOLCHAIN_HAS_ZALRSC_ZAAMO if you're going to use it like this, because it is confusing why ZALRSC gates a ZAAMO option. I'm curious actually, does every toolchain provide zalrsc and zaamo as a pair, or should you check for both here? I'd be surprised if there was a toolchain with one but not the other but worth checking to avoid some bizarre toolchain bug.. | > The text here doesn't really make much sense, given it is default | > disabled as it depends on NONPORTABLE that is default disabled. | > If this is retained, it should be default n and the text reworded. | | Not exactly. RISCV_ISA_ZAAMO is visible if NONPORTABLE && | TOOLCHAIN_HAS_ZALRSC (later was added in v4). Otherwise it is not | visible and set to "y". If one disables NONPORTABLE, RISCV_ISA_ZAAMO set | to "y" with no option to change it. Same for added in v4 | TOOLCHAIN_HAS_ZALRSC Ah, right. But that kinda goes against how NONPORTABLE is meant to work since the default cannot run on all platforms? At that point, just get rid of NONPORTABLE? > + default y > + help > + Use AMO instructions for kernel atomic memory operations. When > + disabled, those operations are built from LR/SC and the kernel > + is compiled with zalrsc instead of the A extension. > + > + Disable this only for a hart that implements Zalrsc but not > + Zaamo, such as the MIPS P8700, and only when the compiler accepts > + _zalrsc in -march. GCC 15.1 introduces that support. Without it this > + option stays enabled and -march keeps the A extension. > + > + The resulting kernel cannot run on a hart without LR/SC. > + > + If you don't know what to do here, say Y. > + > +config RISCV_ISA_ZALRSC > + def_bool y > + help > + Zalrsc provides LR/SC. cmpxchg and the conditional atomics > + require it, so it is always enabled. | > So why have a kconfig option for it then? It's pointless if always | > enabled. | | My logic when adding it: | There are 2 sets of extensions related defines: one is Kconfig ones, | CONFIG_RISCV_ISA_<ext>; and another one found in the | arch/riscv/include/asm/hwcap.h, RISCV_ISA_EXT_<ext> | | In the hwcap.h, there's already | | #define RISCV_ISA_EXT_ZAAMO 97 | #define RISCV_ISA_EXT_ZALRSC 98 | | added by commit 35173b666a16 (riscv: add parsing for Zaamo and Zalrsc | extensions, 2024-06-19) | | These 2 sets are logically coupled, and used in the BPF JUT code like | | #define rv_ext_enabled(ext) \ | (IS_ENABLED(CONFIG_RISCV_ISA_##ext) && | riscv_has_extension_likely(RISCV_ISA_EXT_##ext)) | | So I thought if RISCV_ISA_EXT_ZALRSC exists although it is mandatory, | CONFIG_RISCV_ISA_ZALRSC counterpart should exist for the same reason. | | Do you suggest this is different case and it is worth to remove | CONFIG_RISCV_ISA_ZALRSC anyway? If this is the justification for it, then you need to provide this justification in your commit message - but if zalrsc is always enabled then there's no need for any code like that in the bpf jit in the first place, once more making this dead code that should be deleted? > + > +config RISCV_ISA_A > + def_bool RISCV_ISA_ZAAMO && RISCV_ISA_ZALRSC > + > config TOOLCHAIN_HAS_ZBB > bool > default y > diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile > index 44f5a439741a..1b0a459a0f74 100644 > --- a/arch/riscv/Makefile > +++ b/arch/riscv/Makefile > @@ -58,8 +58,9 @@ ifeq ($(CONFIG_SHADOW_CALL_STACK),y) > endif > > # ISA string setting > -riscv-march-$(CONFIG_ARCH_RV32I) := rv32ima > -riscv-march-$(CONFIG_ARCH_RV64I) := rv64ima > +riscv-march-$(CONFIG_ARCH_RV32I) := rv32im > +riscv-march-$(CONFIG_ARCH_RV64I) := rv64im > +riscv-march-$(CONFIG_RISCV_ISA_A) := $(riscv-march-y)a > riscv-march-$(CONFIG_FPU) := $(riscv-march-y)fd > riscv-march-$(CONFIG_RISCV_ISA_C) := $(riscv-march-y)c > riscv-march-$(CONFIG_RISCV_ISA_V) := $(riscv-march-y)v > @@ -75,9 +76,15 @@ else > riscv-march-$(CONFIG_TOOLCHAIN_NEEDS_EXPLICIT_ZICSR_ZIFENCEI) := $(riscv-march-y)_zicsr_zifencei > endif > > -KBUILD_BASE_ISA = -march=$(shell echo $(riscv-march-y) | sed -E 's/(rv32ima|rv64ima)fd([^v_]*)v?/\1\2/') > +KBUILD_BASE_ISA = -march=$(shell echo $(riscv-march-y) | sed -E 's/(rv32ima?|rv64ima?)fd([^v_]*)v?/\1\2/') > export KBUILD_BASE_ISA > > +# Zaamo is off only when the compiler accepts _zalrsc in -march. I think the comment here is not quite right, it should be "can only be off", no? > +ifneq ($(CONFIG_RISCV_ISA_A),y) > +riscv-march-$(CONFIG_RISCV_ISA_ZAAMO) := $(riscv-march-y)_zaamo Isn't this dead code? The logic for RISCV_ISA_A is: config RISCV_ISA_A def_bool RISCV_ISA_ZAAMO && RISCV_ISA_ZALRSC but ZALRSC is always set, so RISCV_ISA_A is equivalent to RISCV_ISA_ZAAMO so there can't be a case where RISCV_ISA_A is not set but RISCV_ISA_ZAAMO is? > +riscv-march-$(CONFIG_RISCV_ISA_ZALRSC) := $(riscv-march-y)_zalrsc And here, this is effectively just riscv-march-y, since if you get as far as here, it must be true. > +endif > + > # Remove F,D,V from isa string for all. Keep extensions between "fd" and "v" by > # matching non-v and non-multi-letter extensions out with the filter ([^v_]*) > KBUILD_CFLAGS += $(KBUILD_BASE_ISA) > @@ -85,7 +92,7 @@ KBUILD_CFLAGS += $(KBUILD_BASE_ISA) > KBUILD_AFLAGS += -march=$(riscv-march-y) > > # For C code built with floating-point support, exclude V but keep F and D. > -CC_FLAGS_FPU := -march=$(shell echo $(riscv-march-y) | sed -E 's/(rv32ima|rv64ima)([^v_]*)v?/\1\2/') > +CC_FLAGS_FPU := -march=$(shell echo $(riscv-march-y) | sed -E 's/(rv32ima?|rv64ima?)([^v_]*)v?/\1\2/') > > KBUILD_CFLAGS += -mno-save-restore > > diff --git a/arch/riscv/include/asm/atomic.h b/arch/riscv/include/asm/atomic.h > index 616b8b332ac5..dc6a8c95c975 100644 > --- a/arch/riscv/include/asm/atomic.h > +++ b/arch/riscv/include/asm/atomic.h > @@ -50,6 +50,7 @@ static __always_inline void arch_atomic64_set(atomic64_t *v, s64 i) > * have the AQ or RL bits set. These don't return anything, so there's only > * one version to worry about. > */ > +#if defined(__riscv_atomic) || defined(__riscv_zaamo) | > ...why are we making this a compiletime decision in the first place? | > Why can't this be an alternative, starting with the zalrsc stuff and | > moving to the better implementation if available? | | I started with alternatives approach, see | https://lore.kernel.org/all/20260114072552.731974-1-vladimir.kondratiev@mobileye.com/ | Then I converted it to the static compile-time decision following input | from Gary Guo that I think is valid, here: | https://lore.kernel.org/all/DFSQIY6WV4U7.3B3RY31KE739B@garyguo.net/ Okay, that logic from Gary does make some sense. I'll accept that argument, but again this needs to be communicated in the commit message. | > Is there some zaamo code that gets generated automatically that we lose | > out on by always building the kernel with zalrsc instead of with all of a? | | There's BPF JIT that emits AMO instructions, patch for this is not ready | yet. These AMO instructions will be emulated and work correctly but very | slowly. I think that needs to be part of this series. You're far too focused on your individual platform where the instructions work, rather than the holistic solution of lowering our minimum requirements so that platforms that do not have any AMO support work. The only way that it'd make sense to move forward with this without the bpf jit patch is if you disabled bpf entirely when RISCV_ISA_ZAAMO is disabled. > +#if defined(__riscv_atomic) || defined(__riscv_zaamo) Something else to note is that we do not use this pattern anywhere else in arch code as far as I know? We always use the RISC_ISA_WHATEVER config option to gate conditional stuff. Since RISCV_ISA_A is equivalent to RISCV_ISA_ZAAMO (unless I am mistaken there somehow) this just collapses to a single check? > #define ATOMIC_OP(op, asm_op, I, asm_type, c_type, prefix) \ > static __always_inline \ > void arch_atomic##prefix##_##op(c_type i, atomic##prefix##_t *v) \ > @@ -59,7 +60,25 @@ void arch_atomic##prefix##_##op(c_type i, atomic##prefix##_t *v) \ > : "+A" (v->counter) \ > : "r" (I) \ > : "memory"); \ > -} \ > +} > +#elif defined(__riscv_zalrsc) > +#define ATOMIC_OP(op, asm_op, I, asm_type, c_type, prefix) \ > +static __always_inline \ > +void arch_atomic##prefix##_##op(c_type i, atomic##prefix##_t *v) \ > +{ \ > + register c_type ret, temp; \ > + __asm__ __volatile__ ( \ > + "1: lr." #asm_type " %1, %0\n" \ > + " " #asm_op " %2, %1, %3\n" \ > + " sc." #asm_type " %2, %2, %0\n" \ > + " bnez %2, 1b\n" \ > + : "+A" (v->counter), "=&r" (ret), "=&r" (temp) \ > + : "r" (I) \ > + : "memory"); \ > +} > +#else > +#error "Need AMO or LR/SC atomics" And this error is actually unreachable? (and so on in every other place that this pattern is repeated) Cheers, Conor. > +#endif [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 228 bytes --] ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v4 1/2] riscv: build kernel atomics from LR/SC when Zaamo is disabled 2026-11-03 21:09 ` Conor Dooley @ 2026-11-04 11:41 ` Vladimir Kondratiev 0 siblings, 0 replies; 8+ messages in thread From: Vladimir Kondratiev @ 2026-11-04 11:41 UTC (permalink / raw) To: Conor Dooley Cc: Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Will Deacon, Peter Zijlstra, Mark Rutland, Gary Guo, Yury Norov, Rasmus Villemoes, cfu, torvalds, olof, aleksa.paunovic, arikalo, Boqun Feng, linux-riscv, linux-kernel On 11/3/26 23:09, Conor Dooley wrote: >> +config TOOLCHAIN_HAS_ZALRSC >> + bool >> + default y >> + depends on !64BIT || $(cc-option,-mabi=lp64 -march=rv64im_zalrsc) >> + depends on !32BIT || $(cc-option,-mabi=ilp32 -march=rv32im_zalrsc) >> + >> +config RISCV_ISA_ZAAMO >> + bool "Zaamo extension support for AMO atomics" if NONPORTABLE && TOOLCHAIN_HAS_ZALRSC > > Probably this option should be called something like > TOOLCHAIN_HAS_ZALRSC_ZAAMO if you're going to use it like this, because > it is confusing why ZALRSC gates a ZAAMO option. > > I'm curious actually, does every toolchain provide zalrsc and zaamo as a > pair, or should you check for both here? I'd be surprised if there was a > toolchain with one but not the other but worth checking to avoid some > bizarre toolchain bug.. Agree, will be in next version. > Ah, right. But that kinda goes against how NONPORTABLE is meant to work > since the default cannot run on all platforms? At that point, just get > rid of NONPORTABLE? Agree, dropping NONPORTABLE > | My logic when adding it: > | There are 2 sets of extensions related defines: one is Kconfig ones, > | CONFIG_RISCV_ISA_<ext>; and another one found in the > | arch/riscv/include/asm/hwcap.h, RISCV_ISA_EXT_<ext> > | > | In the hwcap.h, there's already > | > | #define RISCV_ISA_EXT_ZAAMO 97 > | #define RISCV_ISA_EXT_ZALRSC 98 > | > | added by commit 35173b666a16 (riscv: add parsing for Zaamo and Zalrsc > | extensions, 2024-06-19) > | > | These 2 sets are logically coupled, and used in the BPF JUT code like > | > | #define rv_ext_enabled(ext) \ > | (IS_ENABLED(CONFIG_RISCV_ISA_##ext) && > | riscv_has_extension_likely(RISCV_ISA_EXT_##ext)) > | > | So I thought if RISCV_ISA_EXT_ZALRSC exists although it is mandatory, > | CONFIG_RISCV_ISA_ZALRSC counterpart should exist for the same reason. > | > | Do you suggest this is different case and it is worth to remove > | CONFIG_RISCV_ISA_ZALRSC anyway? > > If this is the justification for it, then you need to provide this > justification in your commit message - but if zalrsc is always enabled > then there's no need for any code like that in the bpf jit in the first > place, once more making this dead code that should be deleted? Adding justification. Also, it is possible to have zaamo+zacas and not support zalrsc; in this case CONFIG_RISCV_ISA_ZALRSC will be "n"; adding clause explaining this theoretical option. >> +# Zaamo is off only when the compiler accepts _zalrsc in -march. > > I think the comment here is not quite right, it should be "can only be > off", no? Sure, fixed >> +ifneq ($(CONFIG_RISCV_ISA_A),y) >> +riscv-march-$(CONFIG_RISCV_ISA_ZAAMO) := $(riscv-march-y)_zaamo > > Isn't this dead code? The logic for RISCV_ISA_A is: > config RISCV_ISA_A > def_bool RISCV_ISA_ZAAMO && RISCV_ISA_ZALRSC > but ZALRSC is always set, so RISCV_ISA_A is equivalent to > RISCV_ISA_ZAAMO so there can't be a case where RISCV_ISA_A is not set > but RISCV_ISA_ZAAMO is? > >> +riscv-march-$(CONFIG_RISCV_ISA_ZALRSC) := $(riscv-march-y)_zalrsc > > And here, this is effectively just riscv-march-y, since if you get as > far as here, it must be true. See above, in theory one can have zaamo+zacas but not zalrsc > | > ...why are we making this a compiletime decision in the first place? > | > Why can't this be an alternative, starting with the zalrsc stuff and > | > moving to the better implementation if available? > | > | I started with alternatives approach, see > | Suspicious Link - Removed > | Then I converted it to the static compile-time decision following input > | from Gary Guo that I think is valid, here: > | Suspicious Link - Removed > > > Okay, that logic from Gary does make some sense. I'll accept that > argument, but again this needs to be communicated in the commit message. Done > | There's BPF JIT that emits AMO instructions, patch for this is not ready > | yet. These AMO instructions will be emulated and work correctly but very > | slowly. > > I think that needs to be part of this series. You're far too focused on > your individual platform where the instructions work, rather than the > holistic solution of lowering our minimum requirements so that platforms > that do not have any AMO support work. The only way that it'd make sense > to move forward with this without the bpf jit patch is if you disabled > bpf entirely when RISCV_ISA_ZAAMO is disabled. Added patch for BPF JIT. Also, added is patch for kprobes/uprobes, to reject probes inside lr/sc sequence. This is needed anyway, having lr/sc blocks for any atomics only increase chances but we do have lr/sc blocks in code anyway and need to protect it. > >> +#if defined(__riscv_atomic) || defined(__riscv_zaamo) > > Something else to note is that we do not use this pattern anywhere else > in arch code as far as I know? We always use the RISC_ISA_WHATEVER > config option to gate conditional stuff. Since RISCV_ISA_A is equivalent > to RISCV_ISA_ZAAMO (unless I am mistaken there somehow) this just collapses > to a single check? Agree, replacing with #ifdef CONFIG_RISCV_ISA_ZAAMO>> +#else >> +#error "Need AMO or LR/SC atomics" > > And this error is actually unreachable? > > (and so on in every other place that this pattern is repeated) Agree, removed Thanks, Vladimir ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v4 2/2] riscv: accept the atomic extensions this kernel was built with 2026-11-01 12:51 [PATCH v4 0/2] riscv: support CPUs with Zalrsc but not Zaamo Vladimir Kondratiev 2026-11-01 12:51 ` [PATCH v4 1/2] riscv: build kernel atomics from LR/SC when Zaamo is disabled Vladimir Kondratiev @ 2026-11-01 12:51 ` Vladimir Kondratiev 2026-11-03 21:41 ` Conor Dooley 2026-11-03 22:27 ` [PATCH v4 0/2] riscv: support CPUs with Zalrsc but not Zaamo Palmer Dabbelt 2 siblings, 1 reply; 8+ messages in thread From: Vladimir Kondratiev @ 2026-11-01 12:51 UTC (permalink / raw) To: Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Will Deacon, Peter Zijlstra, Mark Rutland, Gary Guo, Yury Norov, Rasmus Villemoes, cfu, torvalds, olof, aleksa.paunovic, arikalo, Boqun Feng, Boqun Feng Cc: linux-riscv, linux-kernel, Vladimir.Kondratiev, Vladimir Kondratiev riscv_early_of_processor_hartid() rejects a hart unless its riscv,isa-extensions property contains "a". That rejects a Zalrsc-only hart even when the kernel was built to use only LR/SC. Require "a" or "zaamo" when the kernel emits AMO, and "a" or "zalrsc" when it emits LR/SC. A normal build defines both toolchain macros and still requires the full A extension. The legacy "riscv,isa" fallback keeps its rv{32,64}ima check. Signed-off-by: Vladimir Kondratiev <vladimir.kondratiev@mobileye.com> --- arch/riscv/kernel/cpu.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/arch/riscv/kernel/cpu.c b/arch/riscv/kernel/cpu.c index 3dbc8cc557dd..5d1e39d16e59 100644 --- a/arch/riscv/kernel/cpu.c +++ b/arch/riscv/kernel/cpu.c @@ -82,12 +82,32 @@ int __init riscv_early_of_processor_hartid(struct device_node *node, unsigned lo return -ENODEV; if (of_property_match_string(node, "riscv,isa-extensions", "i") < 0 || - of_property_match_string(node, "riscv,isa-extensions", "m") < 0 || - of_property_match_string(node, "riscv,isa-extensions", "a") < 0) { - pr_warn("CPU with hartid=%lu does not support ima", *hart); + of_property_match_string(node, "riscv,isa-extensions", "m") < 0) { + pr_warn("CPU with hartid=%lu does not support im", *hart); return -ENODEV; } - +#if !defined(__riscv_atomic) && !defined(__riscv_zaamo) && !defined(__riscv_zalrsc) +#error "Need AMO or LR/SC atomics" +#endif + /* + * Require each atomic form this kernel emits. The single-letter + * "a" extension satisfies either check. A Zalrsc-only build defines + * __riscv_zalrsc and checks that extension only. + */ +#if defined(__riscv_atomic) || defined(__riscv_zaamo) + if (of_property_match_string(node, "riscv,isa-extensions", "a") < 0 && + of_property_match_string(node, "riscv,isa-extensions", "zaamo") < 0) { + pr_warn("CPU with hartid=%lu does not support AMO atomics", *hart); + return -ENODEV; + } +#endif +#if defined(__riscv_atomic) || defined(__riscv_zalrsc) + if (of_property_match_string(node, "riscv,isa-extensions", "a") < 0 && + of_property_match_string(node, "riscv,isa-extensions", "zalrsc") < 0) { + pr_warn("CPU with hartid=%lu does not support LR/SC atomics", *hart); + return -ENODEV; + } +#endif return 0; old_interface: -- 2.43.0 ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v4 2/2] riscv: accept the atomic extensions this kernel was built with 2026-11-01 12:51 ` [PATCH v4 2/2] riscv: accept the atomic extensions this kernel was built with Vladimir Kondratiev @ 2026-11-03 21:41 ` Conor Dooley 2026-11-04 11:23 ` Vladimir Kondratiev 0 siblings, 1 reply; 8+ messages in thread From: Conor Dooley @ 2026-11-03 21:41 UTC (permalink / raw) To: Vladimir Kondratiev Cc: Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Will Deacon, Peter Zijlstra, Mark Rutland, Gary Guo, Yury Norov, Rasmus Villemoes, cfu, torvalds, olof, aleksa.paunovic, arikalo, Boqun Feng, linux-riscv, linux-kernel [-- Attachment #1: Type: text/plain, Size: 5045 bytes --] On Sun, Nov 01, 2026 at 02:51:40PM +0200, Vladimir Kondratiev wrote: > riscv_early_of_processor_hartid() rejects a hart unless its > riscv,isa-extensions property contains "a". That rejects a Zalrsc-only > hart even when the kernel was built to use only LR/SC. > > Require "a" or "zaamo" when the kernel emits AMO, and "a" or "zalrsc" > when it emits LR/SC. A normal build defines both toolchain macros and > still requires the full A extension. The legacy "riscv,isa" fallback > keeps its rv{32,64}ima check. > > Signed-off-by: Vladimir Kondratiev <vladimir.kondratiev@mobileye.com> > --- > arch/riscv/kernel/cpu.c | 28 ++++++++++++++++++++++++---- > 1 file changed, 24 insertions(+), 4 deletions(-) > > diff --git a/arch/riscv/kernel/cpu.c b/arch/riscv/kernel/cpu.c > index 3dbc8cc557dd..5d1e39d16e59 100644 > --- a/arch/riscv/kernel/cpu.c > +++ b/arch/riscv/kernel/cpu.c > @@ -82,12 +82,32 @@ int __init riscv_early_of_processor_hartid(struct device_node *node, unsigned lo > return -ENODEV; > > if (of_property_match_string(node, "riscv,isa-extensions", "i") < 0 || > - of_property_match_string(node, "riscv,isa-extensions", "m") < 0 || > - of_property_match_string(node, "riscv,isa-extensions", "a") < 0) { > - pr_warn("CPU with hartid=%lu does not support ima", *hart); > + of_property_match_string(node, "riscv,isa-extensions", "m") < 0) { > + pr_warn("CPU with hartid=%lu does not support im", *hart); > return -ENODEV; > } > - > +#if !defined(__riscv_atomic) && !defined(__riscv_zaamo) && !defined(__riscv_zalrsc) > +#error "Need AMO or LR/SC atomics" > +#endif > + /* > + * Require each atomic form this kernel emits. The single-letter > + * "a" extension satisfies either check. A Zalrsc-only build defines > + * __riscv_zalrsc and checks that extension only. > + */ > +#if defined(__riscv_atomic) || defined(__riscv_zaamo) > + if (of_property_match_string(node, "riscv,isa-extensions", "a") < 0 && > + of_property_match_string(node, "riscv,isa-extensions", "zaamo") < 0) { > + pr_warn("CPU with hartid=%lu does not support AMO atomics", *hart); > + return -ENODEV; > + } > +#endif > +#if defined(__riscv_atomic) || defined(__riscv_zalrsc) > + if (of_property_match_string(node, "riscv,isa-extensions", "a") < 0 && > + of_property_match_string(node, "riscv,isa-extensions", "zalrsc") < 0) { > + pr_warn("CPU with hartid=%lu does not support LR/SC atomics", *hart); > + return -ENODEV; > + } > +#endif > return 0; I really hate this in particular. Please make use of IS_ENABLED() to simplify this and enable writing it without #if stuff. It's effectively just: if (IS_ENABLED(RISCV_ISA_A) && of_property_match_string(node, "riscv,isa-extensions", "a") < 0 && of_property_match_string(node, "riscv,isa-extensions", "zaamo") < 0) { pr_warn("CPU with hartid=%lu does not support AMO atomics", *hart); return -ENODEV; } if (of_property_match_string(node, "riscv,isa-extensions", "a") < 0 && of_property_match_string(node, "riscv,isa-extensions", "zalrsc") < 0) { pr_warn("CPU with hartid=%lu does not support LR/SC atomics", *hart); return -ENODEV; } but I would probably opt to write this as: /* * The hart must support lr/sc in all configurations, and AMOs if the * kernel was built with the full A extension. */ if (of_property_match_string(node, "riscv,isa-extensions", "a") >= 0) return 0; if (of_property_match_string(node, "riscv,isa-extensions", "zalrsc") < 0) { pr_warn("CPU with hartid=%lu does not support LR/SC atomics", *hart); return -ENODEV; } if (IS_ENABLED(RISCV_ISA_A) && of_property_match_string(node, "riscv,isa-extensions", "zaamo") < 0) { pr_warn("CPU with hartid=%lu does not support AMO atomics", *hart); return -ENODEV; } That works, right? That got me thinking though, I feel like this breaks some of the extension parsing logic in other ways. If you putting zalrsc and zaamo in your devicetree, we never do any backwards processing to set the a extension (we don't do this for any extensions by design, we only populate "child" extensions based on their "parent"). If there's some userspace thing that expects to see a in cpufeature or w/e it'd break. There shouldn't be since a has always been supported but who knows. You need custom userspace anyway so I honestly don't give that much of a shit about this going wrong. What does "worry" me is hwprobe. Currently you've not modified hwprobe at all to correctly state that you do not support RISCV_HWPROBE_BASE_BEHAVIOR_IMA. (and brings up the question of whether you support RISCV_HWPROBE_BASE_BEHAVIOR_IMA_FD etc!) Maybe, again you don't care cos of your custom userspace, but this needs to be holistic if it is being done at all. Personally, I don't want to lower the minimum requirements - the people designing this stuff to run linux should have checked to see what our requirements were (and have always been...). Thanks, Conor. [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 228 bytes --] ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v4 2/2] riscv: accept the atomic extensions this kernel was built with 2026-11-03 21:41 ` Conor Dooley @ 2026-11-04 11:23 ` Vladimir Kondratiev 0 siblings, 0 replies; 8+ messages in thread From: Vladimir Kondratiev @ 2026-11-04 11:23 UTC (permalink / raw) To: Conor Dooley Cc: Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Will Deacon, Peter Zijlstra, Mark Rutland, Gary Guo, Yury Norov, Rasmus Villemoes, cfu, torvalds, olof, aleksa.paunovic, arikalo, Boqun Feng, linux-riscv, linux-kernel On 11/3/26 23:41, Conor Dooley wrote: > That got me thinking though, I feel like this breaks some of the > extension parsing logic in other ways. > > If you putting zalrsc and zaamo in your devicetree, we never do any > backwards processing to set the a extension (we don't do this for any > extensions by design, we only populate "child" extensions based on their > "parent"). If there's some userspace thing that expects to see a in > cpufeature or w/e it'd break. There shouldn't be since a has always been > supported but who knows. You need custom userspace anyway so I honestly > don't give that much of a shit about this going wrong. > > What does "worry" me is hwprobe. Currently you've not modified hwprobe > at all to correctly state that you do not support > RISCV_HWPROBE_BASE_BEHAVIOR_IMA. (and brings up the question of whether > you support RISCV_HWPROBE_BASE_BEHAVIOR_IMA_FD etc!) > > Maybe, again you don't care cos of your custom userspace, but this needs > to be holistic if it is being done at all. > Personally, I don't want to lower the minimum requirements - the people > designing this stuff to run linux should have checked to see what our > requirements were (and have always been...). I will drop this patch and keep advertising "a" because formally it is true - all "a" including amo instructions do work but rather slow. Also, not worth to complicate logic here for (so far) one very special SoC. For this SoC, we will compile user space with upfront knowledge of all hardware features. Thanks, Vladimir ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v4 0/2] riscv: support CPUs with Zalrsc but not Zaamo 2026-11-01 12:51 [PATCH v4 0/2] riscv: support CPUs with Zalrsc but not Zaamo Vladimir Kondratiev 2026-11-01 12:51 ` [PATCH v4 1/2] riscv: build kernel atomics from LR/SC when Zaamo is disabled Vladimir Kondratiev 2026-11-01 12:51 ` [PATCH v4 2/2] riscv: accept the atomic extensions this kernel was built with Vladimir Kondratiev @ 2026-11-03 22:27 ` Palmer Dabbelt 2 siblings, 0 replies; 8+ messages in thread From: Palmer Dabbelt @ 2026-11-03 22:27 UTC (permalink / raw) To: vladimir.kondratiev Cc: pjw, aou, Alexandre Ghiti, Will Deacon, peterz, Mark Rutland, gary, yury.norov, linux, cfu, Linus Torvalds, Olof Johansson, aleksa.paunovic, arikalo, boqun, boqun, linux-riscv, linux-kernel, Vladimir.Kondratiev, vladimir.kondratiev On Sun, 01 Nov 2026 04:51:38 PST (-0800), vladimir.kondratiev@mobileye.com wrote: > RISC-V split the A extension into Zaamo (AMO*.w/AMO*.d) and Zalrsc > (LR/SC). Linux has so far required the whole A extension. > > MIPS P8700 [1] implements only Zalrsc. AMO instructions are emulated by a > trap into SBI. I measured typical userspace atomics on this CPU: an > LR/SC atomic takes about 40 cycles, an AMO atomic about 600. The same > ratio applies to kernel refcounts, bitops, xchg and futexes, so the > kernel needs to emit LR/SC when Zaamo is not implemented. In RISC-V land, things that are implemented by M-mode traps are generally considered "implemented" in the context of supervisor mode, just implemented to have low performance. I don't agree with that decision, but that's really an ISA thing and not something we can do that much about it Linux. So I think the first thing you need to decide here is how you're going to be treating the atomics: are they really unimplemented, or are they implemented but slow? Right now this is kind of a mixed bag: you're sort of treated them as unimplemented in the kernel (or at least parts of the kernel), but then implemented in userspace. If you're going to treat the AMOs as "implemented but slow", then I think it's best to see some actual performance numbers for a real application. Sure 600 cycles is slow for an atomic, but I was recently playing around with some much larger systems with ~300 cycle AMOs and couldn't get any meaningful benchmarks to show up over there -- those were OOO cores blocked on bus resources, so a completely different problem, but just saying they're 600 cycles so we can't use them isn't really good enough -- particularly when the alternative is 40 cycles, which is also super slow, so it's not like you can be doing all that much with atomics on these cores even with the "fast" sequences. You're probably also going to need some way to tell userspace about this "implemented but slow" behavior, similar to how we describe misaligned accesses (which is all a bit of a messy retrofit, so maybe don't copy it exactly). If you're going to treat the AMOs as "unimplemented", then we need some way to deal with userspace. Right now we have A as part of all the base behaviors we expose to userspace. We used to have the non-A userspace support, but we decided to drop it when upstreaming the port because it didn't seem useful to anyone. There was a design sketched out here with a VDSO call for cmpxchg() that looked similar to some of the non-A Arm systems, but as far as I know nobody's implemented that in the kernel or userspace so it'd need to be proved out. > Mobileye is going to use the "eyeq7h" platform, built on this CPU, for > ADAS and autonomous driving. Kernel atomics are on the scheduling, > memory-management and driver paths of that product, so the emulated > AMO cost is not acceptable there. Is that announced anywhere? All I can find is an EyeQ 6 on the website. Also: are you planning on continuing with these non-A systems? Those 40-cycle atomics are going to burn you at some point, if the plan for the next-generation systems is just to go implement A then maybe it's best to keep this out of tree. We already have way too much fragmentation in RISC-V land as it is, we don't need any more. > This series keeps today's default: both extensions on, -march still > contains "a", generated code is unchanged. Turning Zaamo off is a > NONPORTABLE choice. The resulting kernel matches one -march, which > userspace has to use as well, so a runtime alternative would not > produce a portable image. It's really not non-portable, though: the Zalrsc code runs fine on systems that support Zaamo, it just may be a performance issue. I'm not sure anyone's actually measured that, but I think it's sane to just assume that native AMOs are going to out-performance LR/SC on _some_ systems. It's still just a performance thing, though... > Zalrsc stays mandatory. cmpxchg, conditional atomics and futex CAS are > already LR/SC sequences; Zaamo cannot replace them. BPF atomic ops > still emit AMO and remain unsupported on a Zalrsc-only kernel. > > [1] https://mips.com/products/hardware/p8700/ > > Changes in v4: > - Require a compiler that accepts _zalrsc in -march before Zaamo can > be disabled. GCC 15.1 introduces that support. Older compilers keep > Zaamo enabled and -march keeps "a". > - Link to v3: https://patch.msgid.link/20261026-lrsc-only-v3-0-ec29c8190fb1@mobileye.com > > Changes in v3: > - Make Zalrsc mandatory. cmpxchg and the conditional atomics already > use LR/SC, so RISCV_ISA_ZALRSC can no longer be turned off. > - Check AMO and LR/SC support independently. A kernel that emits both > requires "a", or both "zaamo" and "zalrsc". > - Put the LR/SC bitop sequence in its own block so two inlined > set_bit or clear_bit calls compile. > - Remove the LR/SC loop that ran after the hart lottery and accessed > address 1. Build the lottery increment with add so RV32 assembles. > - Add an exception-table entry for sc as well as lr in the futex > LR/SC helper. > > Changes in v2: > - Switch from a runtime choice of atomic implementation to a > compile-time one, gated by NONPORTABLE. > > Signed-off-by: Vladimir Kondratiev <vladimir.kondratiev@mobileye.com> > --- > Vladimir Kondratiev (2): > riscv: build kernel atomics from LR/SC when Zaamo is disabled > riscv: accept the atomic extensions this kernel was built with > > arch/riscv/Kconfig | 32 +++++++++++++++++++++++ > arch/riscv/Makefile | 15 ++++++++--- > arch/riscv/include/asm/atomic.h | 56 +++++++++++++++++++++++++++++++++++++++- > arch/riscv/include/asm/bitops.h | 51 ++++++++++++++++++++++++++++++++++++ > arch/riscv/include/asm/cmpxchg.h | 18 +++++++++++++ > arch/riscv/include/asm/futex.h | 53 +++++++++++++++++++++++++++++++++++++ > arch/riscv/kernel/cpu.c | 28 +++++++++++++++++--- > arch/riscv/kernel/entry.S | 9 +++++++ > arch/riscv/kernel/head.S | 14 ++++++++++ > 9 files changed, 267 insertions(+), 9 deletions(-) > --- > base-commit: c03c1a14507dfaf8b856c2608f9ac216021fc222 > change-id: 20260120-lrsc-only-cc06422f5bb1 > > Best regards, ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-11-04 11:41 UTC | newest] Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-11-01 12:51 [PATCH v4 0/2] riscv: support CPUs with Zalrsc but not Zaamo Vladimir Kondratiev 2026-11-01 12:51 ` [PATCH v4 1/2] riscv: build kernel atomics from LR/SC when Zaamo is disabled Vladimir Kondratiev 2026-11-03 21:09 ` Conor Dooley 2026-11-04 11:41 ` Vladimir Kondratiev 2026-11-01 12:51 ` [PATCH v4 2/2] riscv: accept the atomic extensions this kernel was built with Vladimir Kondratiev 2026-11-03 21:41 ` Conor Dooley 2026-11-04 11:23 ` Vladimir Kondratiev 2026-11-03 22:27 ` [PATCH v4 0/2] riscv: support CPUs with Zalrsc but not Zaamo Palmer Dabbelt
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®