mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [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; 6+ 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] 6+ 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; 6+ 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] 6+ 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; 6+ 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] 6+ 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
  0 siblings, 0 replies; 6+ 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] 6+ 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
  0 siblings, 0 replies; 6+ 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] 6+ 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; 6+ 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] 6+ messages in thread

end of thread, other threads:[~2026-11-03 22:27 UTC | newest]

Thread overview: 6+ 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-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-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®