Skip to content

Commit

Permalink
riscv: Add Zawrs support for spinlocks
Browse files Browse the repository at this point in the history
RISC-V code uses the generic ticket lock implementation, which calls
the macros smp_cond_load_relaxed() and smp_cond_load_acquire().
Introduce a RISC-V specific implementation of smp_cond_load_relaxed()
which applies WRS.NTO of the Zawrs extension in order to reduce power
consumption while waiting and allows hypervisors to enable guests to
trap while waiting. smp_cond_load_acquire() doesn't need a RISC-V
specific implementation as the generic implementation is based on
smp_cond_load_relaxed() and smp_acquire__after_ctrl_dep() sufficiently
provides the acquire semantics.

This implementation is heavily based on Arm's approach which is the
approach Andrea Parri also suggested.

The Zawrs specification can be found here:
https://github.com/riscv/riscv-zawrs/blob/main/zawrs.adoc

Signed-off-by: Christoph Müllner <[email protected]>
Co-developed-by: Andrew Jones <[email protected]>
Signed-off-by: Andrew Jones <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Palmer Dabbelt <[email protected]>
Signed-off-by: Chen Pei <[email protected]>
  • Loading branch information
cmuellner authored and RevySR committed Sep 3, 2024
1 parent ac7e2d8 commit ef58212
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 18 deletions.
13 changes: 13 additions & 0 deletions arch/riscv/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,19 @@ config RISCV_ISA_V_DEFAULT_ENABLE

If you don't know what to do here, say Y.

config RISCV_ISA_ZAWRS
bool "Zawrs extension support for more efficient busy waiting"
depends on RISCV_ALTERNATIVE
default y
help
The Zawrs extension defines instructions to be used in polling loops
which allow a hart to enter a low-power state or to trap to the
hypervisor while waiting on a store to a memory location. Enable the
use of these instructions in the kernel when the Zawrs extension is
detected at boot.

If you don't know what to do here, say Y.

config TOOLCHAIN_HAS_ZBB
bool
default y
Expand Down
45 changes: 30 additions & 15 deletions arch/riscv/include/asm/barrier.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#define _ASM_RISCV_BARRIER_H

#ifndef __ASSEMBLY__
#include <asm/cmpxchg.h>

#define nop() __asm__ __volatile__ ("nop")
#define __nops(n) ".rept " #n "\nnop\n.endr\n"
Expand All @@ -29,21 +30,6 @@
#define __smp_rmb() RISCV_FENCE(r,r)
#define __smp_wmb() RISCV_FENCE(w,w)

#define __smp_store_release(p, v) \
do { \
compiletime_assert_atomic_type(*p); \
RISCV_FENCE(rw,w); \
WRITE_ONCE(*p, v); \
} while (0)

#define __smp_load_acquire(p) \
({ \
typeof(*p) ___p1 = READ_ONCE(*p); \
compiletime_assert_atomic_type(*p); \
RISCV_FENCE(r,rw); \
___p1; \
})

/*
* 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
Expand Down Expand Up @@ -71,6 +57,35 @@ do { \
*/
#define smp_mb__after_spinlock() RISCV_FENCE(iorw,iorw)

#define __smp_store_release(p, v) \
do { \
compiletime_assert_atomic_type(*p); \
RISCV_FENCE(rw, w); \
WRITE_ONCE(*p, v); \
} while (0)

#define __smp_load_acquire(p) \
({ \
typeof(*p) ___p1 = READ_ONCE(*p); \
compiletime_assert_atomic_type(*p); \
RISCV_FENCE(r, rw); \
___p1; \
})

#ifdef CONFIG_RISCV_ISA_ZAWRS
#define smp_cond_load_relaxed(ptr, cond_expr) ({ \
typeof(ptr) __PTR = (ptr); \
__unqual_scalar_typeof(*ptr) VAL; \
for (;;) { \
VAL = READ_ONCE(*__PTR); \
if (cond_expr) \
break; \
__cmpwait_relaxed(ptr, VAL); \
} \
(typeof(*ptr))VAL; \
})
#endif

#include <asm-generic/barrier.h>

#endif /* __ASSEMBLY__ */
Expand Down
60 changes: 59 additions & 1 deletion arch/riscv/include/asm/cmpxchg.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
#include <linux/bug.h>
#include <linux/mmdebug.h>

