KVM: PPC: Book3S HV: Improve real-mode handling of external interrupts
authorPaul Mackerras <paulus@samba.org>
Wed, 17 Apr 2013 20:31:41 +0000 (20:31 +0000)
committerAlexander Graf <agraf@suse.de>
Fri, 26 Apr 2013 18:27:32 +0000 (20:27 +0200)
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>
arch/powerpc/include/asm/reg.h
arch/powerpc/kvm/book3s_hv.c
arch/powerpc/kvm/book3s_hv_rmhandlers.S

index c9c67fc..7993224 100644 (file)
 #define     LPCR_PECE1 0x00002000      /* decrementer can cause exit */
 #define     LPCR_PECE2 0x00001000      /* machine check etc can cause exit */
 #define   LPCR_MER     0x00000800      /* Mediated External Exception */
+#define   LPCR_MER_SH  11
 #define   LPCR_LPES    0x0000000c
 #define   LPCR_LPES0   0x00000008      /* LPAR Env selector 0 */
 #define   LPCR_LPES1   0x00000004      /* LPAR Env selector 1 */
index 1619191..178521e 100644 (file)
@@ -1384,9 +1384,12 @@ static int kvmppc_run_vcpu(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
                        break;
                vc->runner = vcpu;
                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)
                                n_ceded += v->arch.ceded;
+                       else
+                               v->arch.ceded = 0;
+               }
                if (n_ceded == vc->n_runnable)
                        kvmppc_vcore_blocked(vc);
                else
index fd3b72d..b02f91e 100644 (file)
@@ -97,50 +97,51 @@ kvm_start_guest:
        li      r0,1
        stb     r0,PACA_NAPSTATELOST(r13)
 
-       /* get vcpu pointer, NULL if we have no vcpu to run */
-       ld      r4,HSTATE_KVM_VCPU(r13)
-       cmpdi   cr1,r4,0
+       /* were we napping due to cede? */
+       lbz     r0,HSTATE_NAPPING(r13)
+       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 */
        mfspr   r3,SPRN_SRR1
        rlwinm  r3,r3,44-31,0x7         /* extract wake reason field */
        cmpwi   r3,4                    /* was it an external interrupt? */
-       bne     27f
-
-       /*
-        * 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
+       bne     27f                     /* if not */
+       ld      r5,HSTATE_XICS_PHYS(r13)
+       li      r7,XICS_XIRR            /* if it was an external interrupt, */
        lwzcix  r8,r5,r7                /* get and ack the interrupt */
        sync
        clrldi. r9,r8,40                /* get interrupt source ID. */
-       beq     27f                     /* none there? */
-       cmpwi   r9,XICS_IPI
-       bne     26f
+       beq     28f                     /* none there? */
+       cmpwi   r9,XICS_IPI             /* was it an IPI? */
+       bne     29f
+       li      r0,0xff
+       li      r6,XICS_MFRR
        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 */
-
-       /* reload vcpu pointer after clearing the IPI */
+       /* get vcpu pointer, NULL if we have no vcpu to run */
        ld      r4,HSTATE_KVM_VCPU(r13)
        cmpdi   r4,0
        /* if we have no vcpu to run, go back to sleep */
        beq     kvm_no_guest
+       b       kvmppc_hv_entry
 
-       /* were we napping due to cede? */
-       lbz     r0,HSTATE_NAPPING(r13)
-       cmpwi   r0,0
-       bne     kvm_end_cede
+27:    /* XXX should handle hypervisor maintenance interrupts etc. here */
+       b       kvm_no_guest
+28:    /* SRR1 said external but ICP said nope?? */
+       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
 kvmppc_hv_entry:
@@ -483,20 +484,20 @@ toc_tlbie_lock:
        mtctr   r6
        mtxer   r7
 
+       ld      r10, VCPU_PC(r4)
+       ld      r11, VCPU_MSR(r4)
 kvmppc_cede_reentry:           /* r4 = vcpu, r13 = paca */
        ld      r6, VCPU_SRR0(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
        rotldi  r11, r11, 1 + MSR_HV_LG
        ori     r11, r11, MSR_ME
 
        /* Check if we can deliver an external or decrementer interrupt now */
        ld      r0,VCPU_PENDING_EXC(r4)
-       li      r8,(1 << BOOK3S_IRQPRIO_EXTERNAL)
-       oris    r8,r8,(1 << BOOK3S_IRQPRIO_EXTERNAL_LEVEL)@h
+       lis     r8,(1 << BOOK3S_IRQPRIO_EXTERNAL_LEVEL)@h
        and     r0,r0,r8
        cmpdi   cr1,r0,0
        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 */
 5:     mtspr   SPRN_SRR0, r6
        mtspr   SPRN_SRR1, r7
-       li      r0,0
-       stb     r0,VCPU_CEDED(r4)       /* cancel cede */
 
 fast_guest_return:
+       li      r0,0
+       stb     r0,VCPU_CEDED(r4)       /* cancel cede */
        mtspr   SPRN_HSRR0,r10
        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
         * set, we know the host wants us out so let's do it now
         */
+do_ext_interrupt:
        lbz     r0, HSTATE_HOST_IPI(r13)
        cmpwi   r0, 0
        bne     ext_interrupt_to_host
@@ -698,19 +700,9 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_206)
        lwzcix  r3, r5, r7
        rlwinm. r0, r3, 0, 0xffffff
        sync
-       bne     1f
+       beq     3f              /* if nothing pending in the ICP */
 
-       /* Nothing pending in the ICP, check for mediated interrupts
-        * 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...
+       /* We found something in the ICP...
         *
         * If it's not an IPI, stash it in the PACA and return to
         * the host, we don't (yet) handle directing real external
@@ -735,16 +727,33 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_206)
        bne-    1f
 
        /* Allright, looks like an IPI for the guest, we need to set MER */
-       mfspr   r8,SPRN_LPCR
-       ori     r8,r8,LPCR_MER
-       mtspr   SPRN_LPCR,r8
+3:
+       /* Check if any CPU is heading out to the host, if so head out too */
+       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
         * we return to the guest with MER set
         */
        andi.   r0, r11, MSR_EE
-       bne     bounce_ext_interrupt
-       mr      r4, r9
+       beq     2f
+       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
 
        /* We raced with the host, we need to resend that IPI, bummer */
@@ -1487,15 +1496,6 @@ ignore_hdec:
        mr      r4,r9
        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)
        std     r4,VCPU_DABR(r3)
        /* Work around P7 bug where DABR can get corrupted on mtspr */
@@ -1601,6 +1601,9 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_206)
        b       .
 
 kvm_end_cede:
+       /* get vcpu pointer */
+       ld      r4, HSTATE_KVM_VCPU(r13)
+
        /* Woken by external or decrementer interrupt */
        ld      r1, HSTATE_HOST_R1(r13)
 
@@ -1640,6 +1643,16 @@ kvm_end_cede:
        li      r0,0
        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 */
        lwz     r0,VCORE_ENTRY_EXIT(r5)
        cmpwi   r0,0x100
@@ -1659,8 +1672,7 @@ kvm_cede_prodded:
 
        /* we've ceded but we want to give control to the host */
 kvm_cede_exit:
-       li      r3,H_TOO_HARD
-       blr
+       b       hcall_real_fallback
 
        /* Try to handle a machine check in real mode */
 machine_check_realmode: