[PATCH] riscv: Avoid smp_mb__after_spinlock() fences on non-SMP

Davidlohr Bueso dave at stgolabs.net
Sun Oct 18 18:34:56 PDT 2026


smp_mb__after_spinlock() is defined in asm/barrier.h, which
linux/spinlock.h includes unconditionally, so non-SMP kernels get
RISCV_FENCE(iorw, iorw) as well and emit a full fence.

The other architectures that override it, arm64, csky, powerpc and
xtensa, define it in asm/spinlock.h, which linux/spinlock.h only
includes for CONFIG_SMP, so their non-SMP builds get the generic
kcsan_mb() fallback. Move the riscv definition, the SMP fence is
unchanged.

Signed-off-by: Davidlohr Bueso <dave at stgolabs.net>
---
 arch/riscv/include/asm/barrier.h  | 27 ---------------------------
 arch/riscv/include/asm/spinlock.h | 27 +++++++++++++++++++++++++++
 2 files changed, 27 insertions(+), 27 deletions(-)

diff --git a/arch/riscv/include/asm/barrier.h b/arch/riscv/include/asm/barrier.h
index 700ba3f922cb..2284473844d8 100644
--- a/arch/riscv/include/asm/barrier.h
+++ b/arch/riscv/include/asm/barrier.h
@@ -24,33 +24,6 @@
 #define __smp_rmb()	RISCV_FENCE(r, r)
 #define __smp_wmb()	RISCV_FENCE(w, w)
 
-/*
- * This is a very specific barrier: it's currently only used in two places in
- * the kernel, both in the scheduler.  See include/linux/spinlock.h for the two
- * orderings it guarantees, but the "critical section is RCsc" guarantee
- * mandates a barrier on RISC-V.  The sequence looks like:
- *
- *    lr.aq lock
- *    sc    lock <= LOCKED
- *    smp_mb__after_spinlock()
- *    // critical section
- *    lr    lock
- *    sc.rl lock <= UNLOCKED
- *
- * The AQ/RL pair provides a RCpc critical section, but there's not really any
- * way we can take advantage of that here because the ordering is only enforced
- * on that one lock.  Thus, we're just doing a full fence.
- *
- * Since we allow writeX to be called from preemptive regions we need at least
- * an "o" in the predecessor set to ensure device writes are visible before the
- * task is marked as available for scheduling on a new hart.  While I don't see
- * any concrete reason we need a full IO fence, it seems safer to just upgrade
- * this in order to avoid any IO crossing a scheduling boundary.  In both
- * instances the scheduler pairs this with an mb(), so nothing is necessary on
- * the new hart.
- */
-#define smp_mb__after_spinlock()	RISCV_FENCE(iorw, iorw)
-
 #define __smp_store_release(p, v)					\
 do {									\
 	compiletime_assert_atomic_type(*p);				\
diff --git a/arch/riscv/include/asm/spinlock.h b/arch/riscv/include/asm/spinlock.h
index 52f11bfd0079..54bd8db8eb4f 100644
--- a/arch/riscv/include/asm/spinlock.h
+++ b/arch/riscv/include/asm/spinlock.h
@@ -3,6 +3,33 @@
 #ifndef __ASM_RISCV_SPINLOCK_H
 #define __ASM_RISCV_SPINLOCK_H
 
+#include <asm/fence.h>
+
+/*
+ * This is a very specific barrier.  See include/linux/spinlock.h for the two
+ * orderings it guarantees, but the "critical section is RCsc" guarantee
+ * mandates a barrier on RISC-V.  The sequence looks like:
+ *
+ *    lr.aq lock
+ *    sc    lock <= LOCKED
+ *    smp_mb__after_spinlock()
+ *    // critical section
+ *    lr    lock
+ *    sc.rl lock <= UNLOCKED
+ *
+ * The AQ/RL pair provides a RCpc critical section, but there's not really any
+ * way we can take advantage of that here because the ordering is only enforced
+ * on that one lock.  Thus, we're just doing a full fence.
+ *
+ * Since we allow writeX to be called from preemptive regions we need at least
+ * an "o" in the predecessor set to ensure device writes are visible before the
+ * task is marked as available for scheduling on a new hart.  While I don't see
+ * any concrete reason we need a full IO fence, it seems safer to just upgrade
+ * this in order to avoid any IO crossing a scheduling boundary.  The
+ * scheduler pairs this with an mb(), so nothing is necessary on the new hart.
+ */
+#define smp_mb__after_spinlock()	RISCV_FENCE(iorw, iorw)
+
 #ifdef CONFIG_QUEUED_SPINLOCKS
 #define _Q_PENDING_LOOPS	(1 << 9)
 #endif
-- 
2.39.5




More information about the linux-riscv mailing list