forked from Minki/linux
KVM: PPC: Book3S HV: Fix some races in starting secondary threads
Subsequent patches implementing in-kernel XICS emulation will make it possible for IPIs to arrive at secondary threads at arbitrary times. This fixes some races in how we start the secondary threads, which if not fixed could lead to occasional crashes of the host kernel. This makes sure that (a) we have grabbed all the secondary threads, and verified that they are no longer in the kernel, before we start any thread, (b) that the secondary thread loads its vcpu pointer after clearing the IPI that woke it up (so we don't miss a wakeup), and (c) that the secondary thread clears its vcpu pointer before incrementing the nap count. It also removes unnecessary setting of the vcpu and vcore pointers in the paca in kvmppc_core_vcpu_load. Signed-off-by: Paul Mackerras <paulus@samba.org> Signed-off-by: Alexander Graf <agraf@suse.de>
This commit is contained in:
parent
512691d490
commit
7b444c6710
@ -64,8 +64,6 @@ void kvmppc_core_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
|
||||
{
|
||||
struct kvmppc_vcore *vc = vcpu->arch.vcore;
|
||||
|
||||
local_paca->kvm_hstate.kvm_vcpu = vcpu;
|
||||
local_paca->kvm_hstate.kvm_vcore = vc;
|
||||
if (vc->runner == vcpu && vc->vcore_state != VCORE_INACTIVE)
|
||||
vc->stolen_tb += mftb() - vc->preempt_tb;
|
||||
}
|
||||
@ -880,6 +878,7 @@ static int kvmppc_grab_hwthread(int cpu)
|
||||
|
||||
/* Ensure the thread won't go into the kernel if it wakes */
|
||||
tpaca->kvm_hstate.hwthread_req = 1;
|
||||
tpaca->kvm_hstate.kvm_vcpu = NULL;
|
||||
|
||||
/*
|
||||
* If the thread is already executing in the kernel (e.g. handling
|
||||
@ -929,7 +928,6 @@ static void kvmppc_start_thread(struct kvm_vcpu *vcpu)
|
||||
smp_wmb();
|
||||
#if defined(CONFIG_PPC_ICP_NATIVE) && defined(CONFIG_SMP)
|
||||
if (vcpu->arch.ptid) {
|
||||
kvmppc_grab_hwthread(cpu);
|
||||
xics_wake_cpu(cpu);
|
||||
++vc->n_woken;
|
||||
}
|
||||
@ -955,7 +953,8 @@ static void kvmppc_wait_for_nap(struct kvmppc_vcore *vc)
|
||||
|
||||
/*
|
||||
* Check that we are on thread 0 and that any other threads in
|
||||
* this core are off-line.
|
||||
* this core are off-line. Then grab the threads so they can't
|
||||
* enter the kernel.
|
||||
*/
|
||||
static int on_primary_thread(void)
|
||||
{
|
||||
@ -967,6 +966,17 @@ static int on_primary_thread(void)
|
||||
while (++thr < threads_per_core)
|
||||
if (cpu_online(cpu + thr))
|
||||
return 0;
|
||||
|
||||
/* Grab all hw threads so they can't go into the kernel */
|
||||
for (thr = 1; thr < threads_per_core; ++thr) {
|
||||
if (kvmppc_grab_hwthread(cpu + thr)) {
|
||||
/* Couldn't grab one; let the others go */
|
||||
do {
|
||||
kvmppc_release_hwthread(cpu + thr);
|
||||
} while (--thr > 0);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -1014,16 +1024,6 @@ static int kvmppc_run_core(struct kvmppc_vcore *vc)
|
||||
spin_lock(&vc->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure we are running on thread 0, and that
|
||||
* secondary threads are offline.
|
||||
*/
|
||||
if (threads_per_core > 1 && !on_primary_thread()) {
|
||||
list_for_each_entry(vcpu, &vc->runnable_threads, arch.run_list)
|
||||
vcpu->arch.ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Assign physical thread IDs, first to non-ceded vcpus
|
||||
* and then to ceded ones.
|
||||
@ -1043,15 +1043,22 @@ static int kvmppc_run_core(struct kvmppc_vcore *vc)
|
||||
if (vcpu->arch.ceded)
|
||||
vcpu->arch.ptid = ptid++;
|
||||
|
||||
/*
|
||||
* Make sure we are running on thread 0, and that
|
||||
* secondary threads are offline.
|
||||
*/
|
||||
if (threads_per_core > 1 && !on_primary_thread()) {
|
||||
list_for_each_entry(vcpu, &vc->runnable_threads, arch.run_list)
|
||||
vcpu->arch.ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
vc->stolen_tb += mftb() - vc->preempt_tb;
|
||||
vc->pcpu = smp_processor_id();
|
||||
list_for_each_entry(vcpu, &vc->runnable_threads, arch.run_list) {
|
||||
kvmppc_start_thread(vcpu);
|
||||
kvmppc_create_dtl_entry(vcpu, vc);
|
||||
}
|
||||
/* 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);
|
||||
|
@ -134,8 +134,11 @@ kvm_start_guest:
|
||||
|
||||
27: /* XXX should handle hypervisor maintenance interrupts etc. here */
|
||||
|
||||
/* reload vcpu pointer after clearing the IPI */
|
||||
ld r4,HSTATE_KVM_VCPU(r13)
|
||||
cmpdi r4,0
|
||||
/* if we have no vcpu to run, go back to sleep */
|
||||
beq cr1,kvm_no_guest
|
||||
beq kvm_no_guest
|
||||
|
||||
/* were we napping due to cede? */
|
||||
lbz r0,HSTATE_NAPPING(r13)
|
||||
@ -1587,6 +1590,10 @@ secondary_too_late:
|
||||
.endr
|
||||
|
||||
secondary_nap:
|
||||
/* Clear our vcpu pointer so we don't come back in early */
|
||||
li r0, 0
|
||||
std r0, HSTATE_KVM_VCPU(r13)
|
||||
lwsync
|
||||
/* Clear any pending IPI - assume we're a secondary thread */
|
||||
ld r5, HSTATE_XICS_PHYS(r13)
|
||||
li r7, XICS_XIRR
|
||||
@ -1612,8 +1619,6 @@ secondary_nap:
|
||||
kvm_no_guest:
|
||||
li r0, KVM_HWTHREAD_IN_NAP
|
||||
stb r0, HSTATE_HWTHREAD_STATE(r13)
|
||||
li r0, 0
|
||||
std r0, HSTATE_KVM_VCPU(r13)
|
||||
|
||||
li r3, LPCR_PECE0
|
||||
mfspr r4, SPRN_LPCR
|
||||
|
Loading…
Reference in New Issue
Block a user