Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 11 Jan 2014 04:22:01 +0000 (UTC)
From:      Neel Natu <neel@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r260532 - head/sys/amd64/vmm/intel
Message-ID:  <201401110422.s0B4M1Dc060182@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: neel
Date: Sat Jan 11 04:22:00 2014
New Revision: 260532
URL: http://svnweb.freebsd.org/changeset/base/260532

Log:
  Enable "Posted Interrupt Processing" if supported by the CPU. This lets us
  inject interrupts into the guest without causing a VM-exit.
  
  This feature can be disabled by setting the tunable "hw.vmm.vmx.use_apic_pir"
  to "0".
  
  The following sysctls provide information about this feature:
  - hw.vmm.vmx.posted_interrupts (0 if disabled, 1 if enabled)
  - hw.vmm.vmx.posted_interrupt_vector (vector number used for vcpu notification)
  
  Tested on a Intel Xeon E5-2620v2 courtesy of Allan Jude at ScaleEngine.

Modified:
  head/sys/amd64/vmm/intel/vmcs.h
  head/sys/amd64/vmm/intel/vmx.c
  head/sys/amd64/vmm/intel/vmx.h

Modified: head/sys/amd64/vmm/intel/vmcs.h
==============================================================================
--- head/sys/amd64/vmm/intel/vmcs.h	Sat Jan 11 03:14:05 2014	(r260531)
+++ head/sys/amd64/vmm/intel/vmcs.h	Sat Jan 11 04:22:00 2014	(r260532)
@@ -97,6 +97,7 @@ vmcs_write(uint32_t encoding, uint64_t v
 
 /* 16-bit control fields */
 #define	VMCS_VPID			0x00000000
+#define	VMCS_PIR_VECTOR			0x00000002
 
 /* 16-bit guest-state fields */
 #define	VMCS_GUEST_ES_SELECTOR		0x00000800
@@ -129,6 +130,7 @@ vmcs_write(uint32_t encoding, uint64_t v
 #define	VMCS_TSC_OFFSET			0x00002010
 #define	VMCS_VIRTUAL_APIC		0x00002012
 #define	VMCS_APIC_ACCESS		0x00002014
+#define	VMCS_PIR_DESC			0x00002016
 #define	VMCS_EPTP			0x0000201A
 #define	VMCS_EOI_EXIT0			0x0000201C
 #define	VMCS_EOI_EXIT1			0x0000201E

Modified: head/sys/amd64/vmm/intel/vmx.c
==============================================================================
--- head/sys/amd64/vmm/intel/vmx.c	Sat Jan 11 03:14:05 2014	(r260531)
+++ head/sys/amd64/vmm/intel/vmx.c	Sat Jan 11 04:22:00 2014	(r260532)
@@ -45,11 +45,13 @@ __FBSDID("$FreeBSD$");
 #include <machine/cpufunc.h>
 #include <machine/md_var.h>
 #include <machine/segments.h>
+#include <machine/smp.h>
 #include <machine/specialreg.h>
 #include <machine/vmparam.h>
 
 #include <machine/vmm.h>
 #include "vmm_host.h"
+#include "vmm_ipi.h"
 #include "vmm_msr.h"
 #include "vmm_ktr.h"
 #include "vmm_stat.h"
@@ -172,6 +174,14 @@ static int virtual_interrupt_delivery;
 SYSCTL_INT(_hw_vmm_vmx, OID_AUTO, virtual_interrupt_delivery, CTLFLAG_RD,
     &virtual_interrupt_delivery, 0, "APICv virtual interrupt delivery support");
 
+static int posted_interrupts;
+SYSCTL_INT(_hw_vmm_vmx, OID_AUTO, posted_interrupts, CTLFLAG_RD,
+    &posted_interrupts, 0, "APICv posted interrupt support");
+
+static int pirvec;
+SYSCTL_INT(_hw_vmm_vmx, OID_AUTO, posted_interrupt_vector, CTLFLAG_RD,
+    &pirvec, 0, "APICv posted interrupt vector");
+
 static struct unrhdr *vpid_unr;
 static u_int vpid_alloc_failed;
 SYSCTL_UINT(_hw_vmm_vmx, OID_AUTO, vpid_alloc_failed, CTLFLAG_RD,
@@ -442,6 +452,9 @@ vmx_disable(void *arg __unused)
 static int
 vmx_cleanup(void)
 {
+	
+	if (pirvec != 0)
+		vmm_ipi_free(pirvec);
 
 	if (vpid_unr != NULL) {
 		delete_unrhdr(vpid_unr);
@@ -637,8 +650,32 @@ vmx_init(int ipinum)
 		procbased_ctls |= PROCBASED_USE_TPR_SHADOW;
 		procbased_ctls2 |= procbased2_vid_bits;
 		procbased_ctls2 &= ~PROCBASED2_VIRTUALIZE_X2APIC_MODE;
+
+		/*
+		 * Check for Posted Interrupts only if Virtual Interrupt
+		 * Delivery is enabled.
+		 */
+		error = vmx_set_ctlreg(MSR_VMX_PINBASED_CTLS,
+		    MSR_VMX_TRUE_PINBASED_CTLS, PINBASED_POSTED_INTERRUPT, 0,
+		    &tmp);
+		if (error == 0) {
+			pirvec = vmm_ipi_alloc();
+			if (pirvec == 0) {
+				if (bootverbose) {
+					printf("vmx_init: unable to allocate "
+					    "posted interrupt vector\n");
+				}
+			} else {
+				posted_interrupts = 1;
+				TUNABLE_INT_FETCH("hw.vmm.vmx.use_apic_pir",
+				    &posted_interrupts);
+			}
+		}
 	}
 
+	if (posted_interrupts)
+		    pinbased_ctls |= PINBASED_POSTED_INTERRUPT;
+
 	/* Initialize EPT */
 	error = ept_init(ipinum);
 	if (error) {
@@ -848,6 +885,11 @@ vmx_vminit(struct vm *vm, pmap_t pmap)
 			error += vmwrite(VMCS_EOI_EXIT2, 0);
 			error += vmwrite(VMCS_EOI_EXIT3, 0);
 		}
+		if (posted_interrupts) {
+			error += vmwrite(VMCS_PIR_VECTOR, pirvec);
+			error += vmwrite(VMCS_PIR_DESC,
+			    vtophys(&vmx->pir_desc[i]));
+		}
 		VMCLEAR(vmcs);
 		KASSERT(error == 0, ("vmx_vminit: error customizing the vmcs"));
 
@@ -2132,19 +2174,9 @@ vmx_setcap(void *arg, int vcpu, int type
         return (retval);
 }
 
-/*
- * Posted Interrupt Descriptor (described in section 29.6 of the Intel SDM).
- */
-struct pir_desc {
-	uint64_t	pir[4];
-	uint64_t	pending;
-	uint64_t	unused[3];
-} __aligned(64);
-CTASSERT(sizeof(struct pir_desc) == 64);
-
 struct vlapic_vtx {
 	struct vlapic	vlapic;
-	struct pir_desc	pir_desc;
+	struct pir_desc	*pir_desc;
 };
 
 #define	VMX_CTR_PIR(vm, vcpuid, pir_desc, notify, vector, level, msg)	\
@@ -2174,7 +2206,7 @@ vmx_set_intr_ready(struct vlapic *vlapic
 	 * XXX need to deal with level triggered interrupts
 	 */
 	vlapic_vtx = (struct vlapic_vtx *)vlapic;
-	pir_desc = &vlapic_vtx->pir_desc;
+	pir_desc = vlapic_vtx->pir_desc;
 
 	/*
 	 * Keep track of interrupt requests in the PIR descriptor. This is
@@ -2208,7 +2240,7 @@ vmx_pending_intr(struct vlapic *vlapic, 
 	KASSERT(vecptr == NULL, ("vmx_pending_intr: vecptr must be NULL"));
 
 	vlapic_vtx = (struct vlapic_vtx *)vlapic;
-	pir_desc = &vlapic_vtx->pir_desc;
+	pir_desc = vlapic_vtx->pir_desc;
 
 	pending = atomic_load_acq_long(&pir_desc->pending);
 	if (!pending)
@@ -2246,6 +2278,13 @@ vmx_intr_accepted(struct vlapic *vlapic,
 	panic("vmx_intr_accepted: not expected to be called");
 }
 
+static void
+vmx_post_intr(struct vlapic *vlapic, int hostcpu)
+{
+
+	ipi_cpu(hostcpu, pirvec);
+}
+
 /*
  * Transfer the pending interrupts in the PIR descriptor to the IRR
  * in the virtual APIC page.
@@ -2261,7 +2300,7 @@ vmx_inject_pir(struct vlapic *vlapic)
 	uint16_t intr_status_old, intr_status_new;
 
 	vlapic_vtx = (struct vlapic_vtx *)vlapic;
-	pir_desc = &vlapic_vtx->pir_desc;
+	pir_desc = vlapic_vtx->pir_desc;
 	if (atomic_cmpset_long(&pir_desc->pending, 1, 0) == 0) {
 		VCPU_CTR0(vlapic->vm, vlapic->vcpuid, "vmx_inject_pir: "
 		    "no posted interrupt pending");
@@ -2326,6 +2365,7 @@ vmx_vlapic_init(void *arg, int vcpuid)
 {
 	struct vmx *vmx;
 	struct vlapic *vlapic;
+	struct vlapic_vtx *vlapic_vtx;
 	
 	vmx = arg;
 
@@ -2334,12 +2374,18 @@ vmx_vlapic_init(void *arg, int vcpuid)
 	vlapic->vcpuid = vcpuid;
 	vlapic->apic_page = (struct LAPIC *)&vmx->apic_page[vcpuid];
 
+	vlapic_vtx = (struct vlapic_vtx *)vlapic;
+	vlapic_vtx->pir_desc = &vmx->pir_desc[vcpuid];
+
 	if (virtual_interrupt_delivery) {
 		vlapic->ops.set_intr_ready = vmx_set_intr_ready;
 		vlapic->ops.pending_intr = vmx_pending_intr;
 		vlapic->ops.intr_accepted = vmx_intr_accepted;
 	}
 
+	if (posted_interrupts)
+		vlapic->ops.post_intr = vmx_post_intr;
+
 	vlapic_init(vlapic);
 
 	return (vlapic);

Modified: head/sys/amd64/vmm/intel/vmx.h
==============================================================================
--- head/sys/amd64/vmm/intel/vmx.h	Sat Jan 11 03:14:05 2014	(r260531)
+++ head/sys/amd64/vmm/intel/vmx.h	Sat Jan 11 04:22:00 2014	(r260532)
@@ -93,11 +93,20 @@ struct apic_page {
 };
 CTASSERT(sizeof(struct apic_page) == PAGE_SIZE);
 
+/* Posted Interrupt Descriptor (described in section 29.6 of the Intel SDM) */
+struct pir_desc {
+	uint64_t	pir[4];
+	uint64_t	pending;
+	uint64_t	unused[3];
+} __aligned(64);
+CTASSERT(sizeof(struct pir_desc) == 64);
+
 /* virtual machine softc */
 struct vmx {
 	struct vmcs	vmcs[VM_MAXCPU];	/* one vmcs per virtual cpu */
 	struct apic_page apic_page[VM_MAXCPU];	/* one apic page per vcpu */
 	char		msr_bitmap[PAGE_SIZE];
+	struct pir_desc	pir_desc[VM_MAXCPU];
 	struct msr_entry guest_msrs[VM_MAXCPU][GUEST_MSR_MAX_ENTRIES];
 	struct vmxctx	ctx[VM_MAXCPU];
 	struct vmxcap	cap[VM_MAXCPU];
@@ -108,6 +117,7 @@ struct vmx {
 CTASSERT((offsetof(struct vmx, vmcs) & PAGE_MASK) == 0);
 CTASSERT((offsetof(struct vmx, msr_bitmap) & PAGE_MASK) == 0);
 CTASSERT((offsetof(struct vmx, guest_msrs) & 15) == 0);
+CTASSERT((offsetof(struct vmx, pir_desc[0]) & 63) == 0);
 
 #define	VMX_GUEST_VMEXIT	0
 #define	VMX_VMRESUME_ERROR	1



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