[RFC PATCH 2/3] RISC-V: KVM: Preserve guest Vector state across vCPU put/load

slack up yxcnew at outlook.com
Tue Oct 27 06:13:08 PDT 2026


From: Xiaochun Yu <yxcnew at outlook.com>

kvm_arch_vcpu_put() saves the guest Vector state and restores the host
register file. A later load on the same hart saves the host again and
requests a full guest restore, even when the host has not used Vector in
between.

Track a per-hart owner with an opaque vCPU identity, an allocation cookie
and a state generation. After saving a dirty guest to memory, leave its
data registers resident when the host has no Vector restore obligation. A
matching load restores only the Vector control CSRs. All mismatches and
generic Vector users fall back to a full restore.

Keep guest memory authoritative by continuing to save at every dirty put.
Advance the generation on saves, resets and SET_ONE_REG. Do so before
copy_from_user() because a fault can leave a modified prefix in the
destination even though the ioctl fails.

Register the generic invalidation hook only when the read-only
vector_owner_fastpath module parameter is enabled. The parameter defaults
to false so the existing full-restore behavior remains the default. Add
tracepoints for restore mode, owner decisions and invalidation reasons.

Development note: OpenAI Codex assisted with source analysis,
implementation drafting, test orchestration and changelog preparation.

Signed-off-by: Xiaochun Yu <yxcnew at outlook.com>
---
 arch/riscv/include/asm/kvm_host.h        |   4 +
 arch/riscv/include/asm/kvm_vcpu_vector.h |  54 ++++-
 arch/riscv/include/asm/processor.h       |   1 +
 arch/riscv/kvm/trace.h                   |  98 +++++++++
 arch/riscv/kvm/vcpu.c                    |  54 ++++-
 arch/riscv/kvm/vcpu_vector.c             | 252 ++++++++++++++++++++++-
 6 files changed, 439 insertions(+), 24 deletions(-)

diff --git a/arch/riscv/include/asm/kvm_host.h b/arch/riscv/include/asm/kvm_host.h
index e28f058a94d7..a6e2b3097644 100644
--- a/arch/riscv/include/asm/kvm_host.h
+++ b/arch/riscv/include/asm/kvm_host.h
@@ -204,6 +204,10 @@ struct kvm_vcpu_arch {
 	/* CPU context of Guest VCPU */
 	struct kvm_cpu_context guest_context;
 
+	/* Identity and version used by Vector owner tracking. */
+	u64 vector_owner_cookie;
+	u64 vector_state_generation;
+
 	/* CPU CSR context of Guest VCPU */
 	struct kvm_vcpu_csr guest_csr;
 
diff --git a/arch/riscv/include/asm/kvm_vcpu_vector.h b/arch/riscv/include/asm/kvm_vcpu_vector.h
index ebc619ed67dd..7f3eecf4217b 100644
--- a/arch/riscv/include/asm/kvm_vcpu_vector.h
+++ b/arch/riscv/include/asm/kvm_vcpu_vector.h
@@ -26,29 +26,47 @@ static __always_inline void __kvm_riscv_vector_restore(struct kvm_cpu_context *c
 	__riscv_v_vstate_restore(&context->vector, context->vector.datap);
 }
 
+static __always_inline void __kvm_riscv_vector_restore_csrs(struct kvm_cpu_context *context)
+{
+	riscv_v_enable();
+	__vstate_csr_restore(&context->vector);
+	riscv_v_disable();
+}
+
 void kvm_riscv_vcpu_vector_reset(struct kvm_vcpu *vcpu);
 void kvm_riscv_vcpu_guest_vector_save(struct kvm_cpu_context *cntx,
 				      unsigned long *isa);
 void kvm_riscv_vcpu_guest_vector_restore(struct kvm_cpu_context *cntx,
-					 unsigned long *isa);
+					 unsigned long *isa, bool csr_only);
 void kvm_riscv_vcpu_host_vector_save(struct kvm_cpu_context *cntx);
-void kvm_riscv_vcpu_host_vector_restore(struct kvm_cpu_context *cntx);
+void kvm_riscv_vcpu_host_vector_restore(struct kvm_cpu_context *cntx,
+					bool preserve_guest);
 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);
