Author: tychon
Date: Sat Mar 15 23:09:34 2014
New Revision: 263211
URL: http://svnweb.freebsd.org/changeset/base/263211

Log:
  Fix a race wherein the source of an interrupt vector is wrongly
  attributed if an ExtINT arrives during interrupt injection.
  
  Also, fix a spurious interrupt if the PIC tries to raise an interrupt
  before the outstanding one is accepted.
  
  Finally, improve the PIC interrupt latency when another interrupt is
  raised immediately after the outstanding one is accepted by creating a
  vmexit rather than waiting for one to occur by happenstance.
  
  Approved by:  neel (co-mentor)

Modified:
  head/sys/amd64/include/vmm.h
  head/sys/amd64/vmm/intel/vmx.c
  head/sys/amd64/vmm/io/vatpic.c
  head/sys/amd64/vmm/io/vatpic.h
  head/sys/amd64/vmm/io/vlapic.c
  head/sys/amd64/vmm/io/vlapic_priv.h
  head/sys/amd64/vmm/vmm.c

Modified: head/sys/amd64/include/vmm.h
==============================================================================
--- head/sys/amd64/include/vmm.h        Sat Mar 15 21:58:07 2014        
(r263210)
+++ head/sys/amd64/include/vmm.h        Sat Mar 15 23:09:34 2014        
(r263211)
@@ -117,6 +117,9 @@ int vm_run(struct vm *vm, struct vm_run 
 int vm_inject_nmi(struct vm *vm, int vcpu);
 int vm_nmi_pending(struct vm *vm, int vcpuid);
 void vm_nmi_clear(struct vm *vm, int vcpuid);
+int vm_inject_extint(struct vm *vm, int vcpu);
+int vm_extint_pending(struct vm *vm, int vcpuid);
+void vm_extint_clear(struct vm *vm, int vcpuid);
 uint64_t *vm_guest_msrs(struct vm *vm, int cpu);
 struct vlapic *vm_lapic(struct vm *vm, int cpu);
 struct vioapic *vm_ioapic(struct vm *vm);

Modified: head/sys/amd64/vmm/intel/vmx.c
==============================================================================
--- head/sys/amd64/vmm/intel/vmx.c      Sat Mar 15 21:58:07 2014        
(r263210)
+++ head/sys/amd64/vmm/intel/vmx.c      Sat Mar 15 23:09:34 2014        
(r263211)
@@ -57,6 +57,7 @@ __FBSDID("$FreeBSD$");
 #include "vmm_msr.h"
 #include "vmm_ktr.h"
 #include "vmm_stat.h"
+#include "vatpic.h"
 #include "vlapic.h"
 #include "vlapic_priv.h"
 
@@ -1144,7 +1145,7 @@ static void
 vmx_inject_interrupts(struct vmx *vmx, int vcpu, struct vlapic *vlapic)
 {
        struct vm_exception exc;
-       int vector, need_nmi_exiting;
+       int vector, need_nmi_exiting, extint_pending;
        uint64_t rflags;
        uint32_t gi, info;
 
@@ -1196,7 +1197,9 @@ vmx_inject_interrupts(struct vmx *vmx, i
                        vmx_set_nmi_window_exiting(vmx, vcpu);
        }
 
-       if (virtual_interrupt_delivery) {
+       extint_pending = vm_extint_pending(vmx->vm, vcpu);
+
+       if (!extint_pending && virtual_interrupt_delivery) {
                vmx_inject_pir(vlapic);
                return;
        }
@@ -1212,9 +1215,14 @@ vmx_inject_interrupts(struct vmx *vmx, i
                return;
        }
 
-       /* Ask the local apic for a vector to inject */
-       if (!vlapic_pending_intr(vlapic, &vector))
-               return;
+       if (!extint_pending) {
+               /* Ask the local apic for a vector to inject */
+               if (!vlapic_pending_intr(vlapic, &vector))
+                       return;
+       } else {
+               /* Ask the legacy pic for a vector to inject */
+               vatpic_pending_intr(vmx->vm, &vector);
+       }
 
        KASSERT(vector >= 32 && vector <= 255, ("invalid vector %d", vector));
 
@@ -1252,8 +1260,22 @@ vmx_inject_interrupts(struct vmx *vmx, i
        info |= vector;
        vmcs_write(VMCS_ENTRY_INTR_INFO, info);
 
-       /* Update the Local APIC ISR */
-       vlapic_intr_accepted(vlapic, vector);
+       if (!extint_pending) {
+               /* Update the Local APIC ISR */
+               vlapic_intr_accepted(vlapic, vector);
+       } else {
+               vm_extint_clear(vmx->vm, vcpu);
+               vatpic_intr_accepted(vmx->vm, vector);
+
+               /*
+                * After we accepted the current ExtINT the PIC may
+                * have posted another one.  If that is the case, set
+                * the Interrupt Window Exiting execution control so
+                * we can inject that one too.
+                */
+               if (vm_extint_pending(vmx->vm, vcpu))
+                       vmx_set_int_window_exiting(vmx, vcpu);
+       }
 
        VCPU_CTR1(vmx->vm, vcpu, "Injecting hwintr at vector %d", vector);
 

Modified: head/sys/amd64/vmm/io/vatpic.c
==============================================================================
--- head/sys/amd64/vmm/io/vatpic.c      Sat Mar 15 21:58:07 2014        
(r263210)
+++ head/sys/amd64/vmm/io/vatpic.c      Sat Mar 15 23:09:34 2014        
(r263211)
@@ -82,6 +82,8 @@ struct vatpic {
        struct mtx      mtx;
        struct atpic    atpic[2];
        uint8_t         elc[2];
+
+       bool            intr_raised;
 };
 
 #define        VATPIC_CTR0(vatpic, fmt)                                        
\
@@ -148,6 +150,9 @@ vatpic_notify_intr(struct vatpic *vatpic
 
        KASSERT(VATPIC_LOCKED(vatpic), ("vatpic_notify_intr not locked"));
 
+       if (vatpic->intr_raised == true)
+               return;
+
        /* XXX master only */
        atpic = &vatpic->atpic[0];
 
@@ -155,8 +160,32 @@ vatpic_notify_intr(struct vatpic *vatpic
                VATPIC_CTR4(vatpic, "atpic notify pin = %d "
                    "(imr 0x%x irr 0x%x isr 0x%x)", pin,
                    atpic->mask, atpic->request, atpic->service);
+
+               /*
+                * PIC interrupts are routed to both the Local APIC
+                * and the I/O APIC to support operation in 1 of 3
+                * modes.
+                *
+                * 1. Legacy PIC Mode: the PIC effectively bypasses
+                * all APIC components.  In mode '1' the local APIC is
+                * disabled and LINT0 is reconfigured as INTR to
+                * deliver the PIC interrupt directly to the CPU.
+                *
+                * 2. Virtual Wire Mode: the APIC is treated as a
+                * virtual wire which delivers interrupts from the PIC
+                * to the CPU.  In mode '2' LINT0 is programmed as
+                * ExtINT to indicate that the PIC is the source of
+                * the interrupt.
+                *
+                * 3. Symmetric I/O Mode: PIC interrupts are fielded
+                * by the I/O APIC and delivered to the appropriate
+                * CPU.  In mode '3' the I/O APIC input 0 is
+                * programmed as ExtINT to indicate that the PIC is
+                * the source of the interrupt.
+                */
                lapic_set_local_intr(vatpic->vm, -1, APIC_LVT_LINT0);
                vioapic_pulse_irq(vatpic->vm, 0);
+               vatpic->intr_raised = true;
        } else {
                VATPIC_CTR3(vatpic, "atpic no eligible interrupts "
                    "(imr 0x%x irr 0x%x isr 0x%x)",
@@ -384,7 +413,7 @@ vatpic_pulse_irq(struct vm *vm, int irq)
        return (vatpic_set_irqstate(vm, irq, IRQSTATE_PULSE));
 }
 
-int
+void
 vatpic_pending_intr(struct vm *vm, int *vecptr)
 {
        struct vatpic *vatpic;
@@ -405,8 +434,6 @@ vatpic_pending_intr(struct vm *vm, int *
        *vecptr = atpic->irq_base + pin;
 
        VATPIC_UNLOCK(vatpic);
-
-       return (1);
 }
 
 void
@@ -422,6 +449,8 @@ vatpic_intr_accepted(struct vm *vm, int 
        atpic = &vatpic->atpic[0];
 
        VATPIC_LOCK(vatpic);
+       vatpic->intr_raised = false;
+
        pin = vector & 0x7;
 
        if (atpic->acnt[pin] == 0)

Modified: head/sys/amd64/vmm/io/vatpic.h
==============================================================================
--- head/sys/amd64/vmm/io/vatpic.h      Sat Mar 15 21:58:07 2014        
(r263210)
+++ head/sys/amd64/vmm/io/vatpic.h      Sat Mar 15 23:09:34 2014        
(r263211)
@@ -47,7 +47,7 @@ int vatpic_assert_irq(struct vm *vm, int
 int vatpic_deassert_irq(struct vm *vm, int irq);
 int vatpic_pulse_irq(struct vm *vm, int irq);
 
-int vatpic_pending_intr(struct vm *vm, int *vecptr);
+void vatpic_pending_intr(struct vm *vm, int *vecptr);
 void vatpic_intr_accepted(struct vm *vm, int vector);
 
 #endif /* _VATPIC_H_ */

Modified: head/sys/amd64/vmm/io/vlapic.c
==============================================================================
--- head/sys/amd64/vmm/io/vlapic.c      Sat Mar 15 21:58:07 2014        
(r263210)
+++ head/sys/amd64/vmm/io/vlapic.c      Sat Mar 15 23:09:34 2014        
(r263211)
@@ -52,7 +52,6 @@ __FBSDID("$FreeBSD$");
 
 #include "vlapic.h"
 #include "vlapic_priv.h"
-#include "vatpic.h"
 #include "vioapic.h"
 
 #define        PRIO(x)                 ((x) >> 4)
@@ -300,16 +299,6 @@ vlapic_set_intr_ready(struct vlapic *vla
        return (1);
 }
 
-static VMM_STAT(VLAPIC_EXTINT_COUNT, "number of ExtINTs received by vlapic");
-
-static void
-vlapic_deliver_extint(struct vlapic *vlapic)
-{
-       vmm_stat_incr(vlapic->vm, vlapic->vcpuid, VLAPIC_EXTINT_COUNT, 1);
-       vlapic->extint_pending = true;
-       vcpu_notify_event(vlapic->vm, vlapic->vcpuid, false);
-}
-
 static __inline uint32_t *
 vlapic_get_lvtptr(struct vlapic *vlapic, uint32_t offset)
 {
@@ -460,7 +449,7 @@ vlapic_fire_lvt(struct vlapic *vlapic, u
                vm_inject_nmi(vlapic->vm, vlapic->vcpuid);
                break;
        case APIC_LVT_DM_EXTINT:
-               vlapic_deliver_extint(vlapic);
+               vm_inject_extint(vlapic->vm, vlapic->vcpuid);
                break;
        default:
                // Other modes ignored
@@ -673,7 +662,7 @@ vlapic_trigger_lvt(struct vlapic *vlapic
                */
                switch (vector) {
                        case APIC_LVT_LINT0:
-                               vlapic_deliver_extint(vlapic);
+                               vm_inject_extint(vlapic->vm, vlapic->vcpuid);
                                break;
                        case APIC_LVT_LINT1:
                                vm_inject_nmi(vlapic->vm, vlapic->vcpuid);
@@ -1053,13 +1042,6 @@ vlapic_pending_intr(struct vlapic *vlapi
        int              idx, i, bitpos, vector;
        uint32_t        *irrptr, val;
 
-       if (vlapic->extint_pending) {
-               if (vecptr == NULL)
-                       return (1);
-               else
-                       return (vatpic_pending_intr(vlapic->vm, vecptr));
-       }
-
        if (vlapic->ops.pending_intr)
                return ((*vlapic->ops.pending_intr)(vlapic, vecptr));
 
@@ -1094,12 +1076,6 @@ vlapic_intr_accepted(struct vlapic *vlap
        uint32_t        *irrptr, *isrptr;
        int             idx, stk_top;
 
-       if (vlapic->extint_pending) {
-               vlapic->extint_pending = false;
-               vatpic_intr_accepted(vlapic->vm, vector);
-               return;
-       }
-
        if (vlapic->ops.intr_accepted)
                return ((*vlapic->ops.intr_accepted)(vlapic, vector));
 
@@ -1539,7 +1515,7 @@ vlapic_deliver_intr(struct vm *vm, bool 
                vcpuid--;
                CPU_CLR(vcpuid, &dmask);
                if (delmode == IOART_DELEXINT) {
-                       vlapic_deliver_extint(vm_lapic(vm, vcpuid));
+                       vm_inject_extint(vm, vcpuid);
                } else {
                        lapic_set_intr(vm, vcpuid, vec, level);
                }

Modified: head/sys/amd64/vmm/io/vlapic_priv.h
==============================================================================
--- head/sys/amd64/vmm/io/vlapic_priv.h Sat Mar 15 21:58:07 2014        
(r263210)
+++ head/sys/amd64/vmm/io/vlapic_priv.h Sat Mar 15 23:09:34 2014        
(r263211)
@@ -156,8 +156,6 @@ struct vlapic {
        uint32_t                esr_pending;
        int                     esr_firing;
 
-       bool                    extint_pending;
-
        struct callout  callout;        /* vlapic timer */
        struct bintime  timer_fire_bt;  /* callout expiry time */
        struct bintime  timer_freq_bt;  /* timer frequency */

Modified: head/sys/amd64/vmm/vmm.c
==============================================================================
--- head/sys/amd64/vmm/vmm.c    Sat Mar 15 21:58:07 2014        (r263210)
+++ head/sys/amd64/vmm/vmm.c    Sat Mar 15 23:09:34 2014        (r263211)
@@ -95,6 +95,7 @@ struct vcpu {
        struct vm_exit  exitinfo;
        enum x2apic_state x2apic_state;
        int             nmi_pending;
+       int             extint_pending;
        struct vm_exception exception;
        int             exception_pending;
 };
@@ -1351,6 +1352,53 @@ vm_nmi_clear(struct vm *vm, int vcpuid)
        vmm_stat_incr(vm, vcpuid, VCPU_NMI_COUNT, 1);
 }
 
+static VMM_STAT(VCPU_EXTINT_COUNT, "number of ExtINTs delivered to vcpu");
+
+int
+vm_inject_extint(struct vm *vm, int vcpuid)
+{
+       struct vcpu *vcpu;
+
+       if (vcpuid < 0 || vcpuid >= VM_MAXCPU)
+               return (EINVAL);
+
+       vcpu = &vm->vcpu[vcpuid];
+
+       vcpu->extint_pending = 1;
+       vcpu_notify_event(vm, vcpuid, false);
+       return (0);
+}
+
+int
+vm_extint_pending(struct vm *vm, int vcpuid)
+{
+       struct vcpu *vcpu;
+
+       if (vcpuid < 0 || vcpuid >= VM_MAXCPU)
+               panic("vm_extint_pending: invalid vcpuid %d", vcpuid);
+
+       vcpu = &vm->vcpu[vcpuid];
+
+       return (vcpu->extint_pending);
+}
+
+void
+vm_extint_clear(struct vm *vm, int vcpuid)
+{
+       struct vcpu *vcpu;
+
+       if (vcpuid < 0 || vcpuid >= VM_MAXCPU)
+               panic("vm_extint_pending: invalid vcpuid %d", vcpuid);
+
+       vcpu = &vm->vcpu[vcpuid];
+
+       if (vcpu->extint_pending == 0)
+               panic("vm_extint_clear: inconsistent extint_pending state");
+
+       vcpu->extint_pending = 0;
+       vmm_stat_incr(vm, vcpuid, VCPU_EXTINT_COUNT, 1);
+}
+
 int
 vm_get_capability(struct vm *vm, int vcpu, int type, int *retval)
 {
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to