Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 15 Mar 2014 23:09:35 +0000 (UTC)
From:      Tycho Nightingale <tychon@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r263211 - in head/sys/amd64: include vmm vmm/intel vmm/io
Message-ID:  <201403152309.s2FN9ZWf029583@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
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)
 {



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201403152309.s2FN9ZWf029583>