+bool kvm_riscv_vcpu_vector_owner_enabled(void);
+bool kvm_riscv_vcpu_vector_owner_load(struct kvm_vcpu *vcpu, int cpu);
+bool kvm_riscv_vcpu_vector_owner_put(struct kvm_vcpu *vcpu);
+void kvm_riscv_vcpu_vector_owner_invalidate(enum riscv_v_invalidate_reason reason);
 void kvm_riscv_vcpu_flush_vector(void);
 
 static inline void kvm_riscv_v_init(void)
 {
-	if (has_vector())
+	if (has_vector()) {
 		kvm_riscv_register_vctx_callback(&kvm_riscv_vcpu_flush_vector);
+		if (kvm_riscv_vcpu_vector_owner_enabled())
+			kvm_riscv_register_vctx_invalidate(&kvm_riscv_vcpu_vector_owner_invalidate);
+	}
 }
 
 static inline void kvm_riscv_v_exit(void)
 {
-	if (has_vector())
+	if (has_vector()) {
+		if (kvm_riscv_vcpu_vector_owner_enabled())
+			kvm_riscv_unregister_vctx_invalidate();
 		kvm_riscv_unregister_vctx_callback();
+	}
 }
 
 #else
@@ -59,6 +77,10 @@ static inline void __kvm_riscv_vector_restore(struct kvm_cpu_context *context)
 {
 }
 
+static inline void __kvm_riscv_vector_restore_csrs(struct kvm_cpu_context *context)
+{
+}
+
 static inline void kvm_riscv_vcpu_vector_reset(struct kvm_vcpu *vcpu)
 {
 }
@@ -69,7 +91,7 @@ static inline void kvm_riscv_vcpu_guest_vector_save(struct kvm_cpu_context *cntx
 }
 
 static inline void kvm_riscv_vcpu_guest_vector_restore(struct kvm_cpu_context *cntx,
-						       unsigned long *isa)
+					       unsigned long *isa, bool csr_only)
 {
 }
 
@@ -77,7 +99,8 @@ static inline void kvm_riscv_vcpu_host_vector_save(struct kvm_cpu_context *cntx)
 {
 }
 
-static inline void kvm_riscv_vcpu_host_vector_restore(struct kvm_cpu_context *cntx)
+static inline void
+kvm_riscv_vcpu_host_vector_restore(struct kvm_cpu_context *cntx, bool preserve_guest)
 {
 }
 
@@ -90,6 +113,25 @@ static inline void kvm_riscv_vcpu_free_vector_context(struct kvm_vcpu *vcpu)
 {
 }
 
+static inline bool kvm_riscv_vcpu_vector_owner_enabled(void)
+{
+	return false;
+}
+
+static inline bool kvm_riscv_vcpu_vector_owner_load(struct kvm_vcpu *vcpu, int cpu)
+{
+	return false;
+}
+
+static inline bool kvm_riscv_vcpu_vector_owner_put(struct kvm_vcpu *vcpu)
+{
+	return false;
+}
+
+static inline void kvm_riscv_vcpu_vector_owner_invalidate(enum riscv_v_invalidate_reason reason)
+{
+}
+
 static inline void kvm_riscv_v_init(void)
 {
 }
diff --git a/arch/riscv/include/asm/processor.h b/arch/riscv/include/asm/processor.h
index 815715c67f94..abda84ca9548 100644
--- a/arch/riscv/include/asm/processor.h
+++ b/arch/riscv/include/asm/processor.h
@@ -105,6 +105,7 @@ struct pt_regs;
 #define RISCV_KERNEL_MODE_V		0x00000001
 #define RISCV_V_VCPU_NEED_RESTORE	0x00000002
 #define RISCV_V_VCPU_CTX		0x00000004
+#define RISCV_V_VCPU_CSR_RESTORE	0x00000008
 #define RISCV_PREEMPT_V			0x00000100
 #define RISCV_PREEMPT_V_DIRTY		0x80000000
 #define RISCV_PREEMPT_V_NEED_RESTORE	0x40000000
diff --git a/arch/riscv/kvm/trace.h b/arch/riscv/kvm/trace.h
index 23a5fd1b686e..c805606c704d 100644
--- a/arch/riscv/kvm/trace.h
+++ b/arch/riscv/kvm/trace.h
@@ -133,6 +133,104 @@ TRACE_EVENT(kvm_vcpu_irq,
 		  __entry->vcpu_id, __entry->irq, __entry->level)
 );
 
