[PATCH v5 09/33] KVM: PPC: Book3S HV: Streamlined guest entry/exit path on P9 for radix guests

David Gibson david at gibson.dropbear.id.au
Tue Oct 9 11:20:42 AEDT 2018


On Mon, Oct 08, 2018 at 04:30:55PM +1100, Paul Mackerras wrote:
> This creates an alternative guest entry/exit path which is used for
> radix guests on POWER9 systems when we have indep_threads_mode=Y.  In
> these circumstances there is exactly one vcpu per vcore and there is
> no coordination required between vcpus or vcores; the vcpu can enter
> the guest without needing to synchronize with anything else.
> 
> The new fast path is implemented almost entirely in C in book3s_hv.c
> and runs with the MMU on until the guest is entered.  On guest exit
> we use the existing path until the point where we are committed to
> exiting the guest (as distinct from handling an interrupt in the
> low-level code and returning to the guest) and we have pulled the
> guest context from the XIVE.  At that point we check a flag in the
> stack frame to see whether we came in via the old path and the new
> path; if we came in via the new path then we go back to C code to do
> the rest of the process of saving the guest context and restoring the
> host context.
> 
> The C code is split into separate functions for handling the
> OS-accessible state and the hypervisor state, with the idea that the
> latter can be replaced by a hypercall when we implement nested
> virtualization.
> 
> Signed-off-by: Paul Mackerras <paulus at ozlabs.org>

Reviewed-by: David Gibson <david at gibson.dropbear.id.au>

> ---
>  arch/powerpc/include/asm/asm-prototypes.h |   2 +
>  arch/powerpc/include/asm/kvm_ppc.h        |   2 +
>  arch/powerpc/kvm/book3s_hv.c              | 429 +++++++++++++++++++++++++++++-
>  arch/powerpc/kvm/book3s_hv_ras.c          |   2 +
>  arch/powerpc/kvm/book3s_hv_rmhandlers.S   |  95 ++++++-
>  arch/powerpc/kvm/book3s_xive.c            |  63 +++++
>  6 files changed, 589 insertions(+), 4 deletions(-)
> 
> diff --git a/arch/powerpc/include/asm/asm-prototypes.h b/arch/powerpc/include/asm/asm-prototypes.h
> index 0c1a2b0..5c9b00c 100644
> --- a/arch/powerpc/include/asm/asm-prototypes.h
> +++ b/arch/powerpc/include/asm/asm-prototypes.h
> @@ -165,4 +165,6 @@ void kvmhv_load_host_pmu(void);
>  void kvmhv_save_guest_pmu(struct kvm_vcpu *vcpu, bool pmu_in_use);
>  void kvmhv_load_guest_pmu(struct kvm_vcpu *vcpu);
>  
> +int __kvmhv_vcpu_entry_p9(struct kvm_vcpu *vcpu);
> +
>  #endif /* _ASM_POWERPC_ASM_PROTOTYPES_H */
> diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h
> index 83d61b8..245e564 100644
> --- a/arch/powerpc/include/asm/kvm_ppc.h
> +++ b/arch/powerpc/include/asm/kvm_ppc.h
> @@ -585,6 +585,7 @@ extern int kvmppc_xive_set_icp(struct kvm_vcpu *vcpu, u64 icpval);
>  
>  extern int kvmppc_xive_set_irq(struct kvm *kvm, int irq_source_id, u32 irq,
>  			       int level, bool line_status);
> +extern void kvmppc_xive_push_vcpu(struct kvm_vcpu *vcpu);
>  #else
>  static inline int kvmppc_xive_set_xive(struct kvm *kvm, u32 irq, u32 server,
>  				       u32 priority) { return -1; }
> @@ -607,6 +608,7 @@ static inline int kvmppc_xive_set_icp(struct kvm_vcpu *vcpu, u64 icpval) { retur
>  
>  static inline int kvmppc_xive_set_irq(struct kvm *kvm, int irq_source_id, u32 irq,
>  				      int level, bool line_status) { return -ENODEV; }
> +static inline void kvmppc_xive_push_vcpu(struct kvm_vcpu *vcpu) { }
>  #endif /* CONFIG_KVM_XIVE */
>  
>  /*
> diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c
> index 0e17593..0c1dd76 100644
> --- a/arch/powerpc/kvm/book3s_hv.c
> +++ b/arch/powerpc/kvm/book3s_hv.c
> @@ -3080,6 +3080,269 @@ static noinline void kvmppc_run_core(struct kvmppc_vcore *vc)
>  }
>  
>  /*
> + * Load up hypervisor-mode registers on P9.
> + */
> +static int kvmhv_load_hv_regs_and_go(struct kvm_vcpu *vcpu, u64 time_limit)
> +{
> +	struct kvmppc_vcore *vc = vcpu->arch.vcore;
> +	s64 hdec;
> +	u64 tb, purr, spurr;
> +	int trap;
> +	unsigned long host_hfscr = mfspr(SPRN_HFSCR);
> +	unsigned long host_ciabr = mfspr(SPRN_CIABR);
> +	unsigned long host_dawr = mfspr(SPRN_DAWR);
> +	unsigned long host_dawrx = mfspr(SPRN_DAWRX);
> +	unsigned long host_psscr = mfspr(SPRN_PSSCR);
> +	unsigned long host_pidr = mfspr(SPRN_PID);
> +
> +	hdec = time_limit - mftb();
> +	if (hdec < 0)
> +		return BOOK3S_INTERRUPT_HV_DECREMENTER;
> +	mtspr(SPRN_HDEC, hdec);
> +
> +	if (vc->tb_offset) {
> +		u64 new_tb = mftb() + vc->tb_offset;
> +		mtspr(SPRN_TBU40, new_tb);
> +		tb = mftb();
> +		if ((tb & 0xffffff) < (new_tb & 0xffffff))
> +			mtspr(SPRN_TBU40, new_tb + 0x1000000);
> +		vc->tb_offset_applied = vc->tb_offset;
> +	}
> +
> +	if (vc->pcr)
> +		mtspr(SPRN_PCR, vc->pcr);
> +	mtspr(SPRN_DPDES, vc->dpdes);
> +	mtspr(SPRN_VTB, vc->vtb);
> +
> +	local_paca->kvm_hstate.host_purr = mfspr(SPRN_PURR);
> +	local_paca->kvm_hstate.host_spurr = mfspr(SPRN_SPURR);
> +	mtspr(SPRN_PURR, vcpu->arch.purr);
> +	mtspr(SPRN_SPURR, vcpu->arch.spurr);
> +
> +	if (cpu_has_feature(CPU_FTR_DAWR)) {
> +		mtspr(SPRN_DAWR, vcpu->arch.dawr);
> +		mtspr(SPRN_DAWRX, vcpu->arch.dawrx);
> +	}
> +	mtspr(SPRN_CIABR, vcpu->arch.ciabr);
> +	mtspr(SPRN_IC, vcpu->arch.ic);
> +	mtspr(SPRN_PID, vcpu->arch.pid);
> +
> +	mtspr(SPRN_PSSCR, vcpu->arch.psscr | PSSCR_EC |
> +	      (local_paca->kvm_hstate.fake_suspend << PSSCR_FAKE_SUSPEND_LG));
> +
> +	mtspr(SPRN_HFSCR, vcpu->arch.hfscr);
> +
> +	mtspr(SPRN_SPRG0, vcpu->arch.shregs.sprg0);
> +	mtspr(SPRN_SPRG1, vcpu->arch.shregs.sprg1);
> +	mtspr(SPRN_SPRG2, vcpu->arch.shregs.sprg2);
> +	mtspr(SPRN_SPRG3, vcpu->arch.shregs.sprg3);
> +
> +	mtspr(SPRN_AMOR, ~0UL);
> +
> +	mtspr(SPRN_LPCR, vc->lpcr);
> +	isync();
> +
> +	kvmppc_xive_push_vcpu(vcpu);
> +
> +	mtspr(SPRN_SRR0, vcpu->arch.shregs.srr0);
> +	mtspr(SPRN_SRR1, vcpu->arch.shregs.srr1);
> +
> +	trap = __kvmhv_vcpu_entry_p9(vcpu);
> +
> +	/* Advance host PURR/SPURR by the amount used by guest */
> +	purr = mfspr(SPRN_PURR);
> +	spurr = mfspr(SPRN_SPURR);
> +	mtspr(SPRN_PURR, local_paca->kvm_hstate.host_purr +
> +	      purr - vcpu->arch.purr);
> +	mtspr(SPRN_SPURR, local_paca->kvm_hstate.host_spurr +
> +	      spurr - vcpu->arch.spurr);
> +	vcpu->arch.purr = purr;
> +	vcpu->arch.spurr = spurr;
> +
> +	vcpu->arch.ic = mfspr(SPRN_IC);
> +	vcpu->arch.pid = mfspr(SPRN_PID);
> +	vcpu->arch.psscr = mfspr(SPRN_PSSCR) & PSSCR_GUEST_VIS;
> +
> +	vcpu->arch.shregs.sprg0 = mfspr(SPRN_SPRG0);
> +	vcpu->arch.shregs.sprg1 = mfspr(SPRN_SPRG1);
> +	vcpu->arch.shregs.sprg2 = mfspr(SPRN_SPRG2);
> +	vcpu->arch.shregs.sprg3 = mfspr(SPRN_SPRG3);
> +
> +	mtspr(SPRN_PSSCR, host_psscr);
> +	mtspr(SPRN_HFSCR, host_hfscr);
> +	mtspr(SPRN_CIABR, host_ciabr);
> +	mtspr(SPRN_DAWR, host_dawr);
> +	mtspr(SPRN_DAWRX, host_dawrx);
> +	mtspr(SPRN_PID, host_pidr);
> +
> +	/*
> +	 * Since this is radix, do a eieio; tlbsync; ptesync sequence in
> +	 * case we interrupted the guest between a tlbie and a ptesync.
> +	 */
> +	asm volatile("eieio; tlbsync; ptesync");
> +
> +	mtspr(SPRN_LPID, vcpu->kvm->arch.host_lpid);	/* restore host LPID */
> +	isync();
> +
> +	vc->dpdes = mfspr(SPRN_DPDES);
> +	vc->vtb = mfspr(SPRN_VTB);
> +	mtspr(SPRN_DPDES, 0);
> +	if (vc->pcr)
> +		mtspr(SPRN_PCR, 0);
> +
> +	if (vc->tb_offset_applied) {
> +		u64 new_tb = mftb() - vc->tb_offset_applied;
> +		mtspr(SPRN_TBU40, new_tb);
> +		tb = mftb();
> +		if ((tb & 0xffffff) < (new_tb & 0xffffff))
> +			mtspr(SPRN_TBU40, new_tb + 0x1000000);
> +		vc->tb_offset_applied = 0;
> +	}
> +
> +	mtspr(SPRN_HDEC, 0x7fffffff);
> +	mtspr(SPRN_LPCR, vcpu->kvm->arch.host_lpcr);
> +
> +	return trap;
> +}
> +
> +/*
> + * Virtual-mode guest entry for POWER9 and later when the host and
> + * guest are both using the radix MMU.  The LPIDR has already been set.
> + */
> +int kvmhv_p9_guest_entry(struct kvm_vcpu *vcpu, u64 time_limit)
> +{
> +	struct kvmppc_vcore *vc = vcpu->arch.vcore;
> +	unsigned long host_dscr = mfspr(SPRN_DSCR);
> +	unsigned long host_tidr = mfspr(SPRN_TIDR);
> +	unsigned long host_iamr = mfspr(SPRN_IAMR);
> +	s64 dec;
> +	u64 tb;
> +	int trap, save_pmu;
> +
> +	dec = mfspr(SPRN_DEC);
> +	tb = mftb();
> +	if (dec < 512)
> +		return BOOK3S_INTERRUPT_HV_DECREMENTER;
> +	local_paca->kvm_hstate.dec_expires = dec + tb;
> +	if (local_paca->kvm_hstate.dec_expires < time_limit)
> +		time_limit = local_paca->kvm_hstate.dec_expires;
> +
> +	vcpu->arch.ceded = 0;
> +
> +	kvmhv_save_host_pmu();		/* saves it to PACA kvm_hstate */
> +
> +	kvmppc_subcore_enter_guest();
> +
> +	vc->entry_exit_map = 1;
> +	vc->in_guest = 1;
> +
> +	if (vcpu->arch.vpa.pinned_addr) {
> +		struct lppaca *lp = vcpu->arch.vpa.pinned_addr;
> +		u32 yield_count = be32_to_cpu(lp->yield_count) + 1;
> +		lp->yield_count = cpu_to_be32(yield_count);
> +		vcpu->arch.vpa.dirty = 1;
> +	}
> +
> +	if (cpu_has_feature(CPU_FTR_TM) ||
> +	    cpu_has_feature(CPU_FTR_P9_TM_HV_ASSIST))
> +		kvmppc_restore_tm_hv(vcpu, vcpu->arch.shregs.msr, true);
> +
> +	kvmhv_load_guest_pmu(vcpu);
> +
> +	msr_check_and_set(MSR_FP | MSR_VEC | MSR_VSX);
> +	load_fp_state(&vcpu->arch.fp);
> +	load_vr_state(&vcpu->arch.vr);
> +
> +	mtspr(SPRN_DSCR, vcpu->arch.dscr);
> +	mtspr(SPRN_IAMR, vcpu->arch.iamr);
> +	mtspr(SPRN_PSPB, vcpu->arch.pspb);
> +	mtspr(SPRN_FSCR, vcpu->arch.fscr);
> +	mtspr(SPRN_TAR, vcpu->arch.tar);
> +	mtspr(SPRN_EBBHR, vcpu->arch.ebbhr);
> +	mtspr(SPRN_EBBRR, vcpu->arch.ebbrr);
> +	mtspr(SPRN_BESCR, vcpu->arch.bescr);
> +	mtspr(SPRN_WORT, vcpu->arch.wort);
> +	mtspr(SPRN_TIDR, vcpu->arch.tid);
> +	mtspr(SPRN_DAR, vcpu->arch.shregs.dar);
> +	mtspr(SPRN_DSISR, vcpu->arch.shregs.dsisr);
> +	mtspr(SPRN_AMR, vcpu->arch.amr);
> +	mtspr(SPRN_UAMOR, vcpu->arch.uamor);
> +
> +	if (!(vcpu->arch.ctrl & 1))
> +		mtspr(SPRN_CTRLT, mfspr(SPRN_CTRLF) & ~1);
> +
> +	mtspr(SPRN_DEC, vcpu->arch.dec_expires - mftb());
> +
> +	if (vcpu->arch.doorbell_request) {
> +		vc->dpdes = 1;
> +		smp_wmb();
> +		vcpu->arch.doorbell_request = 0;
> +	}
> +
> +	trap = kvmhv_load_hv_regs_and_go(vcpu, time_limit);
> +
> +	vcpu->arch.slb_max = 0;
> +	dec = mfspr(SPRN_DEC);
> +	tb = mftb();
> +	vcpu->arch.dec_expires = dec + tb;
> +	vcpu->cpu = -1;
> +	vcpu->arch.thread_cpu = -1;
> +	vcpu->arch.ctrl = mfspr(SPRN_CTRLF);
> +
> +	vcpu->arch.iamr = mfspr(SPRN_IAMR);
> +	vcpu->arch.pspb = mfspr(SPRN_PSPB);
> +	vcpu->arch.fscr = mfspr(SPRN_FSCR);
> +	vcpu->arch.tar = mfspr(SPRN_TAR);
> +	vcpu->arch.ebbhr = mfspr(SPRN_EBBHR);
> +	vcpu->arch.ebbrr = mfspr(SPRN_EBBRR);
> +	vcpu->arch.bescr = mfspr(SPRN_BESCR);
> +	vcpu->arch.wort = mfspr(SPRN_WORT);
> +	vcpu->arch.tid = mfspr(SPRN_TIDR);
> +	vcpu->arch.amr = mfspr(SPRN_AMR);
> +	vcpu->arch.uamor = mfspr(SPRN_UAMOR);
> +	vcpu->arch.dscr = mfspr(SPRN_DSCR);
> +
> +	mtspr(SPRN_PSPB, 0);
> +	mtspr(SPRN_WORT, 0);
> +	mtspr(SPRN_AMR, 0);
> +	mtspr(SPRN_UAMOR, 0);
> +	mtspr(SPRN_DSCR, host_dscr);
> +	mtspr(SPRN_TIDR, host_tidr);
> +	mtspr(SPRN_IAMR, host_iamr);
> +	mtspr(SPRN_PSPB, 0);
> +
> +	msr_check_and_set(MSR_FP | MSR_VEC | MSR_VSX);
> +	store_fp_state(&vcpu->arch.fp);
> +	store_vr_state(&vcpu->arch.vr);
> +
> +	if (cpu_has_feature(CPU_FTR_TM) ||
> +	    cpu_has_feature(CPU_FTR_P9_TM_HV_ASSIST))
> +		kvmppc_save_tm_hv(vcpu, vcpu->arch.shregs.msr, true);
> +
> +	save_pmu = 1;
> +	if (vcpu->arch.vpa.pinned_addr) {
> +		struct lppaca *lp = vcpu->arch.vpa.pinned_addr;
> +		u32 yield_count = be32_to_cpu(lp->yield_count) + 1;
> +		lp->yield_count = cpu_to_be32(yield_count);
> +		vcpu->arch.vpa.dirty = 1;
> +		save_pmu = lp->pmcregs_in_use;
> +	}
> +
> +	kvmhv_save_guest_pmu(vcpu, save_pmu);
> +
> +	vc->entry_exit_map = 0x101;
> +	vc->in_guest = 0;
> +
> +	mtspr(SPRN_DEC, local_paca->kvm_hstate.dec_expires - mftb());
> +
> +	kvmhv_load_host_pmu();
> +
> +	kvmppc_subcore_exit_guest();
> +
> +	return trap;
> +}
> +
> +/*
>   * Wait for some other vcpu thread to execute us, and
>   * wake us up when we need to handle something in the host.
>   */
> @@ -3405,6 +3668,167 @@ static int kvmppc_run_vcpu(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
>  	return vcpu->arch.ret;
>  }
>  
> +static int kvmhv_run_single_vcpu(struct kvm_run *kvm_run,
> +				 struct kvm_vcpu *vcpu, u64 time_limit)
> +{
> +	int trap, r, pcpu, pcpu0;
> +	int srcu_idx;
> +	struct kvmppc_vcore *vc;
> +	struct kvm *kvm = vcpu->kvm;
> +
> +	trace_kvmppc_run_vcpu_enter(vcpu);
> +
> +	kvm_run->exit_reason = 0;
> +	vcpu->arch.ret = RESUME_GUEST;
> +	vcpu->arch.trap = 0;
> +
> +	vc = vcpu->arch.vcore;
> +	vcpu->arch.ceded = 0;
> +	vcpu->arch.run_task = current;
> +	vcpu->arch.kvm_run = kvm_run;
> +	vcpu->arch.stolen_logged = vcore_stolen_time(vc, mftb());
> +	vcpu->arch.state = KVMPPC_VCPU_RUNNABLE;
> +	vcpu->arch.busy_preempt = TB_NIL;
> +	vcpu->arch.last_inst = KVM_INST_FETCH_FAILED;
> +	vc->runnable_threads[0] = vcpu;
> +	vc->n_runnable = 1;
> +	vc->runner = vcpu;
> +
> +	/* See if the MMU is ready to go */
> +	if (!kvm->arch.mmu_ready) {
> +		r = kvmhv_setup_mmu(vcpu);
> +		if (r) {
> +			kvm_run->exit_reason = KVM_EXIT_FAIL_ENTRY;
> +			kvm_run->fail_entry.
> +				hardware_entry_failure_reason = 0;
> +			vcpu->arch.ret = r;
> +			goto out;
> +		}
> +	}
> +
> +	if (need_resched())
> +		cond_resched();
> +
> +	kvmppc_update_vpas(vcpu);
> +
> +	init_vcore_to_run(vc);
> +	vc->preempt_tb = TB_NIL;
> +
> +	preempt_disable();
> +	pcpu = smp_processor_id();
> +	vc->pcpu = pcpu;
> +	kvmppc_prepare_radix_vcpu(vcpu, pcpu);
> +
> +	local_irq_disable();
> +	hard_irq_disable();
> +	if (signal_pending(current))
> +		goto sigpend;
> +	if (lazy_irq_pending() || need_resched() || !kvm->arch.mmu_ready)
> +		goto out;
> +
> +	kvmppc_core_prepare_to_enter(vcpu);
> +
> +	kvmppc_clear_host_core(pcpu);
> +
> +	local_paca->kvm_hstate.tid = 0;
> +	local_paca->kvm_hstate.napping = 0;
> +	local_paca->kvm_hstate.kvm_split_mode = NULL;
> +	kvmppc_start_thread(vcpu, vc);
> +	kvmppc_create_dtl_entry(vcpu, vc);
> +	trace_kvm_guest_enter(vcpu);
> +
> +	vc->vcore_state = VCORE_RUNNING;
> +	trace_kvmppc_run_core(vc, 0);
> +
> +	mtspr(SPRN_LPID, vc->kvm->arch.lpid);
> +	isync();
> +
> +	/* See comment above in kvmppc_run_core() about this */
> +	pcpu0 = pcpu;
> +	if (cpu_has_feature(CPU_FTR_ARCH_300))
> +		pcpu0 &= ~0x3UL;
> +
> +	if (cpumask_test_cpu(pcpu0, &kvm->arch.need_tlb_flush)) {
> +		radix__local_flush_tlb_lpid_guest(kvm->arch.lpid);
> +		/* Clear the bit after the TLB flush */
> +		cpumask_clear_cpu(pcpu0, &kvm->arch.need_tlb_flush);
> +	}
> +
> +	trace_hardirqs_on();
> +	guest_enter_irqoff();
> +
> +	srcu_idx = srcu_read_lock(&kvm->srcu);
> +
> +	this_cpu_disable_ftrace();
> +
> +	trap = kvmhv_p9_guest_entry(vcpu, time_limit);
> +	vcpu->arch.trap = trap;
> +
> +	this_cpu_enable_ftrace();
> +
> +	srcu_read_unlock(&kvm->srcu, srcu_idx);
> +
> +	mtspr(SPRN_LPID, kvm->arch.host_lpid);
> +	isync();
> +
> +	trace_hardirqs_off();
> +	set_irq_happened(trap);
> +
> +	kvmppc_set_host_core(pcpu);
> +
> +	local_irq_enable();
> +	guest_exit();
> +
> +	cpumask_clear_cpu(pcpu, &kvm->arch.cpu_in_guest);
> +
> +	preempt_enable();
> +
> +	/* cancel pending decrementer exception if DEC is now positive */
> +	if (get_tb() < vcpu->arch.dec_expires && kvmppc_core_pending_dec(vcpu))
> +		kvmppc_core_dequeue_dec(vcpu);
> +
> +	trace_kvm_guest_exit(vcpu);
> +	r = RESUME_GUEST;
> +	if (trap)
> +		r = kvmppc_handle_exit_hv(kvm_run, vcpu, current);
> +	vcpu->arch.ret = r;
> +
> +	if (is_kvmppc_resume_guest(r) && vcpu->arch.ceded &&
> +	    !kvmppc_vcpu_woken(vcpu)) {
> +		kvmppc_set_timer(vcpu);
> +		while (vcpu->arch.ceded && !kvmppc_vcpu_woken(vcpu)) {
> +			if (signal_pending(current)) {
> +				vcpu->stat.signal_exits++;
> +				kvm_run->exit_reason = KVM_EXIT_INTR;
> +				vcpu->arch.ret = -EINTR;
> +				break;
> +			}
> +			spin_lock(&vc->lock);
> +			kvmppc_vcore_blocked(vc);
> +			spin_unlock(&vc->lock);
> +		}
> +	}
> +	vcpu->arch.ceded = 0;
> +
> +	vc->vcore_state = VCORE_INACTIVE;
> +	trace_kvmppc_run_core(vc, 1);
> +
> + done:
> +	kvmppc_remove_runnable(vc, vcpu);
> +	trace_kvmppc_run_vcpu_exit(vcpu, kvm_run);
> +
> +	return vcpu->arch.ret;
> +
> + sigpend:
> +	vcpu->stat.signal_exits++;
> +	kvm_run->exit_reason = KVM_EXIT_INTR;
> +	vcpu->arch.ret = -EINTR;
> + out:
> +	local_irq_enable();
> +	preempt_enable();
> +	goto done;
> +}
> +
>  static int kvmppc_vcpu_run_hv(struct kvm_run *run, struct kvm_vcpu *vcpu)
>  {
>  	int r;
> @@ -3480,7 +3904,10 @@ static int kvmppc_vcpu_run_hv(struct kvm_run *run, struct kvm_vcpu *vcpu)
>  	vcpu->arch.state = KVMPPC_VCPU_BUSY_IN_HOST;
>  
>  	do {
> -		r = kvmppc_run_vcpu(run, vcpu);
> +		if (kvm->arch.threads_indep && kvm_is_radix(kvm))
> +			r = kvmhv_run_single_vcpu(run, vcpu, ~(u64)0);
> +		else
> +			r = kvmppc_run_vcpu(run, vcpu);
>  
>  		if (run->exit_reason == KVM_EXIT_PAPR_HCALL &&
>  		    !(vcpu->arch.shregs.msr & MSR_PR)) {
> diff --git a/arch/powerpc/kvm/book3s_hv_ras.c b/arch/powerpc/kvm/book3s_hv_ras.c
> index ee564b6..0787f12 100644
> --- a/arch/powerpc/kvm/book3s_hv_ras.c
> +++ b/arch/powerpc/kvm/book3s_hv_ras.c
> @@ -177,6 +177,7 @@ void kvmppc_subcore_enter_guest(void)
>  
>  	local_paca->sibling_subcore_state->in_guest[subcore_id] = 1;
>  }
> +EXPORT_SYMBOL_GPL(kvmppc_subcore_enter_guest);
>  
>  void kvmppc_subcore_exit_guest(void)
>  {
> @@ -187,6 +188,7 @@ void kvmppc_subcore_exit_guest(void)
>  
>  	local_paca->sibling_subcore_state->in_guest[subcore_id] = 0;
>  }
> +EXPORT_SYMBOL_GPL(kvmppc_subcore_exit_guest);
>  
>  static bool kvmppc_tb_resync_required(void)
>  {
> diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
> index 45dd637..ea84696 100644
> --- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S
> +++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
> @@ -47,8 +47,9 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_300)
>  #define NAPPING_NOVCPU	2
>  
>  /* Stack frame offsets for kvmppc_hv_entry */
> -#define SFS			160
> +#define SFS			208
>  #define STACK_SLOT_TRAP		(SFS-4)
> +#define STACK_SLOT_SHORT_PATH	(SFS-8)
>  #define STACK_SLOT_TID		(SFS-16)
>  #define STACK_SLOT_PSSCR	(SFS-24)
>  #define STACK_SLOT_PID		(SFS-32)
> @@ -57,6 +58,8 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_300)
>  #define STACK_SLOT_DAWR		(SFS-56)
>  #define STACK_SLOT_DAWRX	(SFS-64)
>  #define STACK_SLOT_HFSCR	(SFS-72)
> +/* the following is used by the P9 short path */
> +#define STACK_SLOT_NVGPRS	(SFS-152)	/* 18 gprs */
>  
>  /*
>   * Call kvmppc_hv_entry in real mode.
> @@ -1020,6 +1023,9 @@ ALT_FTR_SECTION_END_IFCLR(CPU_FTR_ARCH_300)
>  no_xive:
>  #endif /* CONFIG_KVM_XICS */
>  
> +	li	r0, 0
> +	stw	r0, STACK_SLOT_SHORT_PATH(r1)
> +
>  deliver_guest_interrupt:	/* r4 = vcpu, r13 = paca */
>  	/* Check if we can deliver an external or decrementer interrupt now */
>  	ld	r0, VCPU_PENDING_EXC(r4)
> @@ -1034,13 +1040,14 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_300)
>  	bl	kvmppc_guest_entry_inject_int
>  	ld	r4, HSTATE_KVM_VCPU(r13)
>  71:
> -	ld	r10, VCPU_PC(r4)
> -	ld	r11, VCPU_MSR(r4)
>  	ld	r6, VCPU_SRR0(r4)
>  	ld	r7, VCPU_SRR1(r4)
>  	mtspr	SPRN_SRR0, r6
>  	mtspr	SPRN_SRR1, r7
>  
> +fast_guest_entry_c:
> +	ld	r10, VCPU_PC(r4)
> +	ld	r11, VCPU_MSR(r4)
>  	/* r11 = vcpu->arch.msr & ~MSR_HV */
>  	rldicl	r11, r11, 63 - MSR_HV_LG, 1
>  	rotldi	r11, r11, 1 + MSR_HV_LG
> @@ -1117,6 +1124,83 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_300)
>  	HRFI_TO_GUEST
>  	b	.
>  
> +/*
> + * Enter the guest on a P9 or later system where we have exactly
> + * one vcpu per vcore and we don't need to go to real mode
> + * (which implies that host and guest are both using radix MMU mode).
> + * r3 = vcpu pointer
> + * Most SPRs and all the VSRs have been loaded already.
> + */
> +_GLOBAL(__kvmhv_vcpu_entry_p9)
> +EXPORT_SYMBOL_GPL(__kvmhv_vcpu_entry_p9)
> +	mflr	r0
> +	std	r0, PPC_LR_STKOFF(r1)
> +	stdu	r1, -SFS(r1)
> +
> +	li	r0, 1
> +	stw	r0, STACK_SLOT_SHORT_PATH(r1)
> +
> +	std	r3, HSTATE_KVM_VCPU(r13)
> +	mfcr	r4
> +	stw	r4, SFS+8(r1)
> +
> +	std	r1, HSTATE_HOST_R1(r13)
> +
> +	reg = 14
> +	.rept	18
> +	std	reg, STACK_SLOT_NVGPRS + ((reg - 14) * 8)(r1)
> +	reg = reg + 1
> +	.endr
> +
> +	reg = 14
> +	.rept	18
> +	ld	reg, __VCPU_GPR(reg)(r3)
> +	reg = reg + 1
> +	.endr
> +
> +	mfmsr	r10
> +	std	r10, HSTATE_HOST_MSR(r13)
> +
> +	mr	r4, r3
> +	b	fast_guest_entry_c
> +guest_exit_short_path:
> +
> +	li	r0, KVM_GUEST_MODE_NONE
> +	stb	r0, HSTATE_IN_GUEST(r13)
> +
> +	reg = 14
> +	.rept	18
> +	std	reg, __VCPU_GPR(reg)(r9)
> +	reg = reg + 1
> +	.endr
> +
> +	reg = 14
> +	.rept	18
> +	ld	reg, STACK_SLOT_NVGPRS + ((reg - 14) * 8)(r1)
> +	reg = reg + 1
> +	.endr
> +
> +	lwz	r4, SFS+8(r1)
> +	mtcr	r4
> +
> +	mr	r3, r12		/* trap number */
> +
> +	addi	r1, r1, SFS
> +	ld	r0, PPC_LR_STKOFF(r1)
> +	mtlr	r0
> +
> +	/* If we are in real mode, do a rfid to get back to the caller */
> +	mfmsr	r4
> +	andi.	r5, r4, MSR_IR
> +	bnelr
> +	rldicl	r5, r4, 64 - MSR_TS_S_LG, 62	/* extract TS field */
> +	mtspr	SPRN_SRR0, r0
> +	ld	r10, HSTATE_HOST_MSR(r13)
> +	rldimi	r10, r5, MSR_TS_S_LG, 63 - MSR_TS_T_LG
> +	mtspr	SPRN_SRR1, r10
> +	RFI_TO_KERNEL
> +	b	.
> +
>  secondary_too_late:
>  	li	r12, 0
>  	stw	r12, STACK_SLOT_TRAP(r1)
> @@ -1377,6 +1461,11 @@ guest_exit_cont:		/* r9 = vcpu, r12 = trap, r13 = paca */
>  1:
>  #endif /* CONFIG_KVM_XICS */
>  
> +	/* If we came in through the P9 short path, go back out to C now */
> +	lwz	r0, STACK_SLOT_SHORT_PATH(r1)
> +	cmpwi	r0, 0
> +	bne	guest_exit_short_path
> +
>  	/* For hash guest, read the guest SLB and save it away */
>  	ld	r5, VCPU_KVM(r9)
>  	lbz	r0, KVM_RADIX(r5)
> diff --git a/arch/powerpc/kvm/book3s_xive.c b/arch/powerpc/kvm/book3s_xive.c
> index 30c2eb7..ad4a370 100644
> --- a/arch/powerpc/kvm/book3s_xive.c
> +++ b/arch/powerpc/kvm/book3s_xive.c
> @@ -62,6 +62,69 @@
>  #define XIVE_Q_GAP	2
>  
>  /*
> + * Push a vcpu's context to the XIVE on guest entry.
> + * This assumes we are in virtual mode (MMU on)
> + */
> +void kvmppc_xive_push_vcpu(struct kvm_vcpu *vcpu)
> +{
> +	void __iomem *tima = local_paca->kvm_hstate.xive_tima_virt;
> +	u64 pq;
> +
> +	if (!tima)
> +		return;
> +	eieio();
> +	__raw_writeq(vcpu->arch.xive_saved_state.w01, tima + TM_QW1_OS);
> +	__raw_writel(vcpu->arch.xive_cam_word, tima + TM_QW1_OS + TM_WORD2);
> +	vcpu->arch.xive_pushed = 1;
> +	eieio();
> +
> +	/*
> +	 * We clear the irq_pending flag. There is a small chance of a
> +	 * race vs. the escalation interrupt happening on another
> +	 * processor setting it again, but the only consequence is to
> +	 * cause a spurious wakeup on the next H_CEDE, which is not an
> +	 * issue.
> +	 */
> +	vcpu->arch.irq_pending = 0;
> +
> +	/*
> +	 * In single escalation mode, if the escalation interrupt is
> +	 * on, we mask it.
> +	 */
> +	if (vcpu->arch.xive_esc_on) {
> +		pq = __raw_readq((void __iomem *)(vcpu->arch.xive_esc_vaddr +
> +						  XIVE_ESB_SET_PQ_01));
> +		mb();
> +
> +		/*
> +		 * We have a possible subtle race here: The escalation
> +		 * interrupt might have fired and be on its way to the
> +		 * host queue while we mask it, and if we unmask it
> +		 * early enough (re-cede right away), there is a
> +		 * theorical possibility that it fires again, thus
> +		 * landing in the target queue more than once which is
> +		 * a big no-no.
> +		 *
> +		 * Fortunately, solving this is rather easy. If the
> +		 * above load setting PQ to 01 returns a previous
> +		 * value where P is set, then we know the escalation
> +		 * interrupt is somewhere on its way to the host. In
> +		 * that case we simply don't clear the xive_esc_on
> +		 * flag below. It will be eventually cleared by the
> +		 * handler for the escalation interrupt.
> +		 *
> +		 * Then, when doing a cede, we check that flag again
> +		 * before re-enabling the escalation interrupt, and if
> +		 * set, we abort the cede.
> +		 */
> +		if (!(pq & XIVE_ESB_VAL_P))
> +			/* Now P is 0, we can clear the flag */
> +			vcpu->arch.xive_esc_on = 0;
> +	}
> +}
> +EXPORT_SYMBOL_GPL(kvmppc_xive_push_vcpu);
> +
> +/*
>   * This is a simple trigger for a generic XIVE IRQ. This must
>   * only be called for interrupts that support a trigger page
>   */

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://lists.ozlabs.org/pipermail/linuxppc-dev/attachments/20181009/4f6e4695/attachment-0001.sig>


More information about the Linuxppc-dev mailing list