#include <asm/barrier.h>
#include <asm/alternative-macros.h>
#include <asm/fence.h>
#include <asm/hwcap.h>
#include <asm/insn-def.h>

#define __xchg_relaxed(ptr, new, size) \
({ \
Expand Down Expand Up @@ -421,6 +423,62 @@
ptr1); \
__ret; \
})

#endif

#ifdef CONFIG_RISCV_ISA_ZAWRS
/*
* Despite wrs.nto being "WRS-with-no-timeout", in the absence of changes to
* @val we expect it to still terminate within a "reasonable" amount of time
* for an implementation-specific other reason, a pending, locally-enabled
* interrupt, or because it has been configured to raise an illegal
* instruction exception.
*/
static __always_inline void __cmpwait(void *ptr,
unsigned long val,
int size)
{
unsigned long tmp;

asm goto(ALTERNATIVE("j %l[no_zawrs]", "nop",
0, RISCV_ISA_EXT_ZAWRS, 1)
: : : : no_zawrs);

switch (size) {
case 4:
asm volatile(
" lr.w %0, %1\n"
" xor %0, %0, %2\n"
" bnez %0, 1f\n"
ZAWRS_WRS_NTO "\n"
"1:"
: "=&r" (tmp), "+A" (*(u32 *)ptr)
: "r" (val));
break;
#if __riscv_xlen == 64
case 8:
asm volatile(
" lr.d %0, %1\n"
" xor %0, %0, %2\n"
" bnez %0, 1f\n"
ZAWRS_WRS_NTO "\n"
"1:"
: "=&r" (tmp), "+A" (*(u64 *)ptr)
: "r" (val));
break;
#endif
default:
BUILD_BUG();
}

return;

no_zawrs:
asm volatile(RISCV_PAUSE : : : "memory");
}

#define __cmpwait_relaxed(ptr, val) \
__cmpwait((ptr), (unsigned long)(val), sizeof(*(ptr)))
#endif

#endif /* _ASM_RISCV_CMPXCHG_H */
7 changes: 5 additions & 2 deletions arch/riscv/include/asm/hwcap.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,12 @@
#define RISCV_ISA_EXT_ZICSR 40
#define RISCV_ISA_EXT_ZIFENCEI 41
#define RISCV_ISA_EXT_ZIHPM 42
#define RISCV_ISA_EXT_XTHEADMATRIX 43
#define RISCV_ISA_EXT_ZAWRS 85

#define RISCV_ISA_EXT_MAX 64
#define RISCV_ISA_EXT_XTHEADMATRIX 126

#define RISCV_ISA_EXT_MAX 128
#define RISCV_ISA_EXT_INVALID U32_MAX

#ifdef CONFIG_RISCV_M_MODE
#define RISCV_ISA_EXT_SxAIA RISCV_ISA_EXT_SMAIA
Expand Down
2 changes: 2 additions & 0 deletions arch/riscv/include/asm/insn-def.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,5 +198,7 @@
RS1(base), SIMM12(4))

#define RISCV_PAUSE ".4byte 0x100000f"
#define ZAWRS_WRS_NTO ".4byte 0x00d00073"
#define ZAWRS_WRS_STO ".4byte 0x01d00073"

#endif /* __ASM_INSN_DEF_H */
1 change: 1 addition & 0 deletions arch/riscv/kernel/cpufeature.c
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ const struct riscv_isa_ext_data riscv_isa_ext[] = {
__RISCV_ISA_EXT_DATA(zifencei, RISCV_ISA_EXT_ZIFENCEI),
__RISCV_ISA_EXT_DATA(zihintpause, RISCV_ISA_EXT_ZIHINTPAUSE),
__RISCV_ISA_EXT_DATA(zihpm, RISCV_ISA_EXT_ZIHPM),
__RISCV_ISA_EXT_DATA(zawrs, RISCV_ISA_EXT_ZAWRS),
__RISCV_ISA_EXT_DATA(zba, RISCV_ISA_EXT_ZBA),
__RISCV_ISA_EXT_DATA(zbb, RISCV_ISA_EXT_ZBB),
__RISCV_ISA_EXT_DATA(zbs, RISCV_ISA_EXT_ZBS),
Expand Down

0 comments on commit ef58212

Please sign in to comment.