Commit 5236756d authored by Nicholas Piggin's avatar Nicholas Piggin Committed by Michael Ellerman
Browse files

KVM: PPC: Book3S HV P9: Use Linux SPR save/restore to manage some host SPRs



Linux implements SPR save/restore including storage space for registers
in the task struct for process context switching. Make use of this
similarly to the way we make use of the context switching fp/vec save
restore.

This improves code reuse, allows some stack space to be saved, and helps
with avoiding VRSAVE updates if they are not required.

Signed-off-by: default avatarNicholas Piggin <npiggin@gmail.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20211123095231.1036501-39-npiggin@gmail.com
parent 022ecb96
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -113,6 +113,7 @@ static inline void clear_task_ebb(struct task_struct *t)
}

void kvmppc_save_user_regs(void);
void kvmppc_save_current_sprs(void);

extern int set_thread_tidr(struct task_struct *t);

+6 −0
Original line number Diff line number Diff line
@@ -1182,6 +1182,12 @@ void kvmppc_save_user_regs(void)
#endif
}
EXPORT_SYMBOL_GPL(kvmppc_save_user_regs);

void kvmppc_save_current_sprs(void)
{
	save_sprs(&current->thread);
}
EXPORT_SYMBOL_GPL(kvmppc_save_current_sprs);
#endif /* CONFIG_KVM_BOOK3S_HV_POSSIBLE */

static inline void restore_sprs(struct thread_struct *old_thread,
+1 −20
Original line number Diff line number Diff line
@@ -4566,9 +4566,6 @@ static int kvmppc_vcpu_run_hv(struct kvm_vcpu *vcpu)
	struct kvm_run *run = vcpu->run;
	int r;
	int srcu_idx;
	unsigned long ebb_regs[3] = {};	/* shut up GCC */
	unsigned long user_tar = 0;
	unsigned int user_vrsave;
	struct kvm *kvm;
	unsigned long msr;

@@ -4629,14 +4626,7 @@ static int kvmppc_vcpu_run_hv(struct kvm_vcpu *vcpu)

	kvmppc_save_user_regs();

	/* Save userspace EBB and other register values */
	if (cpu_has_feature(CPU_FTR_ARCH_207S)) {
		ebb_regs[0] = mfspr(SPRN_EBBHR);
		ebb_regs[1] = mfspr(SPRN_EBBRR);
		ebb_regs[2] = mfspr(SPRN_BESCR);
		user_tar = mfspr(SPRN_TAR);
	}
	user_vrsave = mfspr(SPRN_VRSAVE);
	kvmppc_save_current_sprs();

	vcpu->arch.waitp = &vcpu->arch.vcore->wait;
	vcpu->arch.pgdir = kvm->mm->pgd;
@@ -4677,15 +4667,6 @@ static int kvmppc_vcpu_run_hv(struct kvm_vcpu *vcpu)
		}
	} while (is_kvmppc_resume_guest(r));

	/* Restore userspace EBB and other register values */
	if (cpu_has_feature(CPU_FTR_ARCH_207S)) {
		mtspr(SPRN_EBBHR, ebb_regs[0]);
		mtspr(SPRN_EBBRR, ebb_regs[1]);
		mtspr(SPRN_BESCR, ebb_regs[2]);
		mtspr(SPRN_TAR, user_tar);
	}
	mtspr(SPRN_VRSAVE, user_vrsave);

	vcpu->arch.state = KVMPPC_VCPU_NOTREADY;
	atomic_dec(&kvm->arch.vcpus_running);

+0 −3
Original line number Diff line number Diff line
@@ -4,11 +4,8 @@
 * Privileged (non-hypervisor) host registers to save.
 */
struct p9_host_os_sprs {
	unsigned long dscr;
	unsigned long tidr;
	unsigned long iamr;
	unsigned long amr;
	unsigned long fscr;

	unsigned int pmc1;
	unsigned int pmc2;
+65 −28
Original line number Diff line number Diff line
@@ -231,15 +231,26 @@ EXPORT_SYMBOL_GPL(switch_pmu_to_host);
static void load_spr_state(struct kvm_vcpu *vcpu,
				struct p9_host_os_sprs *host_os_sprs)
{
	/* TAR is very fast */
	mtspr(SPRN_TAR, vcpu->arch.tar);

#ifdef CONFIG_ALTIVEC
	if (cpu_has_feature(CPU_FTR_ALTIVEC) &&
	    current->thread.vrsave != vcpu->arch.vrsave)
		mtspr(SPRN_VRSAVE, vcpu->arch.vrsave);
#endif

	if (vcpu->arch.hfscr & HFSCR_EBB) {
		if (current->thread.ebbhr != vcpu->arch.ebbhr)
			mtspr(SPRN_EBBHR, vcpu->arch.ebbhr);
		if (current->thread.ebbrr != vcpu->arch.ebbrr)
			mtspr(SPRN_EBBRR, vcpu->arch.ebbrr);
		if (current->thread.bescr != vcpu->arch.bescr)
			mtspr(SPRN_BESCR, vcpu->arch.bescr);
	}

	if (cpu_has_feature(CPU_FTR_P9_TIDR))
	if (cpu_has_feature(CPU_FTR_P9_TIDR) &&
			current->thread.tidr != vcpu->arch.tid)
		mtspr(SPRN_TIDR, vcpu->arch.tid);
	if (host_os_sprs->iamr != vcpu->arch.iamr)
		mtspr(SPRN_IAMR, vcpu->arch.iamr);
@@ -247,9 +258,9 @@ static void load_spr_state(struct kvm_vcpu *vcpu,
		mtspr(SPRN_AMR, vcpu->arch.amr);
	if (vcpu->arch.uamor != 0)
		mtspr(SPRN_UAMOR, vcpu->arch.uamor);
	if (host_os_sprs->fscr != vcpu->arch.fscr)
	if (current->thread.fscr != vcpu->arch.fscr)
		mtspr(SPRN_FSCR, vcpu->arch.fscr);
	if (host_os_sprs->dscr != vcpu->arch.dscr)
	if (current->thread.dscr != vcpu->arch.dscr)
		mtspr(SPRN_DSCR, vcpu->arch.dscr);
	if (vcpu->arch.pspb != 0)
		mtspr(SPRN_PSPB, vcpu->arch.pspb);
@@ -269,20 +280,15 @@ static void store_spr_state(struct kvm_vcpu *vcpu)
{
	vcpu->arch.tar = mfspr(SPRN_TAR);

#ifdef CONFIG_ALTIVEC
	if (cpu_has_feature(CPU_FTR_ALTIVEC))
		vcpu->arch.vrsave = mfspr(SPRN_VRSAVE);
#endif

	if (vcpu->arch.hfscr & HFSCR_EBB) {
		vcpu->arch.ebbhr = mfspr(SPRN_EBBHR);
		vcpu->arch.ebbrr = mfspr(SPRN_EBBRR);
		vcpu->arch.bescr = mfspr(SPRN_BESCR);
		/*
		 * This is like load_fp in context switching, turn off the
		 * facility after it wraps the u8 to try avoiding saving
		 * and restoring the registers each partition switch.
		 */
		if (!vcpu->arch.nested) {
			vcpu->arch.load_ebb++;
			if (!vcpu->arch.load_ebb)
				vcpu->arch.hfscr &= ~HFSCR_EBB;
		}
	}

	if (cpu_has_feature(CPU_FTR_P9_TIDR))
@@ -324,7 +330,6 @@ bool load_vcpu_state(struct kvm_vcpu *vcpu,
#ifdef CONFIG_ALTIVEC
	load_vr_state(&vcpu->arch.vr);
#endif
	mtspr(SPRN_VRSAVE, vcpu->arch.vrsave);

	return ret;
}
@@ -338,7 +343,6 @@ void store_vcpu_state(struct kvm_vcpu *vcpu)
#ifdef CONFIG_ALTIVEC
	store_vr_state(&vcpu->arch.vr);
#endif
	vcpu->arch.vrsave = mfspr(SPRN_VRSAVE);

#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
	if (cpu_has_feature(CPU_FTR_TM) ||
@@ -364,12 +368,8 @@ EXPORT_SYMBOL_GPL(store_vcpu_state);

void save_p9_host_os_sprs(struct p9_host_os_sprs *host_os_sprs)
{
	if (cpu_has_feature(CPU_FTR_P9_TIDR))
		host_os_sprs->tidr = mfspr(SPRN_TIDR);
	host_os_sprs->iamr = mfspr(SPRN_IAMR);
	host_os_sprs->amr = mfspr(SPRN_AMR);
	host_os_sprs->fscr = mfspr(SPRN_FSCR);
	host_os_sprs->dscr = mfspr(SPRN_DSCR);
}
EXPORT_SYMBOL_GPL(save_p9_host_os_sprs);

@@ -377,26 +377,63 @@ EXPORT_SYMBOL_GPL(save_p9_host_os_sprs);
void restore_p9_host_os_sprs(struct kvm_vcpu *vcpu,
			     struct p9_host_os_sprs *host_os_sprs)
{
	/*
	 * current->thread.xxx registers must all be restored to host
	 * values before a potential context switch, othrewise the context
	 * switch itself will overwrite current->thread.xxx with the values
	 * from the guest SPRs.
	 */

	mtspr(SPRN_SPRG_VDSO_WRITE, local_paca->sprg_vdso);

	if (cpu_has_feature(CPU_FTR_P9_TIDR))
		mtspr(SPRN_TIDR, host_os_sprs->tidr);
	if (cpu_has_feature(CPU_FTR_P9_TIDR) &&
			current->thread.tidr != vcpu->arch.tid)
		mtspr(SPRN_TIDR, current->thread.tidr);
	if (host_os_sprs->iamr != vcpu->arch.iamr)
		mtspr(SPRN_IAMR, host_os_sprs->iamr);
	if (vcpu->arch.uamor != 0)
		mtspr(SPRN_UAMOR, 0);
	if (host_os_sprs->amr != vcpu->arch.amr)
		mtspr(SPRN_AMR, host_os_sprs->amr);
	if (host_os_sprs->fscr != vcpu->arch.fscr)
		mtspr(SPRN_FSCR, host_os_sprs->fscr);
	if (host_os_sprs->dscr != vcpu->arch.dscr)
		mtspr(SPRN_DSCR, host_os_sprs->dscr);
	if (current->thread.fscr != vcpu->arch.fscr)
		mtspr(SPRN_FSCR, current->thread.fscr);
	if (current->thread.dscr != vcpu->arch.dscr)
		mtspr(SPRN_DSCR, current->thread.dscr);
	if (vcpu->arch.pspb != 0)
		mtspr(SPRN_PSPB, 0);

	/* Save guest CTRL register, set runlatch to 1 */
	if (!(vcpu->arch.ctrl & 1))
		mtspr(SPRN_CTRLT, 1);

#ifdef CONFIG_ALTIVEC
	if (cpu_has_feature(CPU_FTR_ALTIVEC) &&
	    vcpu->arch.vrsave != current->thread.vrsave)
		mtspr(SPRN_VRSAVE, current->thread.vrsave);
#endif
	if (vcpu->arch.hfscr & HFSCR_EBB) {
		if (vcpu->arch.bescr != current->thread.bescr)
			mtspr(SPRN_BESCR, current->thread.bescr);
		if (vcpu->arch.ebbhr != current->thread.ebbhr)
			mtspr(SPRN_EBBHR, current->thread.ebbhr);
		if (vcpu->arch.ebbrr != current->thread.ebbrr)
			mtspr(SPRN_EBBRR, current->thread.ebbrr);

		if (!vcpu->arch.nested) {
			/*
			 * This is like load_fp in context switching, turn off
			 * the facility after it wraps the u8 to try avoiding
			 * saving and restoring the registers each partition
			 * switch.
			 */
			vcpu->arch.load_ebb++;
			if (!vcpu->arch.load_ebb)
				vcpu->arch.hfscr &= ~HFSCR_EBB;
		}
	}

	if (vcpu->arch.tar != current->thread.tar)
		mtspr(SPRN_TAR, current->thread.tar);
}
EXPORT_SYMBOL_GPL(restore_p9_host_os_sprs);