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)
This commit is contained in:
tychon 2014-03-15 23:09:34 +00:00
parent 47bd98a787
commit 5460439295
7 changed files with 116 additions and 40 deletions

View File

@ -117,6 +117,9 @@ int vm_run(struct vm *vm, struct vm_run *vmrun);
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);

View File

@ -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, int vcpu, struct vlapic *vlapic)
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, int vcpu, struct vlapic *vlapic)
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, int vcpu, struct vlapic *vlapic)
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);

View File

@ -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)
*vecptr = atpic->irq_base + pin;
VATPIC_UNLOCK(vatpic);
return (1);
}
void
@ -422,6 +449,8 @@ vatpic_intr_accepted(struct vm *vm, int vector)
atpic = &vatpic->atpic[0];
VATPIC_LOCK(vatpic);
vatpic->intr_raised = false;
pin = vector & 0x7;
if (atpic->acnt[pin] == 0)

View File

@ -47,7 +47,7 @@ int vatpic_assert_irq(struct vm *vm, int irq);
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_ */

View File

@ -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 *vlapic, int vector, bool level)
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, uint32_t lvt)
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, int vector)
*/
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 *vlapic, int *vecptr)
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 *vlapic, int vector)
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 level, uint32_t dest, bool phys,
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);
}

View File

@ -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 */

View File

@ -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)
{