Skip to content

Commit 5ee121a

Browse files
Merge patch series "riscv: Apply Zawrs when available"
Andrew Jones <[email protected]> says: Zawrs provides two instructions (wrs.nto and wrs.sto), where both are meant to allow the hart to enter a low-power state while waiting on a store to a memory location. The instructions also both wait an implementation-defined "short" duration (unless the implementation terminates the stall for another reason). The difference is that while wrs.sto will terminate when the duration elapses, wrs.nto, depending on configuration, will either just keep waiting or an ILL exception will be raised. Linux will use wrs.nto, so if platforms have an implementation which falls in the "just keep waiting" category (which is not expected), then it should _not_ advertise Zawrs in the hardware description. Like wfi (and with the same {m,h}status bits to configure it), when wrs.nto is configured to raise exceptions it's expected that the higher privilege level will see the instruction was a wait instruction, do something, and then resume execution following the instruction. For example, KVM does configure exceptions for wfi (hstatus.VTW=1) and therefore also for wrs.nto. KVM does this for wfi since it's better to allow other tasks to be scheduled while a VCPU waits for an interrupt. For waits such as those where wrs.nto/sto would be used, which are typically locks, it is also a good idea for KVM to be involved, as it can attempt to schedule the lock holding VCPU. This series starts with Christoph's addition of the riscv smp_cond_load_relaxed function which applies wrs.sto when available. That patch has been reworked to use wrs.nto and to use the same approach as Arm for the wait loop, since we can't have arbitrary C code between the load-reserved and the wrs. Then, hwprobe support is added (since the instructions are also usable from usermode), and finally KVM is taught about wrs.nto, allowing guests to see and use the Zawrs extension. We still don't have test results from hardware, and it's not possible to prove that using Zawrs is a win when testing on QEMU, not even when oversubscribing VCPUs to guests. However, it is possible to use KVM selftests to force a scenario where we can prove Zawrs does its job and does it well. [4] is a test which does this and, on my machine, without Zawrs it takes 16 seconds to complete and with Zawrs it takes 0.25 seconds. This series is also available here [1]. In order to use QEMU for testing a build with [2] is needed. In order to enable guests to use Zawrs with KVM using kvmtool, the branch at [3] may be used. [1] https://github.com/jones-drew/linux/commits/riscv/zawrs-v3/ [2] https://lore.kernel.org/all/[email protected]/ [3] https://github.com/jones-drew/kvmtool/commits/riscv/zawrs/ [4] jones-drew@cb2becc Link: https://lore.kernel.org/r/[email protected] * b4-shazam-merge: KVM: riscv: selftests: Add Zawrs extension to get-reg-list test KVM: riscv: Support guest wrs.nto riscv: hwprobe: export Zawrs ISA extension riscv: Add Zawrs support for spinlocks dt-bindings: riscv: Add Zawrs ISA extension description riscv: Provide a definition for 'pause' Signed-off-by: Palmer Dabbelt <[email protected]>
2 parents c9b8cd1 + f2c43c6 commit 5ee121a

File tree

18 files changed

+146
-31
lines changed

18 files changed

+146
-31
lines changed

Documentation/arch/riscv/hwprobe.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,10 @@ The following keys are defined:
235235
supported as defined in the RISC-V ISA manual starting from commit
236236
c732a4f39a4 ("Zcmop is ratified/1.0").
237237

