[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH 3/5] x86/pt: introduce PT_IRQ_TYPE_GSI to bind GSIs to a PVH Dom0



The current type used to bind a legacy IRQ into a HVM guest is not suitable for
PVH Dom0 because the PCI device that's originating the interrupt is not know to
Xen.

To solve that a new bind type is introduced (PT_IRQ_TYPE_GSI), that takes a gsi
as a guest destination parameter (instead of a PCI device). This new binding
type builds on top of the existing PT_IRQ_TYPE_PCI, but since it's an identity
gsi binding some of the functionality needs to be branched (like the
assert/deassert of the gsi itself).

Right now this is limited to the hardware domain only, and to identity map
gsis.

Signed-off-by: Roger Pau Monné <roger.pau@xxxxxxxxxx>
---
Cc: Jan Beulich <jbeulich@xxxxxxxx>
Cc: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
---
 xen/arch/x86/hvm/irq.c       |  25 ++++++++
 xen/drivers/passthrough/io.c | 147 ++++++++++++++++++++++++++++++++-----------
 xen/include/public/domctl.h  |   4 ++
 xen/include/xen/hvm/irq.h    |   6 ++
 4 files changed, 144 insertions(+), 38 deletions(-)

diff --git a/xen/arch/x86/hvm/irq.c b/xen/arch/x86/hvm/irq.c
index 6e67cae9bd..2c35a1e044 100644
--- a/xen/arch/x86/hvm/irq.c
+++ b/xen/arch/x86/hvm/irq.c
@@ -118,6 +118,31 @@ void hvm_pci_intx_deassert(
     spin_unlock(&d->arch.hvm_domain.irq_lock);
 }
 
