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
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to