vt-d: Allow pass-through of shared interrupts.
authorKeir Fraser <keir@xensource.com>
Fri, 19 Oct 2007 10:26:58 +0000 (11:26 +0100)
committerKeir Fraser <keir@xensource.com>
Fri, 19 Oct 2007 10:26:58 +0000 (11:26 +0100)
Signed-off-by: Xiaohui Xin <xiaohui.xin@intel.com>
Signed-off-by: Kevin Tian <kevin.tian@intel.com>
xen/arch/x86/hvm/svm/amd_iommu/amd-iommu-map.c
xen/arch/x86/hvm/vioapic.c
xen/arch/x86/hvm/vmx/intr.c
xen/arch/x86/hvm/vmx/vtd/dmar.c
xen/arch/x86/hvm/vmx/vtd/io.c
xen/arch/x86/hvm/vpic.c
xen/include/asm-x86/hvm/io.h
xen/include/asm-x86/hvm/irq.h
xen/include/asm-x86/iommu.h

index b46fee74938183f5b14ceee65e6c1751b4c60460..cd98ad0c8744a7b679d267342fd3bd30ac52e6aa 100644 (file)
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
+#include <xen/sched.h>
 #include <asm/hvm/iommu.h>
 #include <asm/amd-iommu.h>
 #include <asm/hvm/svm/amd-iommu-proto.h>
-#include <xen/sched.h>
 
 extern long amd_iommu_poll_comp_wait;
 
index bb8e178a312eced49712296b642cab58e7d75d03..31db4454abb0e7e3ab125f0582636da24a61c766 100644 (file)
@@ -459,7 +459,7 @@ void vioapic_update_EOI(struct domain *d, int vector)
     ent->fields.remote_irr = 0;
 
     if ( vtd_enabled )
-        hvm_dpci_eoi(gsi, ent);
+        hvm_dpci_eoi(current->domain, gsi, ent);
 
     if ( (ent->fields.trig_mode == VIOAPIC_LEVEL_TRIG) &&
          !ent->fields.mask &&
index d5ebb8099dd32164847afaa926e32108f5568cb3..4e43db601b51586a9e3daefb92a76e6ce70fb669 100644 (file)
@@ -121,10 +121,22 @@ static void vmx_dirq_assist(struct vcpu *v)
           irq < NR_IRQS;
           irq = find_next_bit(hvm_irq_dpci->dirq_mask, NR_IRQS, irq + 1) )
     {
+        stop_timer(&hvm_irq_dpci->hvm_timer[irq_to_vector(irq)]);
+
         test_and_clear_bit(irq, &hvm_irq_dpci->dirq_mask);
         device = hvm_irq_dpci->mirq[irq].device;
         intx = hvm_irq_dpci->mirq[irq].intx;
         hvm_pci_intx_assert(d, device, intx);
+
+        /*
+         * Set a timer to see if the guest can finish the interrupt or not. For
+         * example, the guest OS may unmask the PIC during boot, before the
+         * guest driver is loaded. hvm_pci_intx_assert() may succeed, but the
+         * guest will never deal with the irq, then the physical interrupt line
+         * will never be deasserted.
+         */
+        set_timer(&hvm_irq_dpci->hvm_timer[irq_to_vector(irq)],
+                  NOW() + PT_IRQ_TIME_OUT);
     }
 }
 
index 7103994ff6f37133ded4d58d916eb77bfa1df2d9..c0550fb686b5db71a41467830ed2b376358a6ac8 100644 (file)
@@ -492,7 +492,6 @@ acpi_parse_dmar(unsigned long phys_addr, unsigned long size)
 
 int acpi_dmar_init(void)
 {
-    extern int ioapic_ack_new;
     int rc;
 
     if (!vtd_enabled)
@@ -509,8 +508,5 @@ int acpi_dmar_init(void)
         return -ENODEV;
     }
 
-    /* Use fake-vector style of IOAPIC acknowledgement. */
-    ioapic_ack_new = 0;
-
     return 0;
 }
index 1e80f52435c0a95ba6d1f3018c7c494410aaeca7..b5c5ed6df0dc50a93e3385db4ca0a858872368ba 100644 (file)
 #include <public/hvm/ioreq.h>
 #include <public/domctl.h>
 