+void hvm_gsi_assert(struct domain *d, unsigned int gsi)
+{
+    struct hvm_irq *hvm_irq = hvm_domain_irq(d);
+
+    ASSERT(gsi < hvm_irq->nr_gsis);
+    ASSERT(!has_vpic(d));
+    spin_lock(&d->arch.hvm_domain.irq_lock);
+    if ( hvm_irq->gsi_assert_count[gsi]++ == 0 )
+        assert_gsi(d, gsi);
+    spin_unlock(&d->arch.hvm_domain.irq_lock);
+}
+
+void hvm_gsi_deassert(struct domain *d, unsigned int gsi)
+{
+    struct hvm_irq *hvm_irq = hvm_domain_irq(d);
+
+    ASSERT(gsi < hvm_irq->nr_gsis);
+    ASSERT(!has_vpic(d));
+    spin_lock(&d->arch.hvm_domain.irq_lock);
+    if ( hvm_irq->gsi_assert_count[gsi] )
+        hvm_irq->gsi_assert_count[gsi]--;
+    spin_unlock(&d->arch.hvm_domain.irq_lock);
+}
+
+
 void hvm_isa_irq_assert(
     struct domain *d, unsigned int isa_irq)
 {
diff --git a/xen/drivers/passthrough/io.c b/xen/drivers/passthrough/io.c
index 3345db5759..e4cd22cf18 100644
--- a/xen/drivers/passthrough/io.c
+++ b/xen/drivers/passthrough/io.c
@@ -156,6 +156,21 @@ static int pt_irq_guest_eoi(struct domain *d, struct 
hvm_pirq_dpci *pirq_dpci,
     return 0;
 }
 
+static void pt_girq_time_out(struct domain *d, unsigned int guest_gsi)
+{
+    const struct hvm_irq_dpci *dpci = domain_get_irq_dpci(d);
+    const struct hvm_girq_dpci_mapping *girq;
+
+    ASSERT(dpci);
+
+    list_for_each_entry ( girq, &dpci->girq[guest_gsi], list )
+    {
+        struct pirq *pirq = pirq_info(d, girq->machine_gsi);
+
+        pirq_dpci(pirq)->flags |= HVM_IRQ_DPCI_EOI_LATCH;
+    }
+}
+
 static void pt_irq_time_out(void *data)
 {
     struct hvm_pirq_dpci *irq_map = data;
@@ -173,18 +188,18 @@ static void pt_irq_time_out(void *data)
     }
     list_for_each_entry ( digl, &irq_map->digl_list, list )
     {
-        unsigned int guest_gsi = hvm_pci_intx_gsi(digl->device, digl->intx);
-        const struct hvm_girq_dpci_mapping *girq;
-
-        list_for_each_entry ( girq, &dpci->girq[guest_gsi], list )
-        {
-            struct pirq *pirq = pirq_info(irq_map->dom, girq->machine_gsi);
-
-            pirq_dpci(pirq)->flags |= HVM_IRQ_DPCI_EOI_LATCH;
-        }
+        pt_girq_time_out(irq_map->dom,
+                         hvm_pci_intx_gsi(digl->device, digl->intx));
         hvm_pci_intx_deassert(irq_map->dom, digl->device, digl->intx);
     }
 
+    if ( irq_map->guest_gsi != DPCI_INVALID_GSI )
+    {
+        ASSERT(is_hardware_domain(irq_map->dom));
+        pt_girq_time_out(irq_map->dom, irq_map->guest_gsi);
+        hvm_gsi_deassert(irq_map->dom, irq_map->guest_gsi);
+    }
+
     pt_pirq_iterate(irq_map->dom, pt_irq_guest_eoi, NULL);
 
     spin_unlock(&irq_map->dom->event_lock);
@@ -316,6 +331,9 @@ int pt_irq_create_bind(
     if ( pirq < 0 || pirq >= d->nr_pirqs )
         return -EINVAL;
 
+    if ( pt_irq_bind->irq_type == PT_IRQ_TYPE_GSI && !is_hardware_domain(d) )
+        return -EPERM;
+
  restart:
     spin_lock(&d->event_lock);
 
@@ -361,6 +379,7 @@ int pt_irq_create_bind(
         goto restart;
     }
 
+    pirq_dpci->guest_gsi = DPCI_INVALID_GSI;
     switch ( pt_irq_bind->irq_type )
     {
     case PT_IRQ_TYPE_MSI:
@@ -464,32 +483,56 @@ int pt_irq_create_bind(
         break;
     }
 
+    case PT_IRQ_TYPE_GSI:
     case PT_IRQ_TYPE_PCI:
     case PT_IRQ_TYPE_MSI_TRANSLATE:
     {
-        unsigned int bus = pt_irq_bind->u.pci.bus;
-        unsigned int device = pt_irq_bind->u.pci.device;
-        unsigned int intx = pt_irq_bind->u.pci.intx;
-        unsigned int guest_gsi = hvm_pci_intx_gsi(device, intx);
-        unsigned int link = hvm_pci_intx_link(device, intx);
-        struct dev_intx_gsi_link *digl = xmalloc(struct dev_intx_gsi_link);
+        unsigned int bus, device, intx, guest_gsi, link;
+        struct dev_intx_gsi_link *digl = NULL;
         struct hvm_girq_dpci_mapping *girq =
             xmalloc(struct hvm_girq_dpci_mapping);
 
-        if ( !digl || !girq )
+        if ( !girq )
         {
             spin_unlock(&d->event_lock);
-            xfree(girq);
-            xfree(digl);
             return -ENOMEM;
         }
 
-        hvm_irq_dpci->link_cnt[link]++;
+        if ( pt_irq_bind->irq_type != PT_IRQ_TYPE_GSI )
+        {
+            digl = xmalloc(struct dev_intx_gsi_link);
+            if ( !digl )
+            {
+                spin_unlock(&d->event_lock);
+                xfree(girq);
+                return -ENOMEM;
+            }
+
+            digl->bus = bus = pt_irq_bind->u.pci.bus;
+            digl->device = device = pt_irq_bind->u.pci.device;
+            digl->intx = intx = pt_irq_bind->u.pci.intx;
+            list_add_tail(&digl->list, &pirq_dpci->digl_list);
+
+            guest_gsi = hvm_pci_intx_gsi(device, intx);
+            link = hvm_pci_intx_link(device, intx);
 
-        digl->bus = bus;
-        digl->device = device;
-        digl->intx = intx;
-        list_add_tail(&digl->list, &pirq_dpci->digl_list);
+            hvm_irq_dpci->link_cnt[link]++;
+        }
+        else
+        {
+            guest_gsi = pt_irq_bind->u.gsi.gsi;
+
+            /* PT_IRQ_TYPE_GSI should only be used for identity bindings */
+            ASSERT(guest_gsi == pirq);
+            ASSERT(guest_gsi < hvm_domain_irq(d)->nr_gsis);
+
+            /*
+             * The actual PCI device(s) that use this GSI are unknown, Xen
+             * relies on machine_gsi to be identity bound into the guest.
+             */
+            bus = device = intx = 0;
+            pirq_dpci->guest_gsi = guest_gsi;
+        }
 
         girq->bus = bus;
         girq->device = device;
@@ -512,12 +555,15 @@ int pt_irq_create_bind(
                                    HVM_IRQ_DPCI_TRANSLATE;
                 share = 0;
             }
-            else    /* PT_IRQ_TYPE_PCI */
+            else    /* PT_IRQ_TYPE_PCI or PT_IRQ_TYPE_GSI */
             {
                 pirq_dpci->flags = HVM_IRQ_DPCI_MAPPED |
                                    HVM_IRQ_DPCI_MACH_PCI |
                                    HVM_IRQ_DPCI_GUEST_PCI;
-                share = BIND_PIRQ__WILL_SHARE;
+                if ( pt_irq_bind->irq_type == PT_IRQ_TYPE_PCI )
+                    share = BIND_PIRQ__WILL_SHARE;
+                else
+                    share = io_apic_get_gsi_trigger(pirq);
             }
 
             /* Init timer before binding */
@@ -535,8 +581,11 @@ int pt_irq_create_bind(
                  */
                 pirq_dpci->dom = NULL;
                 list_del(&girq->list);
-                list_del(&digl->list);
-                hvm_irq_dpci->link_cnt[link]--;
+                if ( pt_irq_bind->irq_type != PT_IRQ_TYPE_GSI)
+                {
+                    list_del(&digl->list);
+                    hvm_irq_dpci->link_cnt[link]--;
+                }
                 pirq_dpci->flags = 0;
                 pirq_cleanup_check(info, d);
                 spin_unlock(&d->event_lock);
@@ -610,14 +659,26 @@ int pt_irq_destroy_bind(
 
     if ( pt_irq_bind->irq_type != PT_IRQ_TYPE_MSI )
     {
-        unsigned int bus = pt_irq_bind->u.pci.bus;
-        unsigned int device = pt_irq_bind->u.pci.device;
-        unsigned int intx = pt_irq_bind->u.pci.intx;
-        unsigned int guest_gsi = hvm_pci_intx_gsi(device, intx);
-        unsigned int link = hvm_pci_intx_link(device, intx);
+        unsigned int bus, device, intx, guest_gsi, link;
         struct hvm_girq_dpci_mapping *girq;
         struct dev_intx_gsi_link *digl, *tmp;
 
+        if ( pt_irq_bind->irq_type == PT_IRQ_TYPE_GSI )
+        {
+            bus = device = intx = 0;
+            guest_gsi = pt_irq_bind->u.gsi.gsi;
+        }
+        else
+        {
+             bus = pt_irq_bind->u.pci.bus;
+             device = pt_irq_bind->u.pci.device;
+             intx = pt_irq_bind->u.pci.intx;
+             guest_gsi = hvm_pci_intx_gsi(device, intx);
+             link = hvm_pci_intx_link(device, intx);
+        }
+
+
+
         list_for_each_entry ( girq, &hvm_irq_dpci->girq[guest_gsi], list )
         {
             if ( girq->bus         == bus &&
@@ -638,7 +699,8 @@ int pt_irq_destroy_bind(
             return -EINVAL;
         }
 
-        hvm_irq_dpci->link_cnt[link]--;
+        if ( pt_irq_bind->irq_type != PT_IRQ_TYPE_GSI )
+            hvm_irq_dpci->link_cnt[link]--;
 
         /* clear the mirq info */
         if ( pirq_dpci && (pirq_dpci->flags & HVM_IRQ_DPCI_MAPPED) )
@@ -654,6 +716,7 @@ int pt_irq_destroy_bind(
                 }
             }
             what = list_empty(&pirq_dpci->digl_list) ? "final" : "partial";
+            pirq_dpci->guest_gsi = DPCI_INVALID_GSI;
         }
         else
             what = "bogus";
@@ -835,6 +898,11 @@ static void hvm_dirq_assist(struct domain *d, struct 
hvm_pirq_dpci *pirq_dpci)
             hvm_pci_intx_assert(d, digl->device, digl->intx);
             pirq_dpci->pending++;
         }
+        if ( pirq_dpci->guest_gsi != DPCI_INVALID_GSI )
+        {
+            hvm_gsi_assert(d, pirq_dpci->guest_gsi);
+            pirq_dpci->pending++;
+        }
 
         if ( pirq_dpci->flags & HVM_IRQ_DPCI_TRANSLATE )
         {
@@ -862,12 +930,15 @@ static void __hvm_dpci_eoi(struct domain *d,
                            const union vioapic_redir_entry *ent)
 {
     struct pirq *pirq = pirq_info(d, girq->machine_gsi);
-    struct hvm_pirq_dpci *pirq_dpci;
+    struct hvm_pirq_dpci *pirq_dpci = pirq_dpci(pirq);
 
     if ( !hvm_domain_use_pirq(d, pirq) )
-        hvm_pci_intx_deassert(d, girq->device, girq->intx);
-
-    pirq_dpci = pirq_dpci(pirq);
+    {
+        if ( pirq_dpci->guest_gsi == DPCI_INVALID_GSI )
+            hvm_pci_intx_deassert(d, girq->device, girq->intx);
+        else
+            hvm_gsi_deassert(d, pirq_dpci->guest_gsi);
+    }
 
     /*
      * No need to get vector lock for timer
@@ -891,7 +962,7 @@ void hvm_dpci_eoi(struct domain *d, unsigned int guest_gsi,
     if ( !iommu_enabled )
         return;
 
-    if ( guest_gsi < NR_ISAIRQS )
+    if ( guest_gsi < NR_ISAIRQS && !is_hardware_domain(d) )
     {
         hvm_dpci_isairq_eoi(d, guest_gsi);
         return;
diff --git a/xen/include/public/domctl.h b/xen/include/public/domctl.h
index 9e3ce21f71..31dd6ab986 100644
--- a/xen/include/public/domctl.h
+++ b/xen/include/public/domctl.h
@@ -554,6 +554,7 @@ typedef enum pt_irq_type_e {
     PT_IRQ_TYPE_MSI,
     PT_IRQ_TYPE_MSI_TRANSLATE,
     PT_IRQ_TYPE_SPI,    /* ARM: valid range 32-1019 */
+    PT_IRQ_TYPE_GSI,
 } pt_irq_type_t;
 struct xen_domctl_bind_pt_irq {
     uint32_t machine_irq;
@@ -577,6 +578,9 @@ struct xen_domctl_bind_pt_irq {
         struct {
             uint16_t spi;
         } spi;
+        struct {
+          uint32_t gsi;
+        } gsi;
     } u;
 };
 typedef struct xen_domctl_bind_pt_irq xen_domctl_bind_pt_irq_t;
diff --git a/xen/include/xen/hvm/irq.h b/xen/include/xen/hvm/irq.h
index 8304cb5725..89bc505ca6 100644
--- a/xen/include/xen/hvm/irq.h
+++ b/xen/include/xen/hvm/irq.h
@@ -91,6 +91,7 @@ struct hvm_irq_dpci {
 
 #define hvm_irq_dpci_size(cnt) offsetof(struct hvm_irq_dpci, girq[cnt])
 
+#define DPCI_INVALID_GSI UINT_MAX
 /* Machine IRQ to guest device/intx mapping. */
 struct hvm_pirq_dpci {
     uint32_t flags;
@@ -98,6 +99,7 @@ struct hvm_pirq_dpci {
     bool_t masked;
     uint16_t pending;
     struct list_head digl_list;
+    unsigned int guest_gsi;
     struct domain *dom;
     struct hvm_gmsi_info gmsi;
     struct timer timer;
@@ -124,6 +126,10 @@ void hvm_isa_irq_assert(
 void hvm_isa_irq_deassert(
     struct domain *d, unsigned int isa_irq);
 
+/* Modify state of GSIs. */
+void hvm_gsi_assert(struct domain *d, unsigned int gsi);
+void hvm_gsi_deassert(struct domain *d, unsigned int gsi);
+
 int hvm_set_pci_link_route(struct domain *d, u8 link, u8 isa_irq);
 
 int hvm_inject_msi(struct domain *d, uint64_t addr, uint32_t data);
-- 
2.12.1


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.