238+
* :c:macro:`RISCV_HWPROBE_EXT_ZAWRS`: The Zawrs extension is supported as
239+
ratified in commit 98918c844281 ("Merge pull request #1217 from
240+
riscv/zawrs") of riscv-isa-manual.
241+
238242
* :c:macro:`RISCV_HWPROBE_KEY_CPUPERF_0`: A bitmask that contains performance
239243
information about the selected set of processors.
240244

Documentation/devicetree/bindings/riscv/extensions.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,13 @@ properties:
177177
is supported as ratified at commit 5059e0ca641c ("update to
178178
ratified") of the riscv-zacas.
179179
180+
- const: zawrs
181+
description: |
182+
The Zawrs extension for entering a low-power state or for trapping
183+
to a hypervisor while waiting on a store to a memory location, as
184+
ratified in commit 98918c844281 ("Merge pull request #1217 from
185+
riscv/zawrs") of riscv-isa-manual.
186+
180187
- const: zba
181188
description: |
182189
The standard Zba bit-manipulation extension for address generation

arch/riscv/Kconfig

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

603+
config RISCV_ISA_ZAWRS
604+
bool "Zawrs extension support for more efficient busy waiting"
605+
depends on RISCV_ALTERNATIVE
606+
default y
607+
help
608+
The Zawrs extension defines instructions to be used in polling loops
609+
which allow a hart to enter a low-power state or to trap to the
610+
hypervisor while waiting on a store to a memory location. Enable the
611+
use of these instructions in the kernel when the Zawrs extension is
612+
detected at boot.
613+
614+
If you don't know what to do here, say Y.
615+
603616
config TOOLCHAIN_HAS_ZBB
604617
bool
605618
default y
@@ -682,13 +695,6 @@ config RISCV_ISA_ZICBOZ
682695

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

685-
config TOOLCHAIN_HAS_ZIHINTPAUSE
686-
bool
687-
default y
688-
depends on !64BIT || $(cc-option,-mabi=lp64 -march=rv64ima_zihintpause)
689-
depends on !32BIT || $(cc-option,-mabi=ilp32 -march=rv32ima_zihintpause)
690-
depends on LLD_VERSION >= 150000 || LD_VERSION >= 23600
691-
692698
config TOOLCHAIN_NEEDS_EXPLICIT_ZICSR_ZIFENCEI
693699
def_bool y
694700
# https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h=aed44286efa8ae8717a77d94b51ac3614e2ca6dc

arch/riscv/Makefile

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,6 @@ else
8282
riscv-march-$(CONFIG_TOOLCHAIN_NEEDS_EXPLICIT_ZICSR_ZIFENCEI) := $(riscv-march-y)_zicsr_zifencei
8383
endif
8484

85-
# Check if the toolchain supports Zihintpause extension
86-
riscv-march-$(CONFIG_TOOLCHAIN_HAS_ZIHINTPAUSE) := $(riscv-march-y)_zihintpause
87-
8885
# Remove F,D,V from isa string for all. Keep extensions between "fd" and "v" by
8986
# matching non-v and non-multi-letter extensions out with the filter ([^v_]*)
9087
KBUILD_CFLAGS += -march=$(shell echo $(riscv-march-y) | sed -E 's/(rv32ima|rv64ima)fd([^v_]*)v?/\1\2/')

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 __arch_xchg_masked(prepend, append, r, p, n) \
1417
({ \
@@ -221,4 +224,59 @@
221224
arch_cmpxchg_release((ptr), (o), (n)); \
222225
})
223226

227+
#ifdef CONFIG_RISCV_ISA_ZAWRS
228+
/*
229+
* Despite wrs.nto being "WRS-with-no-timeout", in the absence of changes to
230+
* @val we expect it to still terminate within a "reasonable" amount of time
231+
* for an implementation-specific other reason, a pending, locally-enabled
232+
* interrupt, or because it has been configured to raise an illegal
233+
* instruction exception.
234+
*/
235+
static __always_inline void __cmpwait(volatile void *ptr,
236+
unsigned long val,
237+
int size)
238+
{
239+
unsigned long tmp;
240+
241+
asm goto(ALTERNATIVE("j %l[no_zawrs]", "nop",
242+
0, RISCV_ISA_EXT_ZAWRS, 1)
243+
: : : : no_zawrs);
244+
245+
switch (size) {
246+
case 4:
247+
asm volatile(
248+
" lr.w %0, %1\n"
249+
" xor %0, %0, %2\n"
250+
" bnez %0, 1f\n"
251+
ZAWRS_WRS_NTO "\n"
252+
"1:"
253+
: "=&r" (tmp), "+A" (*(u32 *)ptr)
254+
: "r" (val));
255+
break;
256+
#if __riscv_xlen == 64
257+
case 8:
258+
asm volatile(
259+
" lr.d %0, %1\n"
260+
" xor %0, %0, %2\n"
261+
" bnez %0, 1f\n"
262+
ZAWRS_WRS_NTO "\n"
263+
"1:"
264+
: "=&r" (tmp), "+A" (*(u64 *)ptr)
265+
: "r" (val));
266+
break;
267+
#endif
268+
default:
269+
BUILD_BUG();
270+
}
271+
272+
return;
273+
274+
no_zawrs:
275+
asm volatile(RISCV_PAUSE : : : "memory");
276+
}
277+
278+
#define __cmpwait_relaxed(ptr, val) \
279+
__cmpwait((ptr), (unsigned long)(val), sizeof(*(ptr)))
280+
#endif
281+
224282
#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
@@ -92,6 +92,7 @@
9292
#define RISCV_ISA_EXT_ZCD 83
9393
#define RISCV_ISA_EXT_ZCF 84
9494
#define RISCV_ISA_EXT_ZCMOP 85
95+
#define RISCV_ISA_EXT_ZAWRS 86
9596

9697
#define RISCV_ISA_EXT_XLINUXENVCFG 127
9798

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,4 +196,8 @@
196196
INSN_I(OPCODE_MISC_MEM, FUNC3(2), __RD(0), \
197197
RS1(base), SIMM12(4))
198198

199+
#define RISCV_PAUSE ".4byte 0x100000f"
200+
#define ZAWRS_WRS_NTO ".4byte 0x00d00073"
201+
#define ZAWRS_WRS_STO ".4byte 0x01d00073"
202+
199203
#endif /* __ASM_INSN_DEF_H */

arch/riscv/include/asm/kvm_host.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ struct kvm_vcpu_stat {
8080
struct kvm_vcpu_stat_generic generic;
8181
u64 ecall_exit_stat;
8282
u64 wfi_exit_stat;
83+
u64 wrs_exit_stat;
8384
u64 mmio_exit_user;
8485
u64 mmio_exit_kernel;
8586
u64 csr_exit_user;

arch/riscv/include/asm/vdso/processor.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#ifndef __ASSEMBLY__
66

77
#include <asm/barrier.h>
8+
#include <asm/insn-def.h>
89

910
static inline void cpu_relax(void)
1011
{
@@ -14,16 +15,11 @@ static inline void cpu_relax(void)
1415
__asm__ __volatile__ ("div %0, %0, zero" : "=r" (dummy));
1516
#endif
1617

17-
#ifdef CONFIG_TOOLCHAIN_HAS_ZIHINTPAUSE
1818
/*
1919
* Reduce instruction retirement.
2020
* This assumes the PC changes.
2121
*/
22-
__asm__ __volatile__ ("pause");
23-
#else
24-
/* Encoding of the pause instruction */
25-
__asm__ __volatile__ (".4byte 0x100000F");
26-
#endif
22+
__asm__ __volatile__ (RISCV_PAUSE);
2723
barrier();
2824
}
2925

0 commit comments

Comments
 (0)