+TRACE_EVENT(kvm_riscv_vector_restore_request,
+	TP_PROTO(unsigned long vcpu_id, int cpu, unsigned int source,
+		 unsigned long host_vs),
+	TP_ARGS(vcpu_id, cpu, source, host_vs),
+
+	TP_STRUCT__entry(
+		__field(unsigned long, vcpu_id)
+		__field(int, cpu)
+		__field(unsigned int, source)
+		__field(unsigned long, host_vs)
+	),
+
+	TP_fast_assign(
+		__entry->vcpu_id	= vcpu_id;
+		__entry->cpu		= cpu;
+		__entry->source		= source;
+		__entry->host_vs	= host_vs;
+	),
+
+	TP_printk("VCPU: %lu, CPU: %d, source: %s, host VS: 0x%lx",
+		  __entry->vcpu_id, __entry->cpu,
+		  __print_symbolic(__entry->source,
+				   { 0, "load" }, { 1, "kernel_vector" }),
+		  __entry->host_vs)
+);
+
+TRACE_EVENT(kvm_riscv_vector_restore,
+	TP_PROTO(unsigned long vcpu_id, int cpu, bool csr_only),
+	TP_ARGS(vcpu_id, cpu, csr_only),
+
+	TP_STRUCT__entry(
+		__field(unsigned long, vcpu_id)
+		__field(int, cpu)
+		__field(bool, csr_only)
+	),
+
+	TP_fast_assign(
+		__entry->vcpu_id	= vcpu_id;
+		__entry->cpu		= cpu;
+		__entry->csr_only	= csr_only;
+	),
+
+	TP_printk("VCPU: %lu, CPU: %d, mode: %s",
+		  __entry->vcpu_id, __entry->cpu,
+		  __entry->csr_only ? "csr" : "full")
+);
+
+TRACE_EVENT(kvm_riscv_vector_owner,
+	TP_PROTO(unsigned long vcpu_id, int cpu, unsigned int action,
+		 bool eligible, unsigned int reason, unsigned long host_vs,
+		 unsigned long guest_vs, u64 generation),
+	TP_ARGS(vcpu_id, cpu, action, eligible, reason, host_vs, guest_vs,
+		generation),
+
+	TP_STRUCT__entry(
+		__field(unsigned long, vcpu_id)
+		__field(int, cpu)
+		__field(unsigned int, action)
+		__field(bool, eligible)
+		__field(unsigned int, reason)
+		__field(unsigned long, host_vs)
+		__field(unsigned long, guest_vs)
+		__field(u64, generation)
+	),
+
+	TP_fast_assign(
+		__entry->vcpu_id	= vcpu_id;
+		__entry->cpu		= cpu;
+		__entry->action		= action;
+		__entry->eligible	= eligible;
+		__entry->reason		= reason;
+		__entry->host_vs	= host_vs;
+		__entry->guest_vs	= guest_vs;
+		__entry->generation	= generation;
+	),
+
+	TP_printk("VCPU: %lu, CPU: %d, action: %s, eligible: %d, reason: %s, host VS: 0x%lx, guest VS: 0x%lx, generation: %llu",
+		  __entry->vcpu_id, __entry->cpu,
+		  __print_symbolic(__entry->action,
+				   { 0, "load" }, { 1, "put" },
+				   { 2, "invalidate" }),
+		  __entry->eligible,
+		  __print_symbolic(__entry->reason,
+				   { 0, "hit" }, { 1, "empty" },
+				   { 2, "other_vcpu" }, { 3, "cookie" },
+				   { 4, "generation" }, { 5, "guest_off" },
+				   { 6, "host_on" }, { 7, "migration" },
+				   { 8, "put_candidate" },
+				   { 9, "task_save" },
+				   { 10, "task_restore" },
+				   { 11, "task_discard" },
+				   { 12, "kernel_vector" },
+				   { 13, "cpu_state" },
+				   { 14, "not_resident" }),
+		  __entry->host_vs, __entry->guest_vs,
+		  __entry->generation)
+);
+
 #endif /* _TRACE_KVM_H */
 
 #undef TRACE_INCLUDE_PATH
diff --git a/arch/riscv/kvm/vcpu.c b/arch/riscv/kvm/vcpu.c
index 7f1636c44d41..a59cac26ca7c 100644
--- a/arch/riscv/kvm/vcpu.c
+++ b/arch/riscv/kvm/vcpu.c
@@ -35,6 +35,7 @@ void kvm_riscv_clear_former_vcpu(void)
 	 * via the fast-path optimization.
 	 */
 	__this_cpu_write(kvm_former_vcpu, NULL);
