Kernel KVM virtualization development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "slack up" <yxcnew@outlook.com>
Cc: kvm@vger.kernel.org
Subject: Re: [RFC PATCH 2/3] RISC-V: KVM: Preserve guest Vector state across vCPU put/load
Date: Tue, 27 Oct 2026 14:11:06 +0000	[thread overview]
Message-ID: <sashiko-outbox-186708@kernel.org> (raw)
In-Reply-To: <OSQPR01MB63262D568C71BC3510AAC4A4C2602@OSQPR01MB6326.apcprd01.prod.exchangelabs.com>

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Critical] RISC-V: KVM: missing vector owner invalidation on preempt_v start race
- [High] RISC-V: KVM: vector owner invalidation runs before enter_from_user_mode()
- [Low] RISC-V: KVM: missing kernel-parameters.txt entry for vector_owner_fastpath
--

commit f4e86411e0473662833880eb7f87be358e15d73e
Author: Xiaochun Yu <yxcnew@outlook.com>

RISC-V: KVM: Preserve guest Vector state across vCPU put/load

This commit introduces per-hart Vector owner tracking in RISC-V KVM so that a
vCPU reloaded on the same hart can restore only Vector control CSRs when its
Vector data registers remain resident in hardware. The optimization is gated
behind a read-only vector_owner_fastpath module parameter and hooks into the
generic RISC-V Vector context invalidation callback.

> diff --git a/arch/riscv/kvm/vcpu_vector.c b/arch/riscv/kvm/vcpu_vector.c
> index fefc2857f753a..c197d3d7b539d 100644
> --- a/arch/riscv/kvm/vcpu_vector.c
> +++ b/arch/riscv/kvm/vcpu_vector.c
> @@ -17,7 +19,50 @@
>  #include <asm/kvm_vcpu_vector.h>
>  #include <asm/vector.h>
>  
> +#include "trace.h"
> +
>  #ifdef CONFIG_RISCV_ISA_V

[ ... ]

> +static DEFINE_PER_CPU(struct kvm_riscv_vector_owner,
> +			      kvm_riscv_vector_owner);
> +static atomic64_t kvm_riscv_vector_owner_cookie = ATOMIC64_INIT(0);
> +static bool kvm_riscv_vector_owner_fastpath;
> +module_param_named(vector_owner_fastpath, kvm_riscv_vector_owner_fastpath,
> +		   bool, 0444);
> +MODULE_PARM_DESC(vector_owner_fastpath,
> +		 "Preserve matched guest Vector registers across vCPU put/load");

[Severity: Low]
Since vector_owner_fastpath is a read-only module parameter checked during
initialization in kvm_riscv_v_init() (and passed on the kernel command line
as kvm.vector_owner_fastpath when KVM is built-in), should this parameter
also be documented in Documentation/admin-guide/kernel-parameters.txt?

> +
> +bool kvm_riscv_vcpu_vector_owner_enabled(void)
> +{
> +	return READ_ONCE(kvm_riscv_vector_owner_fastpath);
> +}

[ ... ]

