[PATCH v2] RISC-V: KVM: Add kvm-riscv.wfi_trap_policy to control VS-mode WFI trapping
"Yuhang.Chen" <[email protected]> Tue, 4 Aug 2026 10:47:07 +0800
| Newsgroups | org.kernel.vger.kvm,org.infradead.lists.kvm-riscv,org.infradead.lists.linux-riscv,org.kernel.vger.linux-doc,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
Add a kernel command-line option, kvm-riscv.wfi_trap_policy=trap|auto,
that controls whether a WFI executed by a VS-mode guest traps into KVM
(HS-mode) or executes natively.
HSTATUS.VTW governs VS-mode WFI: when set, the WFI traps into KVM, which
blocks the vCPU through kvm_vcpu_halt() and releases the CPU to other
runnable tasks; when clear, the guest runs WFI natively. Because RISC-V
WFI is only a hint (it may be a no-op on some implementations), the
policy is re-evaluated each time a vCPU is loaded rather than fixed once
at reset.
trap : always trap VS-mode WFI into KVM (HSTATUS.VTW=1). This is the
default and preserves the previous unconditional behavior.
auto : clear HSTATUS.VTW so the guest runs WFI natively only when the
vCPU is the sole runnable task on the current CPU; otherwise
keep trapping. When the vCPU is alone, skipping the
virtual-instruction exit cannot starve another task, and on
hardware that honors WFI the hart blocks until a VS-mode
interrupt. As soon as another task becomes runnable, the
policy traps again so that KVM blocks the vCPU and yields the
CPU to it. The vCPU therefore never monopolizes the CPU the
way an unconditional native WFI would: it either blocks
through kvm_vcpu_halt(), or runs WFI natively only when no
other task needs the CPU.
Measured on QEMU TCG (-smp 1, -cpu max): wfi_exit_stat delta and guest
wake count over a 3 s window. "busy" adds a CPU-bound competitor that
shares the vCPU's CPU so that single_task_running() reports false:
policy busy exits wakes cpu% note
------ ---- ----- ----- ---- ------------------------
trap off 286 285 6.5 default; no regression
trap on 291 290 101.0 trap is unconditional
auto off 4 287 5.0 sole task: native WFI
auto on 285 284 101.5 competitor -> traps
With "auto", WFI exits drop to ~0 when the vCPU is the only runnable
task, and rise back to the trap level as soon as a competitor appears,
which is the desired dynamic behavior. Host CPU stays low in the
sole-task case; the ~101% in the busy cases is the forked competitor,
not the vCPU.
Assisted-by: YuanSheng:deepseek-v4-pro
Co-developed-by: Quan Zhou <[email protected]>
Signed-off-by: Quan Zhou <[email protected]>
Signed-off-by: Yuhang.Chen <[email protected]>
---
Changes in v2:
- Drop the "notrap" mode, which cleared HSTATUS.VTW unconditionally and
so never trapped: the vCPU never reached kvm_vcpu_halt() and could
stay busy even while idle.
- Add the "auto" mode, which clears HSTATUS.VTW only when the vCPU is
the sole runnable task (single_task_running()) and otherwise traps.
The policy is applied dynamically from kvm_arch_vcpu_load() instead of
once at reset, so a vCPU that stops being the sole runnable task
switches back to trapping.
- Update the kernel-parameters.txt entry for trap/auto.
v1: https://lore.kernel.org/all/[email protected]/
---
.../admin-guide/kernel-parameters.txt | 16 +++++
arch/riscv/kvm/vcpu.c | 59 +++++++++++++++++++
2 files changed, 75 insertions(+)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index b5493a7f8f22..29fc824b5ce2 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -3254,6 +3254,22 @@ Kernel parameters
notrap: clear WFI instruction trap
+ kvm-riscv.wfi_trap_policy=
+ [KVM,RISCV] Control when to set the WFI instruction
+ trap (HSTATUS.VTW) for KVM VMs. The policy is
+ re-evaluated each time a vCPU is loaded, not only at
+ reset, since RISC-V WFI is only a hint.
+
+ trap: always trap VS-mode WFI into KVM (HSTATUS.VTW=1)
+
+ auto: trap unless the vCPU is the only runnable task on
+ the current CPU, in which case clear the trap
+ (HSTATUS.VTW=0) and let the guest execute WFI
+ natively
+
+ Defaults to trap, preserving the previous unconditional
+ behavior.
+
kvm_cma_resv_ratio=n [PPC,EARLY]
Reserves given percentage from system memory area for
contiguous memory allocation for KVM hash pagetable
diff --git a/arch/riscv/kvm/vcpu.c b/arch/riscv/kvm/vcpu.c
index cf6e231e76e2..e9251f81a4f8 100644
--- a/arch/riscv/kvm/vcpu.c
+++ b/arch/riscv/kvm/vcpu.c
@@ -12,8 +12,10 @@
#include <linux/kdebug.h>
#include <linux/module.h>
#include <linux/percpu.h>
+#include <linux/string.h>
#include <linux/vmalloc.h>
#include <linux/sched/signal.h>
+#include <linux/sched/stat.h>
#include <linux/fs.h>
#include <linux/kvm_host.h>
#include <asm/cacheflush.h>
@@ -26,6 +28,59 @@
static DEFINE_PER_CPU(struct kvm_vcpu *, kvm_former_vcpu);
+/*
+ * WFI trap policy for VS-mode guests, controllable through the
+ * kvm-riscv.wfi_trap_policy= kernel command-line option.
+ */
+enum kvm_riscv_wfi_trap_policy {
+ KVM_RISCV_WFI_TRAP, /* Always trap VS-mode WFI into KVM */
+ KVM_RISCV_WFI_AUTO, /* Trap unless the vCPU is the only runnable task */
+};
+
+static enum kvm_riscv_wfi_trap_policy kvm_riscv_wfi_trap_policy __read_mostly =
+ KVM_RISCV_WFI_TRAP;
+
+static int __init early_kvm_riscv_wfi_trap_policy_cfg(char *arg)
+{
+ if (!arg)
+ return -EINVAL;
+
+ if (strcmp(arg, "trap") == 0) {
+ kvm_riscv_wfi_trap_policy = KVM_RISCV_WFI_TRAP;
+ return 0;
+ }
+
+ if (strcmp(arg, "auto") == 0) {
+ kvm_riscv_wfi_trap_policy = KVM_RISCV_WFI_AUTO;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+early_param("kvm-riscv.wfi_trap_policy", early_kvm_riscv_wfi_trap_policy_cfg);
+
+static bool kvm_riscv_vcpu_wfi_should_trap(struct kvm_vcpu *vcpu)
+{
+ switch (kvm_riscv_wfi_trap_policy) {
+ case KVM_RISCV_WFI_AUTO:
+ /* Native WFI only when the vCPU is the sole runnable task. */
+ return !single_task_running();
+ case KVM_RISCV_WFI_TRAP:
+ default:
+ return true;
+ }
+}
+
+static void kvm_riscv_vcpu_update_wfi_trap(struct kvm_vcpu *vcpu)
+{
+ struct kvm_cpu_context *cntx = &vcpu->arch.guest_context;
+
+ if (kvm_riscv_vcpu_wfi_should_trap(vcpu))
+ cntx->hstatus |= HSTATUS_VTW;
+ else
+ cntx->hstatus &= ~HSTATUS_VTW;
+}
+
const struct kvm_stats_desc kvm_vcpu_stats_desc[] = {
KVM_GENERIC_VCPU_STATS(),
STATS_DESC_COUNTER(VCPU, ecall_exit_stat),
@@ -73,6 +128,7 @@ static void kvm_riscv_vcpu_context_reset(struct kvm_vcpu *vcpu,
/* Setup reset state of shadow SSTATUS and HSTATUS CSRs */
cntx->sstatus = SR_SPP | SR_SPIE;
+ /* Trap VS-mode WFI by default; kvm_arch_vcpu_load() reapplies the policy. */
cntx->hstatus |= HSTATUS_VTW;
cntx->hstatus |= HSTATUS_SPVP;
cntx->hstatus |= HSTATUS_SPV;
@@ -609,6 +665,9 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
kvm_make_request(KVM_REQ_STEAL_UPDATE, vcpu);
+ /* Re-evaluate the WFI trap policy for this vCPU. */
+ kvm_riscv_vcpu_update_wfi_trap(vcpu);
+
vcpu->cpu = cpu;
}
--
2.34.1