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(&current->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 = &current->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 = &current->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(&current->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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.