> +bool kvm_riscv_vcpu_vector_owner_load(struct kvm_vcpu *vcpu, int cpu)
> +{
> +	struct kvm_riscv_vector_owner *owner;
> +	unsigned long guest_vs = vcpu->arch.guest_context.sstatus & SR_VS;
> +	unsigned long host_vs = task_pt_regs(current)->status & SR_VS;
> +	enum kvm_riscv_vector_owner_reason reason;
> +	bool eligible = false;
> +
> +	if (!kvm_riscv_vcpu_vector_owner_enabled())
> +		return false;
> +
> +	owner = this_cpu_ptr(&kvm_riscv_vector_owner);
> +	if (guest_vs == SR_VS_OFF) {
> +		reason = KVM_RISCV_VECTOR_OWNER_GUEST_OFF;
> +	} else if (host_vs != SR_VS_OFF) {
> +		reason = KVM_RISCV_VECTOR_OWNER_HOST_ON;
> +	} else if (!owner->identity) {
> +		reason = vcpu->arch.last_exit_cpu == cpu ?
> +			 KVM_RISCV_VECTOR_OWNER_EMPTY :
> +			 KVM_RISCV_VECTOR_OWNER_MIGRATION;
> +	} else if (owner->identity != (unsigned long)vcpu) {
> +		reason = KVM_RISCV_VECTOR_OWNER_OTHER_VCPU;
> +	} else if (owner->cookie != vcpu->arch.vector_owner_cookie) {
> +		reason = KVM_RISCV_VECTOR_OWNER_COOKIE;
> +	} else if (owner->generation != vcpu->arch.vector_state_generation) {
> +		reason = KVM_RISCV_VECTOR_OWNER_GENERATION;
> +	} else {
> +		reason = KVM_RISCV_VECTOR_OWNER_HIT;
> +		eligible = true;
> +	}

[Severity: Critical]
Can a preemption window in riscv_v_start_kernel_context() allow a kernel
Vector user to clobber the hardware Vector registers without invalidating
kvm_riscv_vector_owner, causing kvm_riscv_vcpu_vector_owner_load() to
falsely report KVM_RISCV_VECTOR_OWNER_HIT?

With CONFIG_RISCV_ISA_V_PREEMPTIVE=y, kernel_vector_begin() calls
riscv_v_start_kernel_context() in arch/riscv/kernel/kernel_mode_vector.c:

	/* Transfer the ownership of V from user to kernel, then save */
	get_cpu_vector_context();
	__riscv_flush_vector_context();
	put_cpu_vector_context();
	/*
	 *  A voluntary context switch caused by put_cpu_vector_context() can
	 *  raise the NEED_RESTORE flag if preempt_v starts too early due to a
	 *  failed risv_v_is_on() check.
	 *
	 *  This causes the next context_nesting_end pollute the v-reg from
	 *  the stale context memory in kernel-mode vector.
	 */
	riscv_v_start(RISCV_PREEMPT_V);

__riscv_flush_vector_context() calls
riscv_v_owner_invalidate(RISCV_V_INVALIDATE_KERNEL_VECTOR) on the initial
hart, and then put_cpu_vector_context() clears RISCV_KERNEL_MODE_V and
re-enables bottom halves or preemption before RISCV_PREEMPT_V is set on
current->thread.riscv_v_flags.

If the task is preempted at put_cpu_vector_context() before
riscv_v_start(RISCV_PREEMPT_V) runs:
1. A vCPU thread can run on this hart and publish itself in
   kvm_riscv_vector_owner via kvm_arch_vcpu_put() ->
   kvm_riscv_vcpu_vector_owner_put(), or the preempted task can migrate to
   another hart that already has a published kvm_riscv_vector_owner.
2. When the task is scheduled back in, __switch_to_vector() in
   arch/riscv/include/asm/vector.h checks riscv_preempt_v_started(next):

	if (riscv_preempt_v_started(next)) {
		riscv_v_owner_invalidate(RISCV_V_INVALIDATE_KERNEL_VECTOR);
		...
	} else {
		riscv_v_vstate_set_restore(next, task_pt_regs(next));
	}

   Because RISCV_PREEMPT_V is not yet set, __switch_to_vector() skips
   riscv_v_owner_invalidate(RISCV_V_INVALIDATE_KERNEL_VECTOR).
3. The task then executes riscv_v_start(RISCV_PREEMPT_V), returns to
   kernel_vector_begin(), enables Vector, and overwrites v0..v31 with host
   kernel data while kvm_riscv_vector_owner on that hart still matches the
   vCPU.
4. When that vCPU reloads on the same hart,
   kvm_riscv_vcpu_vector_owner_load() sees a matching owner, cookie, and
   generation and requests RISCV_V_VCPU_CSR_RESTORE.
   kvm_riscv_vcpu_enter_exit() in arch/riscv/kvm/vcpu.c then restores only
   the Vector CSRs via __kvm_riscv_vector_restore_csrs() and enters the
   guest with host kernel Vector register contents in v0..v31.

> +
> +	trace_kvm_riscv_vector_owner(vcpu->vcpu_id, cpu, 0, eligible,
> +				     reason, host_vs, guest_vs,
> +				     vcpu->arch.vector_state_generation);

[ ... ]

> +void kvm_riscv_vcpu_vector_owner_invalidate(enum riscv_v_invalidate_reason reason)
> +{
> +	struct kvm_riscv_vector_owner *owner;
> +	struct kvm_vcpu *vcpu;
> +	enum kvm_riscv_vector_owner_reason trace_reason;
> +	unsigned long host_vs;
> +	u32 flags;

[ ... ]

> +	preempt_disable();
> +	flags = riscv_v_flags();
> +	if (flags & RISCV_V_VCPU_CSR_RESTORE) {
> +		vcpu = *this_cpu_ptr(kvm_get_running_vcpus());
> +		if (!WARN_ON_ONCE(!vcpu)) {
> +			flags &= ~RISCV_V_VCPU_CSR_RESTORE;
> +			flags |= RISCV_V_VCPU_NEED_RESTORE;
> +			riscv_v_flags_set(flags);
> +			host_vs = task_pt_regs(current)->status & SR_VS;
> +			trace_kvm_riscv_vector_owner(vcpu->vcpu_id,
> +						     smp_processor_id(), 2, false,
> +					     trace_reason, host_vs,
> +					     vcpu->arch.guest_context.sstatus & SR_VS,
> +					     vcpu->arch.vector_state_generation);
> +		}
> +	}
> +
> +	owner = this_cpu_ptr(&kvm_riscv_vector_owner);
> +	if (!owner->identity)
> +		goto out;
> +
> +	host_vs = task_pt_regs(current)->status & SR_VS;
> +	trace_kvm_riscv_vector_owner(owner->vcpu_id, smp_processor_id(), 2,
> +				     false, trace_reason, host_vs,
> +				     owner->guest_vs, owner->generation);
> +	owner->identity = 0;
> +	owner->cookie = 0;
> +	owner->generation = 0;
> +	owner->vcpu_id = 0;
> +	owner->guest_vs = 0;
> +
> +out:
> +	preempt_enable();
> +}

[Severity: High]
When kvm_riscv_v_init() in arch/riscv/include/asm/kvm_vcpu_vector.h registers
kvm_riscv_vcpu_vector_owner_invalidate(), can this callback and its RCU
dispatch in __riscv_v_owner_invalidate() run from do_trap_ecall_u() before
enter_from_user_mode() transitions RCU and lockdep out of user mode?

In arch/riscv/kernel/traps.c, do_trap_ecall_u() is a noinstr trap handler
that calls riscv_v_vstate_discard(regs) before entering the kernel context:

	if (user_mode(regs)) {
		long syscall = regs->a7;

		regs->epc += 4;
		regs->orig_a0 = regs->a0;
		regs->a0 = -ENOSYS;

		riscv_v_vstate_discard(regs);

		if (likely(syscall_enter_from_user_mode_randomize_stack(regs, &syscall))) {

When a user task with active Vector state issues a syscall,
riscv_v_vstate_discard(regs) in arch/riscv/include/asm/vector.h calls
riscv_v_owner_invalidate(RISCV_V_INVALIDATE_TASK_DISCARD) ->
__riscv_v_owner_invalidate() in arch/riscv/kernel/kernel_mode_vector.c:

	rcu_read_lock();
	invalidate_callback = rcu_dereference(kvm_invalidate_vector_ctx_callback);
	if (invalidate_callback)
		invalidate_callback(reason);
	rcu_read_unlock();

Because enter_from_user_mode() is only invoked later inside
syscall_enter_from_user_mode_randomize_stack(), on configurations with
CONFIG_CONTEXT_TRACKING_USER=y (such as NO_HZ_FULL), RCU is still in
CT_STATE_USER (!rcu_is_watching()) and lockdep_hardirqs_off() has not run
yet. Calling rcu_read_lock(), rcu_dereference(), preempt_disable(), and
trace_kvm_riscv_vector_owner() here will trigger RCU and tracepoint lockdep
warnings and leave the callback invocation unprotected by synchronize_rcu()
during kvm_riscv_v_exit().

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/OSQPR01MB63265385597FE472C4209353C2602@OSQPR01MB6326.apcprd01.prod.exchangelabs.com?part=2

  reply	other threads:[~2026-10-27 14:11 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-27 13:13 [RFC PATCH 0/3] RISC-V: KVM: Preserve resident guest Vector state slack up
2026-10-27 13:13 ` [RFC PATCH 1/3] riscv: vector: Add a guest owner invalidation hook slack up
2026-10-27 14:03   ` sashiko-bot
2026-10-27 13:13 ` [RFC PATCH 2/3] RISC-V: KVM: Preserve guest Vector state across vCPU put/load slack up
2026-10-27 14:11   ` sashiko-bot [this message]
2026-10-27 13:13 ` [RFC PATCH 3/3] KVM: riscv: selftests: Add Vector owner regression tests slack up
2026-10-27 14:03   ` sashiko-bot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-186708@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=kvm@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=yxcnew@outlook.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox