KVM: PPC: Book3S HV: Improve real-mode handling of external interrupts

This streamlines our handling of external interrupts that come in
while we're in the guest.  First, when waking up a hardware thread
that was napping, we split off the "napping due to H_CEDE" case
earlier, and use the code that handles an external interrupt (0x500)
in the guest to handle that too.  Secondly, the code that handles
those external interrupts now checks if any other thread is exiting
to the host before bouncing an external interrupt to the guest, and
also checks that there is actually an external interrupt pending for
the guest before setting the LPCR MER bit (mediated external request).

This also makes sure that we clear the "ceded" flag when we handle a
wakeup from cede in real mode, and fixes a potential infinite loop
in kvmppc_run_vcpu() which can occur if we ever end up with the ceded
flag set but MSR[EE] off.

Signed-off-by: Paul Mackerras <paulus@samba.org>
Signed-off-by: Alexander Graf <agraf@suse.de>
This commit is contained in:
Paul Mackerras 2013-04-17 20:31:41 +00:00 committed by Alexander Graf
parent e7d26f285b
commit 4619ac88b7
3 changed files with 80 additions and 64 deletions

View File

@ -290,6 +290,7 @@
#define LPCR_PECE1 0x00002000 /* decrementer can cause exit */ #define LPCR_PECE1 0x00002000 /* decrementer can cause exit */
#define LPCR_PECE2 0x00001000 /* machine check etc can cause exit */ #define LPCR_PECE2 0x00001000 /* machine check etc can cause exit */
#define LPCR_MER 0x00000800 /* Mediated External Exception */ #define LPCR_MER 0x00000800 /* Mediated External Exception */
#define LPCR_MER_SH 11
#define LPCR_LPES 0x0000000c #define LPCR_LPES 0x0000000c
#define LPCR_LPES0 0x00000008 /* LPAR Env selector 0 */ #define LPCR_LPES0 0x00000008 /* LPAR Env selector 0 */
#define LPCR_LPES1 0x00000004 /* LPAR Env selector 1 */ #define LPCR_LPES1 0x00000004 /* LPAR Env selector 1 */

View File

@ -1384,9 +1384,12 @@ static int kvmppc_run_vcpu(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
break; break;
vc->runner = vcpu; vc->runner = vcpu;
n_ceded = 0; n_ceded = 0;
list_for_each_entry(v, &vc->runnable_threads, arch.run_list) list_for_each_entry(v, &vc->runnable_threads, arch.run_list) {
if (!v->arch.pending_exceptions) if (!v->arch.pending_exceptions)
n_ceded += v->arch.ceded; n_ceded += v->arch.ceded;
else
v->arch.ceded = 0;
}
if (n_ceded == vc->n_runnable) if (n_ceded == vc->n_runnable)
kvmppc_vcore_blocked(vc); kvmppc_vcore_blocked(vc);
else else

View File

@ -97,50 +97,51 @@ kvm_start_guest:
li r0,1 li r0,1
stb r0,PACA_NAPSTATELOST(r13) stb r0,PACA_NAPSTATELOST(r13)
/* get vcpu pointer, NULL if we have no vcpu to run */ /* were we napping due to cede? */
ld r4,HSTATE_KVM_VCPU(r13) lbz r0,HSTATE_NAPPING(r13)
cmpdi cr1,r4,0 cmpwi r0,0
bne kvm_end_cede
/*
* We weren't napping due to cede, so this must be a secondary
* thread being woken up to run a guest, or being woken up due
* to a stray IPI. (Or due to some machine check or hypervisor
* maintenance interrupt while the core is in KVM.)
*/
/* Check the wake reason in SRR1 to see why we got here */ /* Check the wake reason in SRR1 to see why we got here */
mfspr r3,SPRN_SRR1 mfspr r3,SPRN_SRR1
rlwinm r3,r3,44-31,0x7 /* extract wake reason field */ rlwinm r3,r3,44-31,0x7 /* extract wake reason field */
cmpwi r3,4 /* was it an external interrupt? */ cmpwi r3,4 /* was it an external interrupt? */
bne 27f bne 27f /* if not */
ld r5,HSTATE_XICS_PHYS(r13)
/* li r7,XICS_XIRR /* if it was an external interrupt, */
* External interrupt - for now assume it is an IPI, since we
* should never get any other interrupts sent to offline threads.
* Only do this for secondary threads.
*/
beq cr1,25f
lwz r3,VCPU_PTID(r4)
cmpwi r3,0
beq 27f
25: ld r5,HSTATE_XICS_PHYS(r13)
li r0,0xff
li r6,XICS_MFRR
li r7,XICS_XIRR
lwzcix r8,r5,r7 /* get and ack the interrupt */ lwzcix r8,r5,r7 /* get and ack the interrupt */
sync sync
clrldi. r9,r8,40 /* get interrupt source ID. */ clrldi. r9,r8,40 /* get interrupt source ID. */
beq 27f /* none there? */ beq 28f /* none there? */
cmpwi r9,XICS_IPI cmpwi r9,XICS_IPI /* was it an IPI? */
bne 26f bne 29f
li r0,0xff
li r6,XICS_MFRR
stbcix r0,r5,r6 /* clear IPI */ stbcix r0,r5,r6 /* clear IPI */
26: stwcix r8,r5,r7 /* EOI the interrupt */ stwcix r8,r5,r7 /* EOI the interrupt */
sync /* order loading of vcpu after that */
27: /* XXX should handle hypervisor maintenance interrupts etc. here */ /* get vcpu pointer, NULL if we have no vcpu to run */
/* reload vcpu pointer after clearing the IPI */
ld r4,HSTATE_KVM_VCPU(r13) ld r4,HSTATE_KVM_VCPU(r13)
cmpdi r4,0 cmpdi r4,0
/* if we have no vcpu to run, go back to sleep */ /* if we have no vcpu to run, go back to sleep */
beq kvm_no_guest beq kvm_no_guest
b kvmppc_hv_entry
/* were we napping due to cede? */ 27: /* XXX should handle hypervisor maintenance interrupts etc. here */
lbz r0,HSTATE_NAPPING(r13) b kvm_no_guest
cmpwi r0,0 28: /* SRR1 said external but ICP said nope?? */
bne kvm_end_cede b kvm_no_guest
29: /* External non-IPI interrupt to offline secondary thread? help?? */
stw r8,HSTATE_SAVED_XIRR(r13)
b kvm_no_guest
.global kvmppc_hv_entry .global kvmppc_hv_entry
kvmppc_hv_entry: kvmppc_hv_entry:
@ -483,20 +484,20 @@ toc_tlbie_lock:
mtctr r6 mtctr r6
mtxer r7 mtxer r7
ld r10, VCPU_PC(r4)
ld r11, VCPU_MSR(r4)
kvmppc_cede_reentry: /* r4 = vcpu, r13 = paca */ kvmppc_cede_reentry: /* r4 = vcpu, r13 = paca */
ld r6, VCPU_SRR0(r4) ld r6, VCPU_SRR0(r4)
ld r7, VCPU_SRR1(r4) ld r7, VCPU_SRR1(r4)
ld r10, VCPU_PC(r4)
ld r11, VCPU_MSR(r4) /* r11 = vcpu->arch.msr & ~MSR_HV */
/* r11 = vcpu->arch.msr & ~MSR_HV */
rldicl r11, r11, 63 - MSR_HV_LG, 1 rldicl r11, r11, 63 - MSR_HV_LG, 1
rotldi r11, r11, 1 + MSR_HV_LG rotldi r11, r11, 1 + MSR_HV_LG
ori r11, r11, MSR_ME ori r11, r11, MSR_ME
/* Check if we can deliver an external or decrementer interrupt now */ /* Check if we can deliver an external or decrementer interrupt now */
ld r0,VCPU_PENDING_EXC(r4) ld r0,VCPU_PENDING_EXC(r4)
li r8,(1 << BOOK3S_IRQPRIO_EXTERNAL) lis r8,(1 << BOOK3S_IRQPRIO_EXTERNAL_LEVEL)@h
oris r8,r8,(1 << BOOK3S_IRQPRIO_EXTERNAL_LEVEL)@h
and r0,r0,r8 and r0,r0,r8
cmpdi cr1,r0,0 cmpdi cr1,r0,0
andi. r0,r11,MSR_EE andi. r0,r11,MSR_EE
@ -524,10 +525,10 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206)
/* Move SRR0 and SRR1 into the respective regs */ /* Move SRR0 and SRR1 into the respective regs */
5: mtspr SPRN_SRR0, r6 5: mtspr SPRN_SRR0, r6
mtspr SPRN_SRR1, r7 mtspr SPRN_SRR1, r7
li r0,0
stb r0,VCPU_CEDED(r4) /* cancel cede */
fast_guest_return: fast_guest_return:
li r0,0
stb r0,VCPU_CEDED(r4) /* cancel cede */
mtspr SPRN_HSRR0,r10 mtspr SPRN_HSRR0,r10
mtspr SPRN_HSRR1,r11 mtspr SPRN_HSRR1,r11
@ -686,6 +687,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_206)
/* External interrupt, first check for host_ipi. If this is /* External interrupt, first check for host_ipi. If this is
* set, we know the host wants us out so let's do it now * set, we know the host wants us out so let's do it now
*/ */
do_ext_interrupt:
lbz r0, HSTATE_HOST_IPI(r13) lbz r0, HSTATE_HOST_IPI(r13)
cmpwi r0, 0 cmpwi r0, 0
bne ext_interrupt_to_host bne ext_interrupt_to_host
@ -698,19 +700,9 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_206)
lwzcix r3, r5, r7 lwzcix r3, r5, r7
rlwinm. r0, r3, 0, 0xffffff rlwinm. r0, r3, 0, 0xffffff
sync sync
bne 1f beq 3f /* if nothing pending in the ICP */
/* Nothing pending in the ICP, check for mediated interrupts /* We found something in the ICP...
* and bounce it to the guest
*/
andi. r0, r11, MSR_EE
beq ext_interrupt_to_host /* shouldn't happen ?? */
mfspr r5, SPRN_LPCR
andi. r0, r5, LPCR_MER
bne bounce_ext_interrupt
b ext_interrupt_to_host /* shouldn't happen ?? */
1: /* We found something in the ICP...
* *
* If it's not an IPI, stash it in the PACA and return to * If it's not an IPI, stash it in the PACA and return to
* the host, we don't (yet) handle directing real external * the host, we don't (yet) handle directing real external
@ -735,16 +727,33 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_206)
bne- 1f bne- 1f
/* Allright, looks like an IPI for the guest, we need to set MER */ /* Allright, looks like an IPI for the guest, we need to set MER */
mfspr r8,SPRN_LPCR 3:
ori r8,r8,LPCR_MER /* Check if any CPU is heading out to the host, if so head out too */
mtspr SPRN_LPCR,r8 ld r5, HSTATE_KVM_VCORE(r13)
lwz r0, VCORE_ENTRY_EXIT(r5)
cmpwi r0, 0x100
bge ext_interrupt_to_host
/* See if there is a pending interrupt for the guest */
mfspr r8, SPRN_LPCR
ld r0, VCPU_PENDING_EXC(r9)
/* Insert EXTERNAL_LEVEL bit into LPCR at the MER bit position */
rldicl. r0, r0, 64 - BOOK3S_IRQPRIO_EXTERNAL_LEVEL, 63
rldimi r8, r0, LPCR_MER_SH, 63 - LPCR_MER_SH
beq 2f
/* And if the guest EE is set, we can deliver immediately, else /* And if the guest EE is set, we can deliver immediately, else
* we return to the guest with MER set * we return to the guest with MER set
*/ */
andi. r0, r11, MSR_EE andi. r0, r11, MSR_EE
bne bounce_ext_interrupt beq 2f
mr r4, r9 mtspr SPRN_SRR0, r10
mtspr SPRN_SRR1, r11
li r10, BOOK3S_INTERRUPT_EXTERNAL
li r11, (MSR_ME << 1) | 1 /* synthesize MSR_SF | MSR_ME */
rotldi r11, r11, 63
2: mr r4, r9
mtspr SPRN_LPCR, r8
b fast_guest_return b fast_guest_return
/* We raced with the host, we need to resend that IPI, bummer */ /* We raced with the host, we need to resend that IPI, bummer */
@ -1487,15 +1496,6 @@ ignore_hdec:
mr r4,r9 mr r4,r9
b fast_guest_return b fast_guest_return
bounce_ext_interrupt:
mr r4,r9
mtspr SPRN_SRR0,r10
mtspr SPRN_SRR1,r11
li r10,BOOK3S_INTERRUPT_EXTERNAL
li r11,(MSR_ME << 1) | 1 /* synthesize MSR_SF | MSR_ME */
rotldi r11,r11,63
b fast_guest_return
_GLOBAL(kvmppc_h_set_dabr) _GLOBAL(kvmppc_h_set_dabr)
std r4,VCPU_DABR(r3) std r4,VCPU_DABR(r3)
/* Work around P7 bug where DABR can get corrupted on mtspr */ /* Work around P7 bug where DABR can get corrupted on mtspr */
@ -1601,6 +1601,9 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_206)
b . b .
kvm_end_cede: kvm_end_cede:
/* get vcpu pointer */
ld r4, HSTATE_KVM_VCPU(r13)
/* Woken by external or decrementer interrupt */ /* Woken by external or decrementer interrupt */
ld r1, HSTATE_HOST_R1(r13) ld r1, HSTATE_HOST_R1(r13)
@ -1640,6 +1643,16 @@ kvm_end_cede:
li r0,0 li r0,0
stb r0,HSTATE_NAPPING(r13) stb r0,HSTATE_NAPPING(r13)
/* Check the wake reason in SRR1 to see why we got here */
mfspr r3, SPRN_SRR1
rlwinm r3, r3, 44-31, 0x7 /* extract wake reason field */
cmpwi r3, 4 /* was it an external interrupt? */
li r12, BOOK3S_INTERRUPT_EXTERNAL
mr r9, r4
ld r10, VCPU_PC(r9)
ld r11, VCPU_MSR(r9)
beq do_ext_interrupt /* if so */
/* see if any other thread is already exiting */ /* see if any other thread is already exiting */
lwz r0,VCORE_ENTRY_EXIT(r5) lwz r0,VCORE_ENTRY_EXIT(r5)
cmpwi r0,0x100 cmpwi r0,0x100
@ -1659,8 +1672,7 @@ kvm_cede_prodded:
/* we've ceded but we want to give control to the host */ /* we've ceded but we want to give control to the host */
kvm_cede_exit: kvm_cede_exit:
li r3,H_TOO_HARD b hcall_real_fallback
blr
/* Try to handle a machine check in real mode */ /* Try to handle a machine check in real mode */
machine_check_realmode: machine_check_realmode: