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

[Xen-devel] [PATCH v7 24/28] xen/arm: ITS: Route LPIs



From: Vijaya Kumar K <Vijaya.Kumar@xxxxxxxxxxxxxxxxxx>

Allocate and initialize irq descriptor for LPIs and
route LPIs to guest

For LPIs deactivation is not required. Hence
GICH_LR.HW is not required to set.

Signed-off-by: Vijaya Kumar K <Vijaya.Kumar@xxxxxxxxxxxxxxxxxx>
---
v7: - Subtract 8192 from irq number to index to lpi property
      table to read priority
v6: - Moved ITS specific code from irq.c to vgic.c and
      introduced vgic_vcpu_raise_lpi()
    - Renamed gicv3_set_properties to gicv3_set_irq_properties
    - Always Inject LPI to vcpu0
    - Changed route_lpi_to_guest definition
---
 xen/arch/arm/gic-v3-its.c     |   17 ++++++-
 xen/arch/arm/gic-v3.c         |   16 ++++--
 xen/arch/arm/gic.c            |   32 +++++++++++-
 xen/arch/arm/irq.c            |  112 ++++++++++++++++++++++++++++++++++++++---
 xen/arch/arm/vgic-v3-its.c    |    2 +-
 xen/arch/arm/vgic-v3.c        |   40 +++++++++++++--
 xen/arch/arm/vgic.c           |   65 ++++++++++++++++++++++++
 xen/include/asm-arm/gic-its.h |    3 ++
 xen/include/asm-arm/gic.h     |    8 ++-
 xen/include/asm-arm/irq.h     |    2 +
 xen/include/asm-arm/vgic.h    |    3 ++
 11 files changed, 278 insertions(+), 22 deletions(-)

diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
index 4875b0f..891a5e2 100644
--- a/xen/arch/arm/gic-v3-its.c
+++ b/xen/arch/arm/gic-v3-its.c
@@ -470,6 +470,21 @@ static void its_flush_and_invalidate_prop(struct irq_desc 
*desc, u8 *cfg)
     its_send_inv(its_dev, vid);
 }
 
+void its_set_lpi_properties(struct irq_desc *desc,
+                            const cpumask_t *cpu_mask,
+                            unsigned int priority)
+{
+    unsigned long flags;
+    u8 *cfg;
+
+    spin_lock_irqsave(&its_lock, flags);
+    cfg = gic_rdists->prop_page + desc->irq - FIRST_GIC_LPI;
+    *cfg = (*cfg & 3) | (priority & LPI_PRIORITY_MASK) ;
+
+    its_flush_and_invalidate_prop(desc, cfg);
+    spin_unlock_irqrestore(&its_lock, flags);
+}
+
 static void its_set_lpi_state(struct irq_desc *desc, int enable)
 {
     u8 *cfg;
@@ -923,7 +938,7 @@ int its_assign_device(struct domain *d, u32 vdevid, u32 
pdevid)
         ASSERT(i < (1 << its_data.eventID_bits));
 
         plpi = its_get_plpi(pdev, i);
-        /* TODO: Route lpi */
+        route_lpi_to_guest(d, plpi, "LPI");
     }
 
     return 0;
diff --git a/xen/arch/arm/gic-v3.c b/xen/arch/arm/gic-v3.c
index ac8a0ea..2cdc065 100644
--- a/xen/arch/arm/gic-v3.c
+++ b/xen/arch/arm/gic-v3.c
@@ -481,9 +481,9 @@ static inline uint64_t gicv3_mpidr_to_affinity(int cpu)
              MPIDR_AFFINITY_LEVEL(mpidr, 0));
 }
 
-static void gicv3_set_irq_properties(struct irq_desc *desc,
-                                     const cpumask_t *cpu_mask,
-                                     unsigned int priority)
+static void gicv3_set_line_properties(struct irq_desc *desc,
+                                      const cpumask_t *cpu_mask,
+                                      unsigned int priority)
 {
     uint32_t cfg, actual, edgebit;
     uint64_t affinity;
@@ -547,6 +547,16 @@ static int gicv3_dist_supports_lpis(void)
     return readl_relaxed(GICD + GICD_TYPER) & GICD_TYPER_LPIS_SUPPORTED;
 }
 
+static void gicv3_set_irq_properties(struct irq_desc *desc,
+                                     const cpumask_t *cpu_mask,
+                                     unsigned int priority)
+{
+    if ( gic_is_lpi(desc->irq) )
+        its_set_lpi_properties(desc, cpu_mask, priority);
+    else
+        gicv3_set_line_properties(desc, cpu_mask, priority);
+}
+
 static void __init gicv3_dist_init(void)
 {
     uint32_t type;
diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
index cefd8fc..ff24bb1 100644
--- a/xen/arch/arm/gic.c
+++ b/xen/arch/arm/gic.c
@@ -72,6 +72,13 @@ bool_t gic_is_lpi(unsigned int irq)
     return (irq >= FIRST_GIC_LPI && irq < gic_nr_irq_ids());
 }
 
+/* Validates PPIs/SGIs/SPIs/LPIs supported */
+bool_t gic_is_valid_irq(unsigned int irq)
+{
+    return (irq < gic_hw_ops->info->nr_lines || gic_is_lpi(irq));
+}
+
+/* Returns number of PPIs/SGIs/SPIs supported */
 unsigned int gic_number_lines(void)
 {
     return gic_hw_ops->info->nr_lines;
@@ -134,7 +141,8 @@ void gic_route_irq_to_xen(struct irq_desc *desc, const 
cpumask_t *cpu_mask,
                           unsigned int priority)
 {
     ASSERT(priority <= 0xff);     /* Only 8 bits of priority */
-    ASSERT(desc->irq < gic_number_lines());/* Can't route interrupts that 
don't exist */
+    /* Can't route interrupts that don't exist */
+    ASSERT(gic_is_valid_irq(desc->irq));
     ASSERT(test_bit(_IRQ_DISABLED, &desc->status));
     ASSERT(spin_is_locked(&desc->lock));
 
@@ -183,6 +191,26 @@ out:
     return res;
 }
 
+int gic_route_lpi_to_guest(struct domain *d, struct irq_desc *desc,
+                           unsigned int priority)
+{
+    ASSERT(spin_is_locked(&desc->lock));
+
+    desc->handler = get_guest_hw_irq_controller(desc->irq);
+    set_bit(_IRQ_GUEST, &desc->status);
+
+    /* Set cpumask to current processor */
+    gic_set_irq_properties(desc, cpumask_of(smp_processor_id()), priority);
+
+    /*
+     * Enable LPI by default. Each pLPI is enabled and routed
+     * when device is assigned.
+     */
+    desc->handler->enable(desc);
+
+    return 0;
+}
+
 /* This function only works with SPIs for now */
 int gic_remove_irq_from_guest(struct domain *d, unsigned int virq,
                               struct irq_desc *desc)
@@ -663,7 +691,7 @@ void gic_interrupt(struct cpu_user_regs *regs, int is_fiq)
         /* Reading IRQ will ACK it */
         irq = gic_hw_ops->read_irq();
 
-        if ( likely(irq >= 16 && irq < 1020) )
+        if ( likely((irq >= 16 && irq < 1020) || gic_is_lpi(irq)) )
         {
             local_irq_enable();
             do_IRQ(regs, irq, is_fiq);
diff --git a/xen/arch/arm/irq.c b/xen/arch/arm/irq.c
index 32c3b53..c258ab4 100644
--- a/xen/arch/arm/irq.c
+++ b/xen/arch/arm/irq.c
@@ -221,7 +221,7 @@ int request_irq(unsigned int irq, unsigned int irqflags,
      * which interrupt is which (messes up the interrupt freeing
      * logic etc).
      */
-    if ( irq >= nr_irqs )
+    if ( !gic_is_valid_irq(irq) )
         return -EINVAL;
     if ( !handler )
         return -EINVAL;
@@ -279,11 +279,16 @@ void do_IRQ(struct cpu_user_regs *regs, unsigned int irq, 
int is_fiq)
 
         set_bit(_IRQ_INPROGRESS, &desc->status);
 
-        /*
-         * The irq cannot be a PPI, we only support delivery of SPIs to
-         * guests.
-        */
-        vgic_vcpu_inject_spi(info->d, info->virq);
+#ifdef HAS_GICV3
+        if ( gic_is_lpi(irq) )
+            vgic_vcpu_raise_lpi(info->d, desc);
+        else
+#endif
+            /*
+             * The irq cannot be a PPI, we only support delivery of SPIs to
+             * guests
+             */
+            vgic_vcpu_inject_spi(info->d, info->virq);
         goto out_no_end;
     }
 
@@ -368,6 +373,9 @@ void release_irq(unsigned int irq, const void *dev_id)
     /* Wait to make sure it's not being used on another CPU */
     do { smp_mb(); } while ( test_bit(_IRQ_INPROGRESS, &desc->status) );
 
+    if ( gic_is_lpi(irq) )
+        xfree(desc->msi_desc);
+
     if ( action->free_on_release )
         xfree(action);
 }
@@ -449,9 +457,97 @@ err:
 
 bool_t is_assignable_irq(unsigned int irq)
 {
-    /* For now, we can only route SPIs to the guest */
-    return ((irq >= NR_LOCAL_IRQS) && (irq < gic_number_lines()));
+    /* For now, we can only route SPI/LPIs to the guest */
+    return (((irq >= NR_LOCAL_IRQS) && (irq < gic_number_lines())) ||
+              gic_is_lpi(irq));
+}
+
+#ifdef HAS_GICV3
+/*
+ * Route an LPI to a specific guest.
+ */
+int route_lpi_to_guest(struct domain *d, unsigned int plpi, const char 
*devname)
+{
+    struct irqaction *action;
+    struct irq_guest *info;
+    struct irq_desc *desc;
+    unsigned long flags;
+    int retval = 0;
+
+    if ( !gic_is_lpi(plpi) )
+    {
+        printk(XENLOG_G_ERR "Only LPI can be routed \n");
+        return -EINVAL;
+    }
+
+    action = xmalloc(struct irqaction);
+    if ( !action )
+        return -ENOMEM;
+
+    info = xmalloc(struct irq_guest);
+    if ( !info )
+    {
+        xfree(action);
+        return -ENOMEM;
+    }
+    info->d = d;
+
+    action->dev_id = info;
+    action->name = devname;
+    action->free_on_release = 1;
+
+    desc = irq_to_desc(plpi);
+    spin_lock_irqsave(&desc->lock, flags);
+
+    ASSERT(desc->msi_desc != NULL);
+
+    if ( desc->arch.type == DT_IRQ_TYPE_INVALID )
+    {
+        printk(XENLOG_G_ERR "LPI %u has not been configured\n", plpi);
+        retval = -EIO;
+        goto out;
+    }
+
+    /* If the IRQ is already used by same domain, do not setup again.*/
+    if ( desc->action != NULL )
+    {
+        struct domain *ad = irq_get_domain(desc);
+
+        if ( test_bit(_IRQ_GUEST, &desc->status) && d == ad )
+        {
+            printk(XENLOG_G_ERR
+                   "d%u: LPI %u is already assigned to domain %u\n",
+                   d->domain_id, plpi, d->domain_id);
+            retval = -EBUSY;
+            goto out;
+        }
+    }
+
+    retval = __setup_irq(desc, 0, action);
+    if ( retval )
+        goto out;
+
+    retval = gic_route_lpi_to_guest(d, desc, GIC_PRI_IRQ);
+
+    spin_unlock_irqrestore(&desc->lock, flags);
+
+    if ( retval )
+    {
+        release_irq(desc->irq, info);
+        goto free_info;
+    }
+
+    return 0;
+
+out:
+    spin_unlock_irqrestore(&desc->lock, flags);
+    xfree(action);
+free_info:
+    xfree(info);
+
+    return retval;
 }
+#endif
 
 /*
  * Route an IRQ to a specific guest.
diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
index 33b6e2e..4f907d2 100644
--- a/xen/arch/arm/vgic-v3-its.c
+++ b/xen/arch/arm/vgic-v3-its.c
@@ -326,7 +326,7 @@ static int vits_process_int(struct vcpu *v, struct vgic_its 
*vits,
     DPRINTK("%pv: vITS: INT: Device 0x%"PRIx32" id %"PRIu32"\n",
             v, dev_id, event);
 
-    /* TODO: Inject LPI */
+    vgic_vcpu_inject_lpi(v->domain, dev_id, event);
 
     return 0;
 }
diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
index c631c7b..4fd9c54 100644
--- a/xen/arch/arm/vgic-v3.c
+++ b/xen/arch/arm/vgic-v3.c
@@ -1420,14 +1420,44 @@ static const struct mmio_handler_ops 
vgic_distr_mmio_handler = {
 
 static int vgic_v3_get_irq_priority(struct vcpu *v, unsigned int irq)
 {
-    int priority;
+    int priority = 0;
     unsigned long flags;
-    struct vgic_irq_rank *rank = vgic_rank_irq(v, irq);
+    struct vgic_irq_rank *rank;
 
-    vgic_lock_rank(v, rank, flags);
-    priority = vgic_byte_read(rank->ipriority[REG_RANK_INDEX(8,
+    if ( !gic_is_lpi(irq) )
+    {
+        rank = vgic_rank_irq(v, irq);
+
+        vgic_lock_rank(v, rank, flags);
+        priority = vgic_byte_read(rank->ipriority[REG_RANK_INDEX(8,
                                               irq, DABT_WORD)], 0, irq & 0x3);
-    vgic_unlock_rank(v, rank, flags);
+        vgic_unlock_rank(v, rank, flags);
+    }
+    else if ( vgic_is_domain_lpi(v->domain, irq) )
+    {
+        struct vgic_its *vits = v->domain->arch.vgic.vits;
+
+        /*
+         * Guest can receive LPI before availability of LPI property table.
+         * Hence assert is wrong.
+         * TODO: Handle LPI which is valid and does not have LPI property
+         * table entry and remove below assert.
+         */
+        ASSERT(irq  < vits->prop_size);
+
+        spin_lock_irqsave(&vits->prop_lock, flags);
+        /*
+         * LPI property table always starts from 0 (==8192 LPI).
+         * So, subtract 8192 from irq value.
+         */
+        priority = *((u8*)vits->prop_page + irq - FIRST_GIC_LPI);
+        /*
+         * Bits[7:2] specify priority with bits[1:0] of priority
+         * is set to zero. Hence only mask bits[7:2]
+         */
+        priority &= LPI_PRIORITY_MASK;
+        spin_unlock_irqrestore(&vits->prop_lock, flags);
+    }
 
     return priority;
 }
diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
index 160a873..ca9ecf0 100644
--- a/xen/arch/arm/vgic.c
+++ b/xen/arch/arm/vgic.c
@@ -31,7 +31,9 @@
 
 #include <asm/mmio.h>
 #include <asm/gic.h>
+#include <asm/gic-its.h>
 #include <asm/vgic.h>
+#include <asm/vits.h>
 
 static inline struct vgic_irq_rank *vgic_get_rank(struct vcpu *v, int rank)
 {
@@ -489,6 +491,69 @@ void vgic_vcpu_inject_spi(struct domain *d, unsigned int 
virq)
     vgic_vcpu_inject_irq(v, virq);
 }
 
+#ifdef HAS_GICV3
+void vgic_vcpu_inject_lpi(struct domain *d, unsigned int vdevid,
+                          unsigned int eventID)
+{
+    struct vdevice_table dt_entry;
+    struct vitt vitt_entry;
+    uint32_t col_id;
+
+    if ( vits_get_vdevice_entry(d, vdevid, &dt_entry) )
+    {
+        dprintk(XENLOG_WARNING,
+                "Failed to read dt entry for dev 0x%"PRIx32" ..dropping\n",
+                vdevid);
+        return;
+    }
+
+    if ( dt_entry.vitt_ipa == INVALID_PADDR )
+    {
+        dprintk(XENLOG_WARNING,
+                "Event %"PRId32" of dev 0x%"PRIx32" is invalid..dropping\n",
+                eventID, vdevid);
+        return;
+    }
+
+    if ( vits_get_vitt_entry(d, vdevid, eventID, &vitt_entry) )
+    {
+        dprintk(XENLOG_WARNING,
+                "Event %"PRId32" of dev 0x%"PRIx32" is invalid..dropping\n",
+                eventID, vdevid);
+        return;
+    }
+
+    col_id = vitt_entry.vcollection;
+
+    if ( !vitt_entry.valid || !is_valid_collection(d, col_id) ||
+         !vgic_is_domain_lpi(d, vitt_entry.vlpi) )
+    {
+        dprintk(XENLOG_WARNING,
+                "vlpi %"PRId32" for dev 0x%"PRIx32" is not valid..dropping\n",
+                vitt_entry.vlpi, vdevid);
+        return;
+    }
+
+    /*
+     * We don't have vlpi to plpi mapping and hence we cannot
+     * have target on which corresponding vlpi is enabled.
+     * So for now we are always injecting vlpi on vcpu0.
+     * (See vgic_vcpu_inject_lpi() function) and so we get pending_irq
+     * structure on vcpu0.
+     * TODO: Get correct target vcpu
+     */
+    vgic_vcpu_inject_irq(d->vcpu[0], vitt_entry.vlpi);
+}
+
+void vgic_vcpu_raise_lpi(struct domain *d, struct irq_desc *desc)
+{
+    struct its_device *dev = irqdesc_get_its_device(desc);
+    unsigned int eventID = irqdesc_get_lpi_event(desc);
+
+    vgic_vcpu_inject_lpi(d, dev->virt_device_id, eventID);
+}
+#endif
+
 void arch_evtchn_inject(struct vcpu *v)
 {
     vgic_vcpu_inject_irq(v, v->domain->arch.evtchn_irq);
diff --git a/xen/include/asm-arm/gic-its.h b/xen/include/asm-arm/gic-its.h
index 8f3d0fe..52ced48 100644
--- a/xen/include/asm-arm/gic-its.h
+++ b/xen/include/asm-arm/gic-its.h
@@ -296,6 +296,9 @@ int its_init(struct rdist_prop *rdists);
 int its_cpu_init(void);
 int its_add_device(u32 devid, u32 nr_ites, struct dt_device_node *dt_its);
 int its_assign_device(struct domain *d, u32 vdevid, u32 pdevid);
+void its_set_lpi_properties(struct irq_desc *desc,
+                            const cpumask_t *cpu_mask,
+                            unsigned int priority);
 
 #endif /* __ASM_ARM_GIC_ITS_H__ */
 /*
diff --git a/xen/include/asm-arm/gic.h b/xen/include/asm-arm/gic.h
index 2073759..b6b3a4f 100644
--- a/xen/include/asm-arm/gic.h
+++ b/xen/include/asm-arm/gic.h
@@ -225,6 +225,9 @@ extern void gic_route_irq_to_xen(struct irq_desc *desc, 
const cpumask_t *cpu_mas
 extern int gic_route_irq_to_guest(struct domain *, unsigned int virq,
                                   struct irq_desc *desc,
                                   unsigned int priority);
+extern int gic_route_lpi_to_guest(struct domain *d,
+                                   struct irq_desc *desc,
+                                   unsigned int priority);
 
 /* Remove an IRQ passthrough to a guest */
 int gic_remove_irq_from_guest(struct domain *d, unsigned int virq,
@@ -281,9 +284,10 @@ extern void send_SGI_allbutself(enum gic_sgi sgi);
 /* print useful debug info */
 extern void gic_dump_info(struct vcpu *v);
 
-/* Number of interrupt lines */
+/* Number of interrupt lines (PPIs + SGIs + SPIs)*/
 extern unsigned int gic_number_lines(void);
-
+/* Check if irq is valid */
+bool_t gic_is_valid_irq(unsigned int irq);
 /* IRQ translation function for the device tree */
 int gic_irq_xlate(const u32 *intspec, unsigned int intsize,
                   unsigned int *out_hwirq, unsigned int *out_type);
diff --git a/xen/include/asm-arm/irq.h b/xen/include/asm-arm/irq.h
index c009a5e..25ed272 100644
--- a/xen/include/asm-arm/irq.h
+++ b/xen/include/asm-arm/irq.h
@@ -52,6 +52,8 @@ bool_t is_assignable_irq(unsigned int irq);
 void init_IRQ(void);
 void init_secondary_IRQ(void);
 
+int route_lpi_to_guest(struct domain *d, unsigned int irq,
+                       const char *devname);
 int route_irq_to_guest(struct domain *d, unsigned int virq,
                        unsigned int irq, const char *devname);
 int release_guest_irq(struct domain *d, unsigned int irq);
diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h
index 5ed840c..701556c 100644
--- a/xen/include/asm-arm/vgic.h
+++ b/xen/include/asm-arm/vgic.h
@@ -316,6 +316,9 @@ extern int vcpu_vgic_init(struct vcpu *v);
 extern struct vcpu *vgic_get_target_vcpu(struct vcpu *v, unsigned int irq);
 extern void vgic_vcpu_inject_irq(struct vcpu *v, unsigned int virq);
 extern void vgic_vcpu_inject_spi(struct domain *d, unsigned int virq);
+extern void vgic_vcpu_inject_lpi(struct domain *d, unsigned int devid,
+                                 unsigned int eventID);
+extern void vgic_vcpu_raise_lpi(struct domain *d, struct irq_desc *desc);
 extern void vgic_clear_pending_irqs(struct vcpu *v);
 extern struct pending_irq *irq_to_pending(struct vcpu *v, unsigned int irq);
 extern struct pending_irq *spi_to_pending(struct domain *d, unsigned int irq);
-- 
1.7.9.5


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


 


Rackspace

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