[PATCH v5 0/3] riscv: support CPUs with slow Zaamo
Vladimir Kondratiev
vladimir.kondratiev at mobileye.com
Thu Nov 5 08:37:35 PST 2026
RISC-V split the A extension into Zaamo (AMO*.w/AMO*.d) and Zalrsc
(LR/SC). The kernel puts "a" in -march and emits AMO for atomic_add,
bitops, xchg and the like, and LR/SC for cmpxchg.
Zaamo is not always native. On MIPS P8700 [1] every AMO instruction
traps into M-mode and is emulated there, so the extension is formally
supported, and advertised to userspace, but very slow. A typical
uncontended atomic increment costs:
native AMO 2..5 cycles
LR/SC, private L1 reservation monitor 6..12 cycles
LR/SC, PoC (L2) reservation monitor about 40 cycles
AMO emulated by an M-mode trap about 600 cycles
The last two were measured on the eyeq7h hardware. On such a CPU the
kernel has to use LR/SC for every atomic.
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.
Patch 1 adds RISCV_ISA_ZAAMO. It defaults to y and nothing changes:
-march keeps "a" and the generated code is the same. When it is turned
off, -march carries _zalrsc instead of "a" and every kernel atomic is
an LR/SC loop. Such a kernel runs on any hart that implements A, it
just does not use AMO, so the option does not depend on NONPORTABLE.
Turning Zaamo off needs a compiler that accepts _zaamo and _zalrsc in
-march; GCC 15.1 is the first release that does.
The choice is made at compile time rather than with alternatives.
Nearly every CPU has a native A extension, and an alternative would
inflate all those kernels with the nops needed to pad one AMO to the
size of an LR/SC loop, at every atomic site, for the benefit of a few.
Nothing changes for userspace. The hart still advertises "a", which is
true. Userspace that cares can know about the slow AMO upfront, or
measure it.
Zalrsc stays mandatory. cmpxchg, conditional atomics and futex CAS are
already LR/SC sequences; Zaamo cannot replace them. RISCV_ISA_ZALRSC
is always y and exists as the Kconfig counterpart of
RISCV_ISA_EXT_ZALRSC, which hwcap.h already has next to
RISCV_ISA_EXT_ZAAMO; the two sets are tested together, for example by
rv_ext_enabled() in the BPF JIT. RISCV_ISA_A exists for the same
reason, paired with RISCV_ISA_EXT_A.
Three sets of extensions provide every atomic Linux needs: "a", which
is what the kernel supports today; Zalrsc plus Ziccrse, which this
series adds, with Ziccrse as the forward-progress guarantee for the
constrained LR/SC loops; and Zaamo plus Zacas, with CAS loops in place
of LR/SC as on x86. The series leaves the last one open:
RISCV_ISA_ZALRSC is where it would go, by allowing "n" there and making
amocas the base implementation.
The BPF JIT encodes its own atomics. Patch 2 makes it emit LR/SC loops
when Zaamo is disabled, so BPF programs do not trap either. The Zabha
and Zacas paths in the JIT and in cmpxchg.h are selected at runtime
from the ISA string; firmware for a CPU that emulates AMO should not
advertise them, as both extensions are AMO.
A breakpoint inside an LR/SC sequence drops the reservation and the
loop never completes. With every atomic an LR/SC loop that is easy to
hit, so patch 3 makes kprobes and uprobes reject such addresses.
kprobes walk from the enclosing symbol; uprobes scan forward using the
constrained-sequence rules from the ISA. This patch fixes a problem
that exists today, since cmpxchg is already an LR/SC loop. It does not
depend on the rest of the series and can be merged on its own.
[1] https://mips.com/products/hardware/p8700/
Changes in v5:
- Treat Zaamo as supported but slow rather than absent. The hart keeps
advertising "a" and the kernel keeps accepting it, so the boot-time
extension check patch is dropped.
- Drop the NONPORTABLE dependency. A kernel built without Zaamo runs
on any hart that implements A.
- Rename TOOLCHAIN_HAS_ZALRSC to TOOLCHAIN_HAS_ZALRSC_ZAAMO and test
both _zaamo and _zalrsc in -march.
- Explain why RISCV_ISA_ZALRSC and RISCV_ISA_A exist, and why the
choice is made at compile time rather than with alternatives.
- Add the BPF JIT patch. Without Zaamo the JIT emits LR/SC loops for
the BPF read-modify-write atomics on RV32 and RV64, and reserves a
second exception-table entry for the sc of an arena atomic.
- Reject kprobes and uprobes on lr and sc, and on any instruction
between them.
- Link to v4: https://patch.msgid.link/20261101-lrsc-only-v4-0-4e1d010463b3@mobileye.com
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 at mobileye.com>
---
Vladimir Kondratiev (3):
riscv: build kernel atomics from LR/SC when Zaamo is disabled
riscv, bpf: emit LR/SC loops when Zaamo is disabled
riscv: probes: reject probes inside an LR/SC sequence
arch/riscv/Kconfig | 53 ++++++++++++++++
arch/riscv/Makefile | 16 +++--
arch/riscv/include/asm/atomic.h | 52 +++++++++++++++-
arch/riscv/include/asm/bitops.h | 47 ++++++++++++++
arch/riscv/include/asm/cmpxchg.h | 16 +++++
arch/riscv/include/asm/futex.h | 45 ++++++++++++++
arch/riscv/include/asm/insn.h | 28 +++++++++
arch/riscv/kernel/entry.S | 7 +++
arch/riscv/kernel/head.S | 12 ++++
arch/riscv/kernel/probes/decode-insn.c | 94 ++++++++++++++++++++++++++++
arch/riscv/kernel/probes/decode-insn.h | 7 +++
arch/riscv/kernel/probes/kprobes.c | 22 ++++++-
arch/riscv/kernel/probes/uprobes.c | 31 ++++++++++
arch/riscv/net/bpf_jit.h | 1 +
arch/riscv/net/bpf_jit_comp32.c | 81 +++++++++++++++++++++++--
arch/riscv/net/bpf_jit_comp64.c | 108 ++++++++++++++++++++++++++++++++-
arch/riscv/net/bpf_jit_core.c | 29 +++++++++
17 files changed, 636 insertions(+), 13 deletions(-)
---
base-commit: c03c1a14507dfaf8b856c2608f9ac216021fc222
change-id: 20260120-lrsc-only-cc06422f5bb1
Best regards,
--
Vladimir Kondratiev <vladimir.kondratiev at mobileye.com>
More information about the linux-riscv
mailing list