[PATCH v3] RISC-V: KVM: Add kvm-riscv.wfi_trap_policy to control VS-mode WFI trapping

Anup Patel anup at brainfault.org
Fri Oct 2 21:41:40 PDT 2026


On Wed, Aug 5, 2026 at 12:22 PM Yuhang.Chen <yhchen312 at gmail.com> wrote:
>
> 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 before each guest entry 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. The policy is re-evaluated before every guest entry,
>           so when another task becomes runnable the next entry traps
>           again and KVM blocks the vCPU through kvm_vcpu_halt(), yielding
>           the CPU. 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 <zhouquan at iscas.ac.cn>
> Signed-off-by: Quan Zhou <zhouquan at iscas.ac.cn>
> Signed-off-by: Yuhang.Chen <yhchen312 at gmail.com>
> Reported-by: kernel test robot <lkp at intel.com>
> Closes: https://lore.kernel.org/oe-kbuild-all/202608051354.KYOXP5po-lkp@intel.com/
>
> ---
> Changes in v3:
>
> - Guard the early_param() parser with #ifndef MODULE. RISC-V KVM is
>   tristate and may be built as a module (CONFIG_KVM=m), but
>   early_param() is only defined for built-in code, so v2 failed to
>   compile as a module. With CONFIG_KVM=m the policy now keeps its
>   default (trap) value, which is safe and regression-free; the rest of
>   the policy logic is unaffected. The kernel-parameters.txt entry now
>   documents that the option is only honored when KVM is built in.
>
> - Re-evaluate the WFI trap policy before each guest entry instead of
>   only in kvm_arch_vcpu_load(). In v2 the policy was checked once when
>   the vCPU was loaded, so between two kvm_arch_vcpu_load() calls
>   HSTATUS.VTW could go stale: if a task of lower or equal priority woke
>   on the vCPU's CPU while the guest was running, the run loop would
>   re-enter the guest without re-checking and without preempting, and a
>   native WFI could then halt the hart and delay the woken task until the
>   next timer tick. Checking before every entry closes this window,
>   because a wakeup arrives through a host interrupt that forces a VM
>   exit, and the next entry re-evaluates and traps.
>
> 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/20260709115610.287420-1-yhchen312@gmail.com/
> v2: https://lore.kernel.org/all/20260804024707.2400404-1-yhchen312@gmail.com/
> ---
>  .../admin-guide/kernel-parameters.txt         | 20 ++++++
>  arch/riscv/kvm/vcpu.c                         | 72 +++++++++++++++++++
>  2 files changed, 92 insertions(+)
>
> diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
> index b5493a7f8f22..2ab9eb335e60 100644
> --- a/Documentation/admin-guide/kernel-parameters.txt
> +++ b/Documentation/admin-guide/kernel-parameters.txt
> @@ -3254,6 +3254,26 @@ 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 before each guest entry, 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.
> +
> +                       Only honored when KVM is built into the kernel
> +                       (CONFIG_KVM=y); as a module (CONFIG_KVM=m) the policy
> +                       stays at its default (trap).
> +
>         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..8efeb74699cc 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,67 @@
>
>  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;
> +
> +/*
> + * RISC-V KVM is tristate and may be built as a module, but early_param() is
> + * only defined for built-in code (see <linux/init.h>). Guard the command-line
> + * parser accordingly: when CONFIG_KVM=m the policy simply keeps its default
> + * (trap) value, which is the safe, regression-free behavior.
> + */
> +#ifndef MODULE
> +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);

FYI, module parameters can also be passed as kernel parameter when the
module is built-in. There is no reason why WFI trap-policy will not work as when
KVM RISC-V is a plugabble module.

Convert this to module_param.

> +#endif
> +
> +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 +136,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; the run loop reapplies the policy before each entry. */
>         cntx->hstatus |= HSTATUS_VTW;
>         cntx->hstatus |= HSTATUS_SPVP;
>         cntx->hstatus |= HSTATUS_SPV;
> @@ -936,6 +1000,14 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
>                  */
>                 kvm_riscv_local_tlb_sanitize(vcpu);
>
> +               /*
> +                * Re-evaluate the WFI trap policy for this entry so that
> +                * HSTATUS.VTW tracks the current runnable-task count and
> +                * cannot go stale across guest entries (which would let a
> +                * native WFI halt the CPU while another task is runnable).
> +                */
> +               kvm_riscv_vcpu_update_wfi_trap(vcpu);
> +
>                 trace_kvm_entry(vcpu);
>
>                 guest_timing_enter_irqoff();
> --
> 2.34.1
>

Regards,
Anup



More information about the linux-riscv mailing list