+	kvm_riscv_vcpu_vector_owner_invalidate(RISCV_V_INVALIDATE_CPU_STATE);
 }
 
 const struct kvm_stats_desc kvm_vcpu_stats_desc[] = {
@@ -589,6 +590,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
 {
 	void *nsh;
 	struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr;
+	bool vector_owner_hit;
 
 	/*
 	 * If VCPU is being reloaded on the same physical CPU and no
@@ -647,9 +649,11 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
 	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);
+	vector_owner_hit = kvm_riscv_vcpu_vector_owner_load(vcpu, cpu);
+	if (!vector_owner_hit)
+		kvm_riscv_vcpu_host_vector_save(&vcpu->arch.host_context);
 	kvm_riscv_vcpu_guest_vector_restore(&vcpu->arch.guest_context,
-					    vcpu->arch.isa);
+					    vcpu->arch.isa, vector_owner_hit);
 	put_cpu_vector_context();
 
 	kvm_make_request(KVM_REQ_STEAL_UPDATE, vcpu);
@@ -661,6 +665,7 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
 {
 	void *nsh;
 	struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr;
+	bool preserve_guest;
 
 	vcpu->cpu = -1;
 
@@ -674,7 +679,9 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *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);
+	preserve_guest = kvm_riscv_vcpu_vector_owner_put(vcpu);
+	kvm_riscv_vcpu_host_vector_restore(&vcpu->arch.host_context,
+					   preserve_guest);
 	put_cpu_vector_context();
 
 	if (kvm_riscv_nacl_available()) {
@@ -793,6 +800,12 @@ static __always_inline void kvm_riscv_vcpu_swap_in_host_state(struct kvm_vcpu *v
 		zicficsr->ssp = csr_swap(CSR_SSP, 0);
 }
 
+enum kvm_riscv_vector_restore_type {
+	KVM_RISCV_VECTOR_RESTORE_NONE,
+	KVM_RISCV_VECTOR_RESTORE_FULL,
+	KVM_RISCV_VECTOR_RESTORE_CSR,
+};
+
 /*
  * Actually run the vCPU, entering an RCU extended quiescent state (EQS) while
  * the vCPU is running.
@@ -800,12 +813,14 @@ static __always_inline void kvm_riscv_vcpu_swap_in_host_state(struct kvm_vcpu *v
  * This must be noinstr as instrumentation may make use of RCU, and this is not
  * safe during the EQS.
  */
-static void noinstr kvm_riscv_vcpu_enter_exit(struct kvm_vcpu *vcpu,
-					      struct kvm_cpu_trap *trap)
+static int noinstr kvm_riscv_vcpu_enter_exit(struct kvm_vcpu *vcpu,
+					     struct kvm_cpu_trap *trap)
 {
 	void *nsh;
 	struct kvm_cpu_context *gcntx = &vcpu->arch.guest_context;
 	struct kvm_cpu_context *hcntx = &vcpu->arch.host_context;
+	int vector_restore = KVM_RISCV_VECTOR_RESTORE_NONE;
+	u32 vector_flags;
 
 	/*
 	 * We save trap CSRs (such as SEPC, SCAUSE, STVAL, HTVAL, and
@@ -818,11 +833,21 @@ 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;
+	vector_flags = current->thread.riscv_v_flags;
+	/* sstatus.VS != SR_VS_OFF is guaranteed for either restore request. */
+	if (vector_flags & (RISCV_V_VCPU_NEED_RESTORE |
+			    RISCV_V_VCPU_CSR_RESTORE)) {
+		current->thread.riscv_v_flags &=
+			~(RISCV_V_VCPU_NEED_RESTORE |
+			  RISCV_V_VCPU_CSR_RESTORE);
 		current->thread.riscv_v_flags |= RISCV_V_VCPU_CTX;
-		__kvm_riscv_vector_restore(gcntx);
+		if (vector_flags & RISCV_V_VCPU_NEED_RESTORE) {
+			__kvm_riscv_vector_restore(gcntx);
+			vector_restore = KVM_RISCV_VECTOR_RESTORE_FULL;
+		} else {
+			__kvm_riscv_vector_restore_csrs(gcntx);
+			vector_restore = KVM_RISCV_VECTOR_RESTORE_CSR;
+		}
 		gcntx->sstatus = (gcntx->sstatus & ~SR_VS) | SR_VS_CLEAN;
 	}
 
@@ -886,6 +911,8 @@ static void noinstr kvm_riscv_vcpu_enter_exit(struct kvm_vcpu *vcpu,
 	vcpu->arch.last_exit_cpu = vcpu->cpu;
 	guest_state_exit_irqoff();
 	kvm_riscv_vcpu_swap_in_host_state(vcpu);
+
+	return vector_restore;
 }
 
 int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
@@ -893,6 +920,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
 	int ret;
 	struct kvm_cpu_trap trap;
 	struct kvm_run *run = vcpu->run;
+	int vector_restore;
 
 	if (!vcpu->arch.ran_atleast_once)
 		kvm_riscv_vcpu_config_ran_once(vcpu);
@@ -1001,7 +1029,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
 
 		guest_timing_enter_irqoff();
 
-		kvm_riscv_vcpu_enter_exit(vcpu, &trap);
+		vector_restore = kvm_riscv_vcpu_enter_exit(vcpu, &trap);
 
 		vcpu->mode = OUTSIDE_GUEST_MODE;
 		vcpu->stat.exits++;
@@ -1026,6 +1054,12 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
 
 		local_irq_enable();
 
+		if (vector_restore == KVM_RISCV_VECTOR_RESTORE_FULL)
+			trace_kvm_riscv_vector_restore(vcpu->vcpu_id,
+						       smp_processor_id(), false);
+		else if (vector_restore == KVM_RISCV_VECTOR_RESTORE_CSR)
+			trace_kvm_riscv_vector_restore(vcpu->vcpu_id,
+						       smp_processor_id(), true);
 		trace_kvm_exit(&trap);
 
 		preempt_enable();
diff --git a/arch/riscv/kvm/vcpu_vector.c b/arch/riscv/kvm/vcpu_vector.c
index fefc2857f753..c197d3d7b539 100644
--- a/arch/riscv/kvm/vcpu_vector.c
+++ b/arch/riscv/kvm/vcpu_vector.c
@@ -7,9 +7,11 @@
  *     Greentime Hu <greentime.hu at sifive.com>
  */
 
+#include <linux/atomic.h>
 #include <linux/errno.h>
 #include <linux/err.h>
 #include <linux/kvm_host.h>
+#include <linux/moduleparam.h>
 #include <linux/nospec.h>
 #include <linux/uaccess.h>
 #include <asm/cpufeature.h>
@@ -17,7 +19,50 @@
 #include <asm/kvm_vcpu_vector.h>
 #include <asm/vector.h>
 
+#include "trace.h"
+
 #ifdef CONFIG_RISCV_ISA_V
+enum kvm_riscv_vector_owner_reason {
+	KVM_RISCV_VECTOR_OWNER_HIT,
+	KVM_RISCV_VECTOR_OWNER_EMPTY,
+	KVM_RISCV_VECTOR_OWNER_OTHER_VCPU,
+	KVM_RISCV_VECTOR_OWNER_COOKIE,
+	KVM_RISCV_VECTOR_OWNER_GENERATION,
+	KVM_RISCV_VECTOR_OWNER_GUEST_OFF,
+	KVM_RISCV_VECTOR_OWNER_HOST_ON,
+	KVM_RISCV_VECTOR_OWNER_MIGRATION,
+	KVM_RISCV_VECTOR_OWNER_PUT_CANDIDATE,
+	KVM_RISCV_VECTOR_OWNER_TASK_SAVE,
+	KVM_RISCV_VECTOR_OWNER_TASK_RESTORE,
+	KVM_RISCV_VECTOR_OWNER_TASK_DISCARD,
+	KVM_RISCV_VECTOR_OWNER_KERNEL_VECTOR,
+	KVM_RISCV_VECTOR_OWNER_CPU_STATE,
+	KVM_RISCV_VECTOR_OWNER_NOT_RESIDENT,
+};
+
+struct kvm_riscv_vector_owner {
+	/* Opaque comparison token; cookie rejects address reuse after destroy. */
+	unsigned long identity;
+	u64 cookie;
+	u64 generation;
+	unsigned long vcpu_id;
+	unsigned long guest_vs;
+};
+
+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");
+
+bool kvm_riscv_vcpu_vector_owner_enabled(void)
+{
+	return READ_ONCE(kvm_riscv_vector_owner_fastpath);
+}
+
 void kvm_riscv_vcpu_vector_reset(struct kvm_vcpu *vcpu)
 {
 	unsigned long *isa = vcpu->arch.isa;
@@ -34,6 +79,8 @@ void kvm_riscv_vcpu_vector_reset(struct kvm_vcpu *vcpu)
 	} else {
 		cntx->sstatus |= SR_VS_OFF;
 	}
+
+	vcpu->arch.vector_state_generation++;
 }
 
 static void kvm_riscv_vcpu_vector_clean(struct kvm_cpu_context *cntx)
@@ -46,18 +93,35 @@ void kvm_riscv_vcpu_guest_vector_save(struct kvm_cpu_context *cntx,
 				      unsigned long *isa)
 {
 	if ((cntx->sstatus & SR_VS) == SR_VS_DIRTY) {
-		if (riscv_isa_extension_available(isa, V))
+		if (riscv_isa_extension_available(isa, V)) {
+			struct kvm_vcpu *vcpu = container_of(cntx, struct kvm_vcpu,
+							  arch.guest_context);
+
 			__kvm_riscv_vector_save(cntx);
+			vcpu->arch.vector_state_generation++;
+		}
 		kvm_riscv_vcpu_vector_clean(cntx);
 	}
 }
 
 void kvm_riscv_vcpu_guest_vector_restore(struct kvm_cpu_context *cntx,
-					 unsigned long *isa)
+					 unsigned long *isa, bool csr_only)
 {
 	if ((cntx->sstatus & SR_VS) != SR_VS_OFF) {
-		if (riscv_isa_extension_available(isa, V))
-			riscv_v_flags_set(riscv_v_flags() | RISCV_V_VCPU_NEED_RESTORE);
+		if (riscv_isa_extension_available(isa, V)) {
+			struct kvm_vcpu *vcpu = container_of(cntx, struct kvm_vcpu,
+							  arch.guest_context);
+			unsigned long host_vs = task_pt_regs(current)->status & SR_VS;
+			u32 flags = riscv_v_flags();
+
+			flags &= ~(RISCV_V_VCPU_NEED_RESTORE |
+				   RISCV_V_VCPU_CSR_RESTORE);
+			flags |= csr_only ? RISCV_V_VCPU_CSR_RESTORE :
+					    RISCV_V_VCPU_NEED_RESTORE;
+			riscv_v_flags_set(flags);
+			trace_kvm_riscv_vector_restore_request(vcpu->vcpu_id,
+							       smp_processor_id(), 0, host_vs);
+		}
 	}
 }
 
@@ -68,11 +132,167 @@ void kvm_riscv_vcpu_host_vector_save(struct kvm_cpu_context *cntx)
 		__kvm_riscv_vector_save(cntx);
 }
 
