Re: [PATCH v4 3/3] RISC-V: KVM: fix vcpu vector context handling for kernel-mode vector
Yong-Xuan Wang <[email protected]>
| Newsgroups | org.infradead.lists.linux-riscv,org.infradead.lists.kvm-riscv,org.kernel.vger.kvm |
|---|---|
| Message-ID | <CAMWQL2jkVDVBw=_pecSR42RjooLoPevPP5ZEzwyz_m9YBc_PTQ@mail.gmail.com> |
On Sat, Jul 25, 2026 at 8:18 AM Andy Chiu <[email protected]> wrote: > > Running vector workloads like perf + mcf on KVM can result in an > unexpected termination due to a vtype corruption. This happens because > the kernel-mode vector (KMV) misattributes the guest's vcpu context as > the user's context and source from a wrong status.VS. > > The simplified call chain that results in this problem is shown as > follow: > > __riscv_sys_ioctl() > kvm_arch_vcpu_ioctl_run() > kvm_riscv_vcpu_exit() > kvm_riscv_vcpu_sbi_ecall() > kvm_riscv_vcpu_pmu_ctr_stop() > kvm_vcpu_write_guest() > __copy_to_user() > enter_vector_usercopy() > kernel_vector_begin() > > kernel_vector_begin() should use the sstatus.VS from guest's vcpu > context instead of task_pt_reg(current). Also, it should not save > guest's v-reg into the user's context memory. > > To resolve this, the vcpu context must be correctly saved when KMV is > serving a guest. However, invoking KVM functions directly from generic > RISC-V architecture code introduces a reverse dependency, breaking > builds when KVM is configured as N or M. > > Address this by registering an RCU-protected callback for context > flushing. KVM registers this callback at module initialization and > unregisters it on exit. When KMV starts a kernel context, it can now > safely flush the vector context via the callback. > > Fixes: ecd2ada8a5e0 ("riscv: Add support for kernel mode vector") > Signed-off-by: Andy Chiu <[email protected]> Reviewed-by: Yong-Xuan Wang <[email protected]> Thank you! Yong-Xuan > > --- > Changelog v3: > - clears RISCV_V_VCPU_NEED_RESTORE flag in host restore to prevent > leaking (Sashiko) > - consolidates guest vector restore at returning to guest to prevent > unnecessary save/restore between the preemptible window from > vcpu_load to vcpu_enter_exit > - Document the added riscv_v_flags > Changelog v2: address concerns pointed out by sashiko > - encloses vcpu_flush_v_callback() with rcu_read_{lock,unlock} > - put riscv_v_start before put_cpu_vector_context() to prevent > redundant context save > - protect v context operations against softirqs > - apply bitmask when reading SR_VS out of guest's sstatus > --- > arch/riscv/include/asm/kvm_vcpu_vector.h | 24 +++++++++++ > arch/riscv/include/asm/processor.h | 8 ++++ > arch/riscv/include/asm/vector.h | 5 +++ > arch/riscv/kernel/kernel_mode_vector.c | 51 ++++++++++++++++++------ > arch/riscv/kvm/main.c | 4 ++ > arch/riscv/kvm/vcpu.c | 12 ++++++ > arch/riscv/kvm/vcpu_vector.c | 22 +++++++++- > 7 files changed, 112 insertions(+), 14 deletions(-) > > diff --git a/arch/riscv/include/asm/kvm_vcpu_vector.h b/arch/riscv/include/asm/kvm_vcpu_vector.h > index 57a798a4cb0d..6371d5ea5392 100644 > --- a/arch/riscv/include/asm/kvm_vcpu_vector.h > +++ b/arch/riscv/include/asm/kvm_vcpu_vector.h > @@ -35,6 +35,22 @@ void kvm_riscv_vcpu_host_vector_save(struct kvm_cpu_context *cntx); > void kvm_riscv_vcpu_host_vector_restore(struct kvm_cpu_context *cntx); > int kvm_riscv_vcpu_alloc_vector_context(struct kvm_vcpu *vcpu); > void kvm_riscv_vcpu_free_vector_context(struct kvm_vcpu *vcpu); > +void kvm_riscv_register_vctx_callback(void (*func)(void)); > +void kvm_riscv_unregister_vctx_callback(void); > +void kvm_riscv_vcpu_flush_vector(void); > + > +static inline void kvm_riscv_v_init(void) > +{ > + if (has_vector()) > + kvm_riscv_register_vctx_callback(&kvm_riscv_vcpu_flush_vector); > +} > + > +static inline void kvm_riscv_v_exit(void) > +{ > + if (has_vector()) > + kvm_riscv_unregister_vctx_callback(); > +} > + > #else > > struct kvm_cpu_context; > @@ -69,6 +85,14 @@ static inline int kvm_riscv_vcpu_alloc_vector_context(struct kvm_vcpu *vcpu) > static inline void kvm_riscv_vcpu_free_vector_context(struct kvm_vcpu *vcpu) > { > } > + > +static inline void kvm_riscv_v_init(void) > +{ > +} > + > +static inline void kvm_riscv_v_exit(void) > +{ > +} > #endif > > int kvm_riscv_vcpu_get_reg_vector(struct kvm_vcpu *vcpu, > diff --git a/arch/riscv/include/asm/processor.h b/arch/riscv/include/asm/processor.h > index 812517b2cec1..a6a0c3d5a913 100644 > --- a/arch/riscv/include/asm/processor.h > +++ b/arch/riscv/include/asm/processor.h > @@ -67,6 +67,12 @@ struct pt_regs; > * - bit 0: indicates whether the in-kernel Vector context is active. The > * activation of this state disables the preemption. On a non-RT kernel, it > * also disable bh. > + * - bit 1: tells kvm that the vcpu process has guest context saved in vcpu's > + * context memory and need to be restore upon returing back to the guest. > + * - bit 2: represents that the vector context has now loaded and belongs to > + * the guest kernel. Any non-scheduler context saving routing needs to save > + * the register file to vcpu's context memory. The bit is set upon returing > + * back to the guest and cleared after loading the host's vector context. > * - bits 8: is used for tracking preemptible kernel-mode Vector, when > * RISCV_ISA_V_PREEMPTIVE is enabled. Calling kernel_vector_begin() does not > * disable the preemption if the thread's kernel_vstate.datap is allocated. > @@ -97,6 +103,8 @@ struct pt_regs; > > #define RISCV_V_CTX_UNIT_DEPTH 0x00010000 > #define RISCV_KERNEL_MODE_V 0x00000001 > +#define RISCV_V_VCPU_NEED_RESTORE 0x00000002 > +#define RISCV_V_VCPU_CTX 0x00000004 > #define RISCV_PREEMPT_V 0x00000100 > #define RISCV_PREEMPT_V_DIRTY 0x80000000 > #define RISCV_PREEMPT_V_NEED_RESTORE 0x40000000 > diff --git a/arch/riscv/include/asm/vector.h b/arch/riscv/include/asm/vector.h > index 00cb9c0982b1..fffe72a77208 100644 > --- a/arch/riscv/include/asm/vector.h > +++ b/arch/riscv/include/asm/vector.h > @@ -58,6 +58,11 @@ static inline u32 riscv_v_flags(void) > return READ_ONCE(current->thread.riscv_v_flags); > } > > +static inline void riscv_v_flags_set(u32 flags) > +{ > + WRITE_ONCE(current->thread.riscv_v_flags, flags); > +} > + > static __always_inline bool has_vector(void) > { > return riscv_has_extension_unlikely(RISCV_ISA_EXT_ZVE32X); > diff --git a/arch/riscv/kernel/kernel_mode_vector.c b/arch/riscv/kernel/kernel_mode_vector.c > index 965c8edbe984..da6ebc4dffdb 100644 > --- a/arch/riscv/kernel/kernel_mode_vector.c > +++ b/arch/riscv/kernel/kernel_mode_vector.c > @@ -13,16 +13,31 @@ > #include <linux/kvm_types.h> > > #include <asm/vector.h> > +#include <asm/kvm_vcpu_vector.h> > #include <asm/switch_to.h> > #include <asm/simd.h> > #ifdef CONFIG_RISCV_ISA_V_PREEMPTIVE > #include <asm/asm-prototypes.h> > #endif > > -static inline void riscv_v_flags_set(u32 flags) > +static void (* __rcu kvm_flush_vector_ctx_callback)(void); > + > +void kvm_riscv_register_vctx_callback(void (*func)(void)) > { > - WRITE_ONCE(current->thread.riscv_v_flags, flags); > + if (WARN_ON_ONCE(rcu_access_pointer(kvm_flush_vector_ctx_callback))) > + return; > + > + rcu_assign_pointer(kvm_flush_vector_ctx_callback, func); > } > +EXPORT_SYMBOL_GPL(kvm_riscv_register_vctx_callback); > + > +void kvm_riscv_unregister_vctx_callback(void) > +{ > + rcu_assign_pointer(kvm_flush_vector_ctx_callback, NULL); > + synchronize_rcu(); > +} > +EXPORT_SYMBOL_GPL(kvm_riscv_unregister_vctx_callback); > + > > static inline void riscv_v_start(u32 flags) > { > @@ -87,6 +102,22 @@ void put_cpu_vector_context(void) > } > EXPORT_SYMBOL_FOR_KVM(put_cpu_vector_context); > > +static void __riscv_flush_vector_context(void) > +{ > + void (*vcpu_flush_v_callback)(void); > + > + if (riscv_v_flags() & RISCV_V_VCPU_CTX) { > + rcu_read_lock(); > + vcpu_flush_v_callback = rcu_dereference(kvm_flush_vector_ctx_callback); > + vcpu_flush_v_callback(); > + rcu_read_unlock(); > + return; > + } > + > + riscv_v_vstate_save(¤t->thread.vstate, task_pt_regs(current)); > + riscv_v_vstate_set_restore(current, task_pt_regs(current)); > +} > + > #ifdef CONFIG_RISCV_ISA_V_PREEMPTIVE > static __always_inline u32 *riscv_v_flags_ptr(void) > { > @@ -130,7 +161,7 @@ static int riscv_v_stop_kernel_context(void) > > static int riscv_v_start_kernel_context(void) > { > - struct __riscv_v_ext_state *kvstate, *uvstate; > + struct __riscv_v_ext_state *kvstate; > > kvstate = ¤t->thread.kernel_vstate; > if (!kvstate->datap) > @@ -148,13 +179,10 @@ static int riscv_v_start_kernel_context(void) > } > > /* Transfer the ownership of V from user to kernel, then save */ > - riscv_v_start(RISCV_PREEMPT_V | RISCV_PREEMPT_V_DIRTY); > - if (__riscv_v_vstate_check(task_pt_regs(current)->status, DIRTY)) { > - uvstate = ¤t->thread.vstate; > - __riscv_v_vstate_save(uvstate, uvstate->datap); > - } > - riscv_preempt_v_clear_dirty(current); > - riscv_v_vstate_set_restore(current, task_pt_regs(current)); > + get_cpu_vector_context(); > + __riscv_flush_vector_context(); > + riscv_v_start(RISCV_PREEMPT_V); > + put_cpu_vector_context(); > return 0; > } > > @@ -220,8 +248,7 @@ void kernel_vector_begin(void) > > if (riscv_v_start_kernel_context()) { > get_cpu_vector_context(); > - riscv_v_vstate_save(¤t->thread.vstate, task_pt_regs(current)); > - riscv_v_vstate_set_restore(current, task_pt_regs(current)); > + __riscv_flush_vector_context(); > } > > riscv_v_enable(); > diff --git a/arch/riscv/kvm/main.c b/arch/riscv/kvm/main.c > index 0924c75100a2..da8a5f6d4b4d 100644 > --- a/arch/riscv/kvm/main.c > +++ b/arch/riscv/kvm/main.c > @@ -14,6 +14,7 @@ > #include <asm/kvm_mmu.h> > #include <asm/kvm_nacl.h> > #include <asm/sbi.h> > +#include <asm/kvm_vcpu_vector.h> > > DEFINE_STATIC_KEY_FALSE(kvm_riscv_vsstage_tlb_no_gpa); > > @@ -76,6 +77,7 @@ static void kvm_riscv_teardown(void) > { > kvm_riscv_aia_exit(); > kvm_riscv_nacl_exit(); > + kvm_riscv_v_exit(); > kvm_unregister_perf_callbacks(); > } > > @@ -170,6 +172,8 @@ static int __init riscv_kvm_init(void) > > kvm_riscv_setup_vendor_features(); > > + kvm_riscv_v_init(); > + > kvm_register_perf_callbacks(); > > rc = kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE); > diff --git a/arch/riscv/kvm/vcpu.c b/arch/riscv/kvm/vcpu.c > index cf6e231e76e2..e2651808e1d3 100644 > --- a/arch/riscv/kvm/vcpu.c > +++ b/arch/riscv/kvm/vcpu.c > @@ -603,9 +603,11 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) > kvm_riscv_vcpu_host_fp_save(&vcpu->arch.host_context); > kvm_riscv_vcpu_guest_fp_restore(&vcpu->arch.guest_context, > vcpu->arch.isa); > + get_cpu_vector_context(); > kvm_riscv_vcpu_host_vector_save(&vcpu->arch.host_context); > kvm_riscv_vcpu_guest_vector_restore(&vcpu->arch.guest_context, > vcpu->arch.isa); > + put_cpu_vector_context(); > > kvm_make_request(KVM_REQ_STEAL_UPDATE, vcpu); > > @@ -626,9 +628,11 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) > kvm_riscv_vcpu_host_fp_restore(&vcpu->arch.host_context); > > kvm_riscv_vcpu_timer_save(vcpu); > + get_cpu_vector_context(); > kvm_riscv_vcpu_guest_vector_save(&vcpu->arch.guest_context, > vcpu->arch.isa); > kvm_riscv_vcpu_host_vector_restore(&vcpu->arch.host_context); > + put_cpu_vector_context(); > > if (kvm_riscv_nacl_available()) { > nsh = nacl_shmem(); > @@ -765,6 +769,14 @@ static void noinstr kvm_riscv_vcpu_enter_exit(struct kvm_vcpu *vcpu, > kvm_riscv_vcpu_swap_in_guest_state(vcpu); > guest_state_enter_irqoff(); > > + /* sstatus.VS != SR_VS_OFF is guaranteed when NEED_RESTORE is set */ > + if (current->thread.riscv_v_flags & RISCV_V_VCPU_NEED_RESTORE) { > + current->thread.riscv_v_flags &= ~RISCV_V_VCPU_NEED_RESTORE; > + current->thread.riscv_v_flags |= RISCV_V_VCPU_CTX; > + __kvm_riscv_vector_restore(gcntx); > + gcntx->sstatus = (gcntx->sstatus & ~SR_VS) | SR_VS_CLEAN; > + } > + > if (kvm_riscv_nacl_sync_sret_available()) { > nsh = nacl_shmem(); > > diff --git a/arch/riscv/kvm/vcpu_vector.c b/arch/riscv/kvm/vcpu_vector.c > index 62d2fb77bb9b..ef2eee6ec308 100644 > --- a/arch/riscv/kvm/vcpu_vector.c > +++ b/arch/riscv/kvm/vcpu_vector.c > @@ -56,8 +56,7 @@ void kvm_riscv_vcpu_guest_vector_restore(struct kvm_cpu_context *cntx, > { > if ((cntx->sstatus & SR_VS) != SR_VS_OFF) { > if (riscv_isa_extension_available(isa, v)) > - __kvm_riscv_vector_restore(cntx); > - kvm_riscv_vcpu_vector_clean(cntx); > + riscv_v_flags_set(riscv_v_flags() | RISCV_V_VCPU_NEED_RESTORE); > } > } > > @@ -72,6 +71,7 @@ void kvm_riscv_vcpu_host_vector_restore(struct kvm_cpu_context *cntx) > { > if (!kvm_riscv_isa_check_host(V)) > __kvm_riscv_vector_restore(cntx); > + riscv_v_flags_set(riscv_v_flags() & ~(RISCV_V_VCPU_CTX | RISCV_V_VCPU_NEED_RESTORE)); > } > > int kvm_riscv_vcpu_alloc_vector_context(struct kvm_vcpu *vcpu) > @@ -95,6 +95,24 @@ void kvm_riscv_vcpu_free_vector_context(struct kvm_vcpu *vcpu) > kfree(vcpu->arch.guest_context.vector.datap); > kfree(vcpu->arch.host_context.vector.datap); > } > + > +void kvm_riscv_vcpu_flush_vector(void) > +{ > + struct kvm_vcpu *vcpu = *this_cpu_ptr(kvm_get_running_vcpus()); > + > + /* > + * Only reached from __riscv_flush_vector_context() when RISCV_V_VCPU_CTX is set, which > + * always have kvm_get_running_vcpus non-NULL. > + */ > + if (WARN_ON_ONCE(!vcpu)) > + return; > + > + kvm_riscv_vcpu_guest_vector_save(&vcpu->arch.guest_context, vcpu->arch.isa); > + > + if ((vcpu->arch.guest_context.sstatus & SR_VS) != SR_VS_OFF) > + riscv_v_flags_set(riscv_v_flags() | RISCV_V_VCPU_NEED_RESTORE); > +} > + > #endif > > static int kvm_riscv_vcpu_vreg_addr(struct kvm_vcpu *vcpu, > -- > 2.43.0 > _______________________________________________ linux-riscv mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-riscv