Skip to content

Commit b8ddb0d

Browse files
cmuellnerpalmer-dabbelt
authored andcommitted
riscv: Add Zawrs support for spinlocks
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]>
1 parent 6d58528 commit b8ddb0d

File tree

6 files changed

+105
-15
lines changed

6 files changed

+105
-15
lines changed

arch/riscv/Kconfig

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,19 @@ config RISCV_ISA_V_PREEMPTIVE
578578
preemption. Enabling this config will result in higher memory
579579
consumption due to the allocation of per-task's kernel Vector context.
580580

581+
config RISCV_ISA_ZAWRS
582+
bool "Zawrs extension support for more efficient busy waiting"
583+
depends on RISCV_ALTERNATIVE
584+
default y
585+
help
586+
The Zawrs extension defines instructions to be used in polling loops
587+
which allow a hart to enter a low-power state or to trap to the
588+
hypervisor while waiting on a store to a memory location. Enable the
589+
use of these instructions in the kernel when the Zawrs extension is
590+
detected at boot.
591+
592+
If you don't know what to do here, say Y.
593+
581594
config TOOLCHAIN_HAS_ZBB
582595
bool
583596
default y

arch/riscv/include/asm/barrier.h

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#define _ASM_RISCV_BARRIER_H
1212

1313
#ifndef __ASSEMBLY__
14+
#include <asm/cmpxchg.h>
1415
#include <asm/fence.h>
1516

1617
#define nop() __asm__ __volatile__ ("nop")
@@ -28,21 +29,6 @@
2829
#define __smp_rmb() RISCV_FENCE(r, r)
2930
#define __smp_wmb() RISCV_FENCE(w, w)
3031

31-
#define __smp_store_release(p, v) \
32-
do { \
33-
compiletime_assert_atomic_type(*p); \
34-
RISCV_FENCE(rw, w); \
35-
WRITE_ONCE(*p, v); \
36-
} while (0)
37-
38-
#define __smp_load_acquire(p) \
39-
({ \
40-
typeof(*p) ___p1 = READ_ONCE(*p); \
41-
compiletime_assert_atomic_type(*p); \
42-
RISCV_FENCE(r, rw); \
43-
___p1; \
44-
})
45-
4632
/*
4733
* This is a very specific barrier: it's currently only used in two places in
4834
* the kernel, both in the scheduler. See include/linux/spinlock.h for the two
@@ -70,6 +56,35 @@ do { \
7056
*/
7157
#define smp_mb__after_spinlock() RISCV_FENCE(iorw, iorw)
7258

59+
#define __smp_store_release(p, v) \
60+
do { \
61+
compiletime_assert_atomic_type(*p); \
62+
RISCV_FENCE(rw, w); \
63+
WRITE_ONCE(*p, v); \
64+
} while (0)
65+
66+
#define __smp_load_acquire(p) \
67+
({ \
68+
typeof(*p) ___p1 = READ_ONCE(*p); \
69+
compiletime_assert_atomic_type(*p); \
70+
RISCV_FENCE(r, rw); \
71+
___p1; \
72+
})
73+
74+
#ifdef CONFIG_RISCV_ISA_ZAWRS
75+
#define smp_cond_load_relaxed(ptr, cond_expr) ({ \
76+
typeof(ptr) __PTR = (ptr); \
77+
__unqual_scalar_typeof(*ptr) VAL; \
78+
for (;;) { \
79+
VAL = READ_ONCE(*__PTR); \
80+
if (cond_expr) \
81+
break; \
82+
__cmpwait_relaxed(ptr, VAL); \
83+
} \
84+
(typeof(*ptr))VAL; \
85+
})
86+
#endif
87+
7388
#include <asm-generic/barrier.h>
7489

7590
#endif /* __ASSEMBLY__ */

arch/riscv/include/asm/cmpxchg.h

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88

99
#include <linux/bug.h>
1010

11+
#include <asm/alternative-macros.h>
1112
#include <asm/fence.h>
13+
#include <asm/hwcap.h>
14+
#include <asm/insn-def.h>
1215