-void kvm_riscv_vcpu_host_vector_restore(struct kvm_cpu_context *cntx)
+void kvm_riscv_vcpu_host_vector_restore(struct kvm_cpu_context *cntx,
+					bool preserve_guest)
 {
-	if (!kvm_riscv_isa_check_host(V))
+	if (!preserve_guest && !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));
+	riscv_v_flags_set(riscv_v_flags() &
+			  ~(RISCV_V_VCPU_CTX | RISCV_V_VCPU_NEED_RESTORE |
+			    RISCV_V_VCPU_CSR_RESTORE));
+}
+
+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;
+	}
+
+	trace_kvm_riscv_vector_owner(vcpu->vcpu_id, cpu, 0, eligible,
+				     reason, host_vs, guest_vs,
+				     vcpu->arch.vector_state_generation);
+
+	/* A load consumes the cached owner; the next put publishes it. */
+	owner->identity = 0;
+	owner->cookie = 0;
+	owner->generation = 0;
+	owner->vcpu_id = 0;
+	owner->guest_vs = 0;
+
+	return eligible;
+}
+
+bool kvm_riscv_vcpu_vector_owner_put(struct kvm_vcpu *vcpu)
+{
+	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;
+	u32 flags = riscv_v_flags();
+	enum kvm_riscv_vector_owner_reason reason;
+	bool eligible;
+
+	if (!kvm_riscv_vcpu_vector_owner_enabled())
+		return false;
+
+	owner = this_cpu_ptr(&kvm_riscv_vector_owner);
+	eligible = guest_vs != SR_VS_OFF && host_vs == SR_VS_OFF &&
+		   (flags & RISCV_V_VCPU_CTX) &&
+		   !(flags & (RISCV_V_VCPU_NEED_RESTORE |
+			      RISCV_V_VCPU_CSR_RESTORE));
+	if (eligible) {
+		owner->identity = (unsigned long)vcpu;
+		owner->cookie = vcpu->arch.vector_owner_cookie;
+		owner->generation = vcpu->arch.vector_state_generation;
+		owner->vcpu_id = vcpu->vcpu_id;
+		owner->guest_vs = guest_vs;
+		reason = KVM_RISCV_VECTOR_OWNER_PUT_CANDIDATE;
+	} else {
+		owner->identity = 0;
+		owner->cookie = 0;
+		owner->generation = 0;
+		owner->vcpu_id = 0;
+		owner->guest_vs = 0;
+		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
+			reason = KVM_RISCV_VECTOR_OWNER_NOT_RESIDENT;
+	}
+
+	trace_kvm_riscv_vector_owner(vcpu->vcpu_id, smp_processor_id(), 1,
+				     eligible, reason, host_vs, guest_vs,
+				     vcpu->arch.vector_state_generation);
+
+	return eligible;
+}
+
+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;
+
+	switch (reason) {
+	case RISCV_V_INVALIDATE_TASK_SAVE:
+		trace_reason = KVM_RISCV_VECTOR_OWNER_TASK_SAVE;
+		break;
+	case RISCV_V_INVALIDATE_TASK_RESTORE:
+		trace_reason = KVM_RISCV_VECTOR_OWNER_TASK_RESTORE;
+		break;
+	case RISCV_V_INVALIDATE_TASK_DISCARD:
+		trace_reason = KVM_RISCV_VECTOR_OWNER_TASK_DISCARD;
+		break;
+	case RISCV_V_INVALIDATE_KERNEL_VECTOR:
+		trace_reason = KVM_RISCV_VECTOR_OWNER_KERNEL_VECTOR;
+		break;
+	case RISCV_V_INVALIDATE_CPU_STATE:
+		trace_reason = KVM_RISCV_VECTOR_OWNER_CPU_STATE;
+		break;
+	default:
+		return;
+	}
+
+	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();
 }
 
 int kvm_riscv_vcpu_alloc_vector_context(struct kvm_vcpu *vcpu)
