diff options
Diffstat (limited to 'arch/riscv/kvm/vcpu.c')
| -rw-r--r-- | arch/riscv/kvm/vcpu.c | 76 | 
1 files changed, 57 insertions, 19 deletions
diff --git a/arch/riscv/kvm/vcpu.c b/arch/riscv/kvm/vcpu.c index fb84619df012..624166004e36 100644 --- a/arch/riscv/kvm/vcpu.c +++ b/arch/riscv/kvm/vcpu.c @@ -53,6 +53,17 @@ static void kvm_riscv_reset_vcpu(struct kvm_vcpu *vcpu)  	struct kvm_vcpu_csr *reset_csr = &vcpu->arch.guest_reset_csr;  	struct kvm_cpu_context *cntx = &vcpu->arch.guest_context;  	struct kvm_cpu_context *reset_cntx = &vcpu->arch.guest_reset_context; +	bool loaded; + +	/** +	 * The preemption should be disabled here because it races with +	 * kvm_sched_out/kvm_sched_in(called from preempt notifiers) which +	 * also calls vcpu_load/put. +	 */ +	get_cpu(); +	loaded = (vcpu->cpu != -1); +	if (loaded) +		kvm_arch_vcpu_put(vcpu);  	memcpy(csr, reset_csr, sizeof(*csr)); @@ -64,6 +75,11 @@ static void kvm_riscv_reset_vcpu(struct kvm_vcpu *vcpu)  	WRITE_ONCE(vcpu->arch.irqs_pending, 0);  	WRITE_ONCE(vcpu->arch.irqs_pending_mask, 0); + +	/* Reset the guest CSRs for hotplug usecase */ +	if (loaded) +		kvm_arch_vcpu_load(vcpu, smp_processor_id()); +	put_cpu();  }  int kvm_arch_vcpu_precreate(struct kvm *kvm, unsigned int id) @@ -74,9 +90,11 @@ int kvm_arch_vcpu_precreate(struct kvm *kvm, unsigned int id)  int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)  {  	struct kvm_cpu_context *cntx; +	struct kvm_vcpu_csr *reset_csr = &vcpu->arch.guest_reset_csr;  	/* Mark this VCPU never ran */  	vcpu->arch.ran_atleast_once = false; +	vcpu->arch.mmu_page_cache.gfp_zero = __GFP_ZERO;  	/* Setup ISA features available to VCPU */  	vcpu->arch.isa = riscv_isa_extension_base(NULL) & KVM_RISCV_ISA_ALLOWED; @@ -89,6 +107,9 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)  	cntx->hstatus |= HSTATUS_SPVP;  	cntx->hstatus |= HSTATUS_SPV; +	/* By default, make CY, TM, and IR counters accessible in VU mode */ +	reset_csr->scounteren = 0x7; +  	/* Setup VCPU timer */  	kvm_riscv_vcpu_timer_init(vcpu); @@ -100,6 +121,13 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)  void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu)  { +	/** +	 * vcpu with id 0 is the designated boot cpu. +	 * Keep all vcpus with non-zero id in power-off state so that +	 * they can be brought up using SBI HSM extension. +	 */ +	if (vcpu->vcpu_idx != 0) +		kvm_riscv_vcpu_power_off(vcpu);  }  void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) @@ -107,8 +135,8 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)  	/* Cleanup VCPU timer */  	kvm_riscv_vcpu_timer_deinit(vcpu); -	/* Flush the pages pre-allocated for Stage2 page table mappings */ -	kvm_riscv_stage2_flush_cache(vcpu); +	/* Free unused pages pre-allocated for Stage2 page table mappings */ +	kvm_mmu_free_memory_cache(&vcpu->arch.mmu_page_cache);  }  int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu) @@ -675,6 +703,20 @@ static void kvm_riscv_update_hvip(struct kvm_vcpu *vcpu)  	csr_write(CSR_HVIP, csr->hvip);  } +/* + * Actually run the vCPU, entering an RCU extended quiescent state (EQS) while + * the vCPU is running. + * + * 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) +{ +	guest_state_enter_irqoff(); +	__kvm_riscv_switch_to(&vcpu->arch); +	guest_state_exit_irqoff(); +} +  int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)  {  	int ret; @@ -766,9 +808,9 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)  			continue;  		} -		guest_enter_irqoff(); +		guest_timing_enter_irqoff(); -		__kvm_riscv_switch_to(&vcpu->arch); +		kvm_riscv_vcpu_enter_exit(vcpu);  		vcpu->mode = OUTSIDE_GUEST_MODE;  		vcpu->stat.exits++; @@ -788,25 +830,21 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)  		kvm_riscv_vcpu_sync_interrupts(vcpu);  		/* -		 * We may have taken a host interrupt in VS/VU-mode (i.e. -		 * while executing the guest). This interrupt is still -		 * pending, as we haven't serviced it yet! +		 * We must ensure that any pending interrupts are taken before +		 * we exit guest timing so that timer ticks are accounted as +		 * guest time. Transiently unmask interrupts so that any +		 * pending interrupts are taken.  		 * -		 * We're now back in HS-mode with interrupts disabled -		 * so enabling the interrupts now will have the effect -		 * of taking the interrupt again, in HS-mode this time. +		 * There's no barrier which ensures that pending interrupts are +		 * recognised, so we just hope that the CPU takes any pending +		 * interrupts between the enable and disable.  		 */  		local_irq_enable(); +		local_irq_disable(); -		/* -		 * We do local_irq_enable() before calling guest_exit() so -		 * that if a timer interrupt hits while running the guest -		 * we account that tick as being spent in the guest. We -		 * enable preemption after calling guest_exit() so that if -		 * we get preempted we make sure ticks after that is not -		 * counted as guest time. -		 */ -		guest_exit(); +		guest_timing_exit_irqoff(); + +		local_irq_enable();  		preempt_enable();  |