1316
#define __xchg_relaxed(ptr, new, size) \
1417
({ \
@@ -359,4 +362,59 @@
359362
arch_cmpxchg_relaxed((ptr), (o), (n)); \
360363
})
361364

365+
#ifdef CONFIG_RISCV_ISA_ZAWRS
366+
/*
367+
* Despite wrs.nto being "WRS-with-no-timeout", in the absence of changes to
368+
* @val we expect it to still terminate within a "reasonable" amount of time
369+
* for an implementation-specific other reason, a pending, locally-enabled
370+
* interrupt, or because it has been configured to raise an illegal
371+
* instruction exception.
372+
*/
373+
static __always_inline void __cmpwait(volatile void *ptr,
374+
unsigned long val,
375+
int size)
376+
{
377+
unsigned long tmp;
378+
379+
asm goto(ALTERNATIVE("j %l[no_zawrs]", "nop",
380+
0, RISCV_ISA_EXT_ZAWRS, 1)
381+
: : : : no_zawrs);
382+
383+
switch (size) {
384+
case 4:
385+
asm volatile(
386+
" lr.w %0, %1\n"
387+
" xor %0, %0, %2\n"
388+
" bnez %0, 1f\n"
389+
ZAWRS_WRS_NTO "\n"
390+
"1:"
391+
: "=&r" (tmp), "+A" (*(u32 *)ptr)
392+
: "r" (val));
393+
break;
394+
#if __riscv_xlen == 64
395+
case 8:
396+
asm volatile(
397+
" lr.d %0, %1\n"
398+
" xor %0, %0, %2\n"
399+
" bnez %0, 1f\n"
400+
ZAWRS_WRS_NTO "\n"
401+
"1:"
402+
: "=&r" (tmp), "+A" (*(u64 *)ptr)
403+
: "r" (val));
404+
break;
405+
#endif
406+
default:
407+
BUILD_BUG();
408+
}
409+
410+
return;
411+
412+
no_zawrs:
413+
asm volatile(RISCV_PAUSE : : : "memory");
414+
}
415+
416+
#define __cmpwait_relaxed(ptr, val) \
417+
__cmpwait((ptr), (unsigned long)(val), sizeof(*(ptr)))
418+
#endif
419+
362420
#endif /* _ASM_RISCV_CMPXCHG_H */

arch/riscv/include/asm/hwcap.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
#define RISCV_ISA_EXT_ZTSO 72
8282
#define RISCV_ISA_EXT_ZACAS 73
8383
#define RISCV_ISA_EXT_XANDESPMU 74
84+
#define RISCV_ISA_EXT_ZAWRS 75
8485

8586
#define RISCV_ISA_EXT_XLINUXENVCFG 127
8687

arch/riscv/include/asm/insn-def.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,5 +197,7 @@
197197
RS1(base), SIMM12(4))
198198

199199
#define RISCV_PAUSE ".4byte 0x100000f"
200+
#define ZAWRS_WRS_NTO ".4byte 0x00d00073"
201+
#define ZAWRS_WRS_STO ".4byte 0x01d00073"
200202

201203
#endif /* __ASM_INSN_DEF_H */

arch/riscv/kernel/cpufeature.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ const struct riscv_isa_ext_data riscv_isa_ext[] = {
257257
__RISCV_ISA_EXT_DATA(zihintpause, RISCV_ISA_EXT_ZIHINTPAUSE),
258258
__RISCV_ISA_EXT_DATA(zihpm, RISCV_ISA_EXT_ZIHPM),
259259
__RISCV_ISA_EXT_DATA(zacas, RISCV_ISA_EXT_ZACAS),
260+
__RISCV_ISA_EXT_DATA(zawrs, RISCV_ISA_EXT_ZAWRS),
260261
__RISCV_ISA_EXT_DATA(zfa, RISCV_ISA_EXT_ZFA),
261262
__RISCV_ISA_EXT_DATA(zfh, RISCV_ISA_EXT_ZFH),
262263
__RISCV_ISA_EXT_DATA(zfhmin, RISCV_ISA_EXT_ZFHMIN),

0 commit comments

Comments
 (0)