+static void pt_irq_time_out(void *data)
+{
+    struct hvm_irq_dpci_mapping *irq_map = data;
+    unsigned int guest_gsi, machine_gsi;
+    struct domain *d = irq_map->dom;
+
+    guest_gsi = irq_map->guest_gsi;
+    machine_gsi = d->arch.hvm_domain.irq.dpci->girq[guest_gsi].machine_gsi;
+    clear_bit(machine_gsi, d->arch.hvm_domain.irq.dpci->dirq_mask);
+    hvm_dpci_eoi(irq_map->dom, guest_gsi, NULL);
+}
+
 int pt_irq_create_bind_vtd(
     struct domain *d, xen_domctl_bind_pt_irq_t *pt_irq_bind)
 {
@@ -76,17 +88,22 @@ int pt_irq_create_bind_vtd(
     hvm_irq_dpci->mirq[machine_gsi].device = device;
     hvm_irq_dpci->mirq[machine_gsi].intx = intx;
     hvm_irq_dpci->mirq[machine_gsi].guest_gsi = guest_gsi;
+    hvm_irq_dpci->mirq[machine_gsi].dom = d;
 
     hvm_irq_dpci->girq[guest_gsi].valid = 1;
     hvm_irq_dpci->girq[guest_gsi].device = device;
     hvm_irq_dpci->girq[guest_gsi].intx = intx;
     hvm_irq_dpci->girq[guest_gsi].machine_gsi = machine_gsi;
+    hvm_irq_dpci->girq[guest_gsi].dom = d;
 
-    /* Deal with gsi for legacy devices */
+    init_timer(&hvm_irq_dpci->hvm_timer[irq_to_vector(machine_gsi)],
+               pt_irq_time_out, &hvm_irq_dpci->mirq[machine_gsi], 0);
+
+    /* Deal with GSI for legacy devices. */
     pirq_guest_bind(d->vcpu[0], machine_gsi, BIND_PIRQ__WILL_SHARE);
     gdprintk(XENLOG_ERR,
-        "XEN_DOMCTL_irq_mapping: m_irq = %x device = %x intx = %x\n",
-        machine_gsi, device, intx);
+             "XEN_DOMCTL_irq_mapping: m_irq = %x device = %x intx = %x\n",
+             machine_gsi, device, intx);
 
     return 0;
 }
@@ -114,22 +131,25 @@ int hvm_do_IRQ_dpci(struct domain *d, unsigned int mirq)
         hvm_irq->dpci->girq[isa_irq].machine_gsi = mirq;
     }
 
-    if ( !test_and_set_bit(mirq, hvm_irq->dpci->dirq_mask) )
-    {
-        vcpu_kick(d->vcpu[0]);
-        return 1;
-    }
-
-    dprintk(XENLOG_INFO, "mirq already pending\n");
-    return 0;
+    /*
+     * Set a timer here to avoid situations where the IRQ line is shared, and
+     * the device belonging to the pass-through guest is not yet active. In
+     * this case the guest may not pick up the interrupt (e.g., masked at the
+     * PIC) and we need to detect that.
+     */
+    set_bit(mirq, hvm_irq->dpci->dirq_mask);
+    set_timer(&hvm_irq->dpci->hvm_timer[irq_to_vector(mirq)],
+              NOW() + PT_IRQ_TIME_OUT);
+    vcpu_kick(d->vcpu[0]);
+
+    return 1;
 }
 
-void hvm_dpci_eoi(unsigned int guest_gsi, union vioapic_redir_entry *ent)
+void hvm_dpci_eoi(struct domain *d, unsigned int guest_gsi,
+                  union vioapic_redir_entry *ent)
 {
-    struct domain *d = current->domain;
     struct hvm_irq_dpci *hvm_irq_dpci = d->arch.hvm_domain.irq.dpci;
     uint32_t device, intx, machine_gsi;
-    irq_desc_t *desc;
 
     ASSERT(spin_is_locked(&d->arch.hvm_domain.irq_lock));
 
@@ -137,17 +157,15 @@ void hvm_dpci_eoi(unsigned int guest_gsi, union vioapic_redir_entry *ent)
          !hvm_irq_dpci->girq[guest_gsi].valid )
         return;
 