@@ -88,6 +308,10 @@ int kvm_riscv_vcpu_alloc_vector_context(struct kvm_vcpu *vcpu)
 		return -ENOMEM;
 	}
 
+	vcpu->arch.vector_owner_cookie =
+		atomic64_inc_return(&kvm_riscv_vector_owner_cookie);
+	vcpu->arch.vector_state_generation = 0;
+
 	return 0;
 }
 
@@ -110,8 +334,13 @@ void kvm_riscv_vcpu_flush_vector(void)
 
 	kvm_riscv_vcpu_guest_vector_save(&vcpu->arch.guest_context, vcpu->arch.isa);
 
-	if ((vcpu->arch.guest_context.sstatus & SR_VS) != SR_VS_OFF)
+	if ((vcpu->arch.guest_context.sstatus & SR_VS) != SR_VS_OFF) {
+		unsigned long host_vs = task_pt_regs(current)->status & SR_VS;
+
 		riscv_v_flags_set(riscv_v_flags() | RISCV_V_VCPU_NEED_RESTORE);
+		trace_kvm_riscv_vector_restore_request(vcpu->vcpu_id,
+						       smp_processor_id(), 1, host_vs);
+	}
 }
 
 #endif
@@ -229,6 +458,13 @@ int kvm_riscv_vcpu_set_reg_vector(struct kvm_vcpu *vcpu,
 	if (rc)
 		return rc;
 
+	/*
+	 * copy_from_user() can modify a prefix of the destination before it
+	 * reports a fault.  Invalidate any cached hardware copy first so a
+	 * failed SET_ONE_REG cannot leave newer memory hidden behind a stale
+	 * per-hart owner.
+	 */
+	vcpu->arch.vector_state_generation++;
 	if (copy_from_user(reg_addr, uaddr, reg_size))
 		return -EFAULT;
 
-- 
2.43.0




More information about the kvm-riscv mailing list