Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f0888f70 authored by Paul Mackerras's avatar Paul Mackerras Committed by Avi Kivity
Browse files

KVM: PPC: Book3S HV: Make secondary threads more robust against stray IPIs



Currently on POWER7, if we are running the guest on a core and we don't
need all the hardware threads, we do nothing to ensure that the unused
threads aren't executing in the kernel (other than checking that they
are offline).  We just assume they're napping and we don't do anything
to stop them trying to enter the kernel while the guest is running.
This means that a stray IPI can wake up the hardware thread and it will
then try to enter the kernel, but since the core is in guest context,
it will execute code from the guest in hypervisor mode once it turns the
MMU on, which tends to lead to crashes or hangs in the host.

This fixes the problem by adding two new one-byte flags in the
kvmppc_host_state structure in the PACA which are used to interlock
between the primary thread and the unused secondary threads when entering
the guest.  With these flags, the primary thread can ensure that the
unused secondaries are not already in kernel mode (i.e. handling a stray
IPI) and then indicate that they should not try to enter the kernel
if they do get woken for any reason.  Instead they will go into KVM code,
find that there is no vcpu to run, acknowledge and clear the IPI and go
back to nap mode.

Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
Signed-off-by: default avatarAlexander Graf <agraf@suse.de>
Signed-off-by: default avatarAvi Kivity <avi@redhat.com>
parent f6127716
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -79,6 +79,9 @@ struct kvmppc_host_state {
	u8 napping;

#ifdef CONFIG_KVM_BOOK3S_64_HV
	u8 hwthread_req;
	u8 hwthread_state;

	struct kvm_vcpu *kvm_vcpu;
	struct kvmppc_vcore *kvm_vcore;
	unsigned long xics_phys;
@@ -122,4 +125,9 @@ struct kvmppc_book3s_shadow_vcpu {

#endif /*__ASSEMBLY__ */

/* Values for kvm_state */
#define KVM_HWTHREAD_IN_KERNEL	0
#define KVM_HWTHREAD_IN_NAP	1
#define KVM_HWTHREAD_IN_KVM	2

#endif /* __ASM_KVM_BOOK3S_ASM_H__ */
+2 −0
Original line number Diff line number Diff line
@@ -540,6 +540,8 @@ int main(void)
	HSTATE_FIELD(HSTATE_IN_GUEST, in_guest);
	HSTATE_FIELD(HSTATE_RESTORE_HID5, restore_hid5);
	HSTATE_FIELD(HSTATE_NAPPING, napping);
	HSTATE_FIELD(HSTATE_HWTHREAD_REQ, hwthread_req);
	HSTATE_FIELD(HSTATE_HWTHREAD_STATE, hwthread_state);

#ifdef CONFIG_KVM_BOOK3S_64_HV
	HSTATE_FIELD(HSTATE_KVM_VCPU, kvm_vcpu);
+7 −5
Original line number Diff line number Diff line
@@ -63,11 +63,13 @@ BEGIN_FTR_SECTION
	GET_PACA(r13)

#ifdef CONFIG_KVM_BOOK3S_64_HV
	lbz	r0,PACAPROCSTART(r13)
	cmpwi	r0,0x80
	bne	1f
	li	r0,1
	stb	r0,PACAPROCSTART(r13)
	li	r0,KVM_HWTHREAD_IN_KERNEL
	stb	r0,HSTATE_HWTHREAD_STATE(r13)
	/* Order setting hwthread_state vs. testing hwthread_req */
	sync
	lbz	r0,HSTATE_HWTHREAD_REQ(r13)
	cmpwi	r0,0
	beq	1f
	b	kvm_start_guest
1:
#endif
+7 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
#include <asm/asm-offsets.h>
#include <asm/ppc-opcode.h>
#include <asm/hw_irq.h>
#include <asm/kvm_book3s_asm.h>

#undef DEBUG

@@ -81,6 +82,12 @@ _GLOBAL(power7_idle)
	std	r9,_MSR(r1)
	std	r1,PACAR1(r13)

#ifdef CONFIG_KVM_BOOK3S_64_HV
	/* Tell KVM we're napping */
	li	r4,KVM_HWTHREAD_IN_NAP
	stb	r4,HSTATE_HWTHREAD_STATE(r13)
#endif

	/* Magic NAP mode enter sequence */
	std	r0,0(r1)
	ptesync
+46 −3
Original line number Diff line number Diff line
@@ -569,6 +569,45 @@ static void kvmppc_remove_runnable(struct kvmppc_vcore *vc,
	list_del(&vcpu->arch.run_list);
}

static int kvmppc_grab_hwthread(int cpu)
{
	struct paca_struct *tpaca;
	long timeout = 1000;

	tpaca = &paca[cpu];

	/* Ensure the thread won't go into the kernel if it wakes */
	tpaca->kvm_hstate.hwthread_req = 1;

	/*
	 * If the thread is already executing in the kernel (e.g. handling
	 * a stray interrupt), wait for it to get back to nap mode.
	 * The smp_mb() is to ensure that our setting of hwthread_req
	 * is visible before we look at hwthread_state, so if this
	 * races with the code at system_reset_pSeries and the thread
	 * misses our setting of hwthread_req, we are sure to see its
	 * setting of hwthread_state, and vice versa.
	 */
	smp_mb();
	while (tpaca->kvm_hstate.hwthread_state == KVM_HWTHREAD_IN_KERNEL) {
		if (--timeout <= 0) {
			pr_err("KVM: couldn't grab cpu %d\n", cpu);
			return -EBUSY;
		}
		udelay(1);
	}
	return 0;
}

static void kvmppc_release_hwthread(int cpu)
{
	struct paca_struct *tpaca;

	tpaca = &paca[cpu];
	tpaca->kvm_hstate.hwthread_req = 0;
	tpaca->kvm_hstate.kvm_vcpu = NULL;
}

static void kvmppc_start_thread(struct kvm_vcpu *vcpu)
{
	int cpu;
@@ -588,8 +627,7 @@ static void kvmppc_start_thread(struct kvm_vcpu *vcpu)
	smp_wmb();
#if defined(CONFIG_PPC_ICP_NATIVE) && defined(CONFIG_SMP)
	if (vcpu->arch.ptid) {
		tpaca->cpu_start = 0x80;
		wmb();
		kvmppc_grab_hwthread(cpu);
		xics_wake_cpu(cpu);
		++vc->n_woken;
	}
@@ -639,7 +677,7 @@ static int kvmppc_run_core(struct kvmppc_vcore *vc)
	struct kvm_vcpu *vcpu, *vcpu0, *vnext;
	long ret;
	u64 now;
	int ptid;
	int ptid, i;

	/* don't start if any threads have a signal pending */
	list_for_each_entry(vcpu, &vc->runnable_threads, arch.run_list)
@@ -686,12 +724,17 @@ static int kvmppc_run_core(struct kvmppc_vcore *vc)
	vc->napping_threads = 0;
	list_for_each_entry(vcpu, &vc->runnable_threads, arch.run_list)
		kvmppc_start_thread(vcpu);
	/* Grab any remaining hw threads so they can't go into the kernel */
	for (i = ptid; i < threads_per_core; ++i)
		kvmppc_grab_hwthread(vc->pcpu + i);

	preempt_disable();
	spin_unlock(&vc->lock);

	kvm_guest_enter();
	__kvmppc_vcore_entry(NULL, vcpu0);
	for (i = 0; i < threads_per_core; ++i)
		kvmppc_release_hwthread(vc->pcpu + i);

	spin_lock(&vc->lock);
	/* disable sending of IPIs on virtual external irqs */
Loading