+    machine_gsi = hvm_irq_dpci->girq[guest_gsi].machine_gsi;
+    stop_timer(&hvm_irq_dpci->hvm_timer[irq_to_vector(machine_gsi)]);
     device = hvm_irq_dpci->girq[guest_gsi].device;
     intx = hvm_irq_dpci->girq[guest_gsi].intx;
-    machine_gsi = hvm_irq_dpci->girq[guest_gsi].machine_gsi;
     gdprintk(XENLOG_INFO, "hvm_dpci_eoi:: device %x intx %x\n",
              device, intx);
     __hvm_pci_intx_deassert(d, device, intx);
-    if ( (ent == NULL) || (ent->fields.mask == 0) )
-    {
-        desc = &irq_desc[irq_to_vector(machine_gsi)];
-        desc->handler->end(irq_to_vector(machine_gsi));
-    }
+    if ( (ent == NULL) || !ent->fields.mask )
+        pirq_guest_eoi(d, machine_gsi);
 }
 
 void iommu_domain_destroy(struct domain *d)
index ba264bb163f5605c74230648b3976d0876d4a317..161129a97f2ad8e82e5b44541caef0edb55a3efa 100644 (file)
@@ -252,7 +252,8 @@ static void vpic_ioport_write(
                 if ( vtd_enabled )
                 {
                     irq |= ((addr & 0xa0) == 0xa0) ? 8 : 0;
-                    hvm_dpci_eoi(hvm_isa_irq_to_gsi(irq), NULL);
+                    hvm_dpci_eoi(current->domain,
+                                 hvm_isa_irq_to_gsi(irq), NULL);
                 }
                 break;
             case 6: /* Set Priority                */
index 01d57794cf1bc9f32994fc556c0f875dd1f2e039..2537ac3abd7c0a2d3d6bc904b7728b81d3ddd74b 100644 (file)
@@ -151,7 +151,8 @@ void send_invalidate_req(void);
 extern void handle_mmio(unsigned long gpa);
 extern void hvm_interrupt_post(struct vcpu *v, int vector, int type);
 extern void hvm_io_assist(void);
-extern void hvm_dpci_eoi(unsigned int guest_irq, union vioapic_redir_entry *ent);
+extern void hvm_dpci_eoi(struct domain *d, unsigned int guest_irq,
+                         union vioapic_redir_entry *ent);
 
 #endif /* __ASM_X86_HVM_IO_H__ */
 
index f8b76beae3ae739761922c26152db0bed9dcdaf6..a211eb5e9fe65190b167c2d0c139bbd045404612 100644 (file)
@@ -33,6 +33,7 @@ struct hvm_irq_dpci_mapping {
     uint8_t valid;
     uint8_t device;
     uint8_t intx;
+    struct domain *dom;
     union {
         uint8_t guest_gsi;
         uint8_t machine_gsi;
@@ -45,6 +46,7 @@ struct hvm_irq_dpci {
     /* Guest IRQ to guest device/intx mapping. */
     struct hvm_irq_dpci_mapping girq[NR_IRQS];
     DECLARE_BITMAP(dirq_mask, NR_IRQS);
+    struct timer hvm_timer[NR_IRQS];
 };
 
 struct hvm_irq {
index ebda8fb794c4d011a6ac204c8a5734ee5933d921..2a119f0d661d4a394e94f13788dd3ed39185ce40 100644 (file)
@@ -79,7 +79,8 @@ void iommu_domain_teardown(struct domain *d);
 int hvm_do_IRQ_dpci(struct domain *d, unsigned int irq);
 int dpci_ioport_intercept(ioreq_t *p);
 int pt_irq_create_bind_vtd(struct domain *d,
-    xen_domctl_bind_pt_irq_t * pt_irq_bind);
+                           xen_domctl_bind_pt_irq_t *pt_irq_bind);
 
+#define PT_IRQ_TIME_OUT MILLISECS(8)
 
-#endif // _IOMMU_H_
+#endif /* _IOMMU_H_ */