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

[Xen-devel] [RFC PATCH v2 18/22] xen/arm: its: Dynamic allocation of LPI descriptors



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

Number of LPIs supported by GICv3 is huge. Boot time
allocation of irq descriptors and pending_irq descritors
is not viable.

With this patch, allocate irq/pending_irq descritors for
LPIs on-demand and manage using radix tree

Signed-off-by: Vijaya Kumar K <Vijaya.Kumar@xxxxxxxxxxxxxxxxxx>
---
 xen/arch/arm/irq.c           |  183 +++++++++++++++++++++++++++++++++++++++++-
 xen/arch/arm/vgic.c          |   20 ++++-
 xen/include/asm-arm/domain.h |    4 +
 xen/include/asm-arm/gic.h    |    1 +
 xen/include/asm-arm/irq.h    |   10 +++
 5 files changed, 213 insertions(+), 5 deletions(-)

diff --git a/xen/arch/arm/irq.c b/xen/arch/arm/irq.c
index d02f4cf..0d3bf9a 100644
--- a/xen/arch/arm/irq.c
+++ b/xen/arch/arm/irq.c
@@ -30,6 +30,8 @@
 
 static unsigned int local_irqs_type[NR_LOCAL_IRQS];
 static DEFINE_SPINLOCK(local_irqs_type_lock);
+static DEFINE_SPINLOCK(radix_tree_desc_lock);
+static struct radix_tree_root desc_root;
 
 static void ack_none(struct irq_desc *irq)
 {
@@ -51,18 +53,149 @@ hw_irq_controller no_irq_type = {
 static irq_desc_t irq_desc[NR_IRQS];
 static DEFINE_PER_CPU(irq_desc_t[NR_LOCAL_IRQS], local_irq_desc);
 
+static void init_one_irq_data(int irq, struct irq_desc *desc);
+
+struct irq_desc * find_irq_desc(struct radix_tree_root *root_node, int irq)
+{
+    unsigned long flags;
+    struct irq_desc *desc;
+
+    spin_lock_irqsave(&radix_tree_desc_lock, flags);
+    desc = radix_tree_lookup(root_node, irq);
+    spin_unlock_irqrestore(&radix_tree_desc_lock, flags);
+
+    return desc;
+}
+
+struct pending_irq *find_pending_irq_desc(struct domain *d, int irq)
+{
+    unsigned long flags;
+    struct pending_irq *p;
+
+    spin_lock_irqsave(&d->arch.vgic.pending_lpi_lock, flags);
+    p = radix_tree_lookup(&d->arch.vgic.pending_lpis, irq);
+    spin_unlock_irqrestore(&d->arch.vgic.pending_lpi_lock, flags);
+
+    return p;
+}
+
+struct irq_desc *insert_irq_desc(struct radix_tree_root *root_node, int irq)
+{
+    unsigned long flags;
+    struct irq_desc *desc;
+    int ret;
+
+    spin_lock_irqsave(&radix_tree_desc_lock, flags);
+    desc = radix_tree_lookup(root_node, irq);
+    if ( desc == NULL )
+    {
+
+        desc = xzalloc(struct irq_desc);
+        if ( desc == NULL )
+            goto err;
+        init_one_irq_data(irq, desc);
+        ret = radix_tree_insert(root_node, irq, desc);
+        if ( ret )
+        {
+            xfree(desc);
+            goto err;
+        }
+    }
+    spin_unlock_irqrestore(&radix_tree_desc_lock, flags);
+
+    return desc;
+err:
+    spin_unlock_irqrestore(&radix_tree_desc_lock, flags);
+
+    return NULL;
+}
+
+struct pending_irq *insert_pending_irq_desc(struct domain *d, int irq)
+{
+    unsigned long flags;
+    int ret;
+    struct pending_irq *p;
+
+    spin_lock_irqsave(&d->arch.vgic.pending_lpi_lock, flags);
+    p = radix_tree_lookup(&d->arch.vgic.pending_lpis, irq);
+    if ( p == NULL )
+    {
+        if ( (p = xzalloc(struct pending_irq)) == NULL )
+            goto err;
+        ret = radix_tree_insert(&d->arch.vgic.pending_lpis, irq, p);
+        if ( ret )
+        {
+            xfree(p);
+            goto err;
+        }
+        INIT_LIST_HEAD(&p->inflight);
+        INIT_LIST_HEAD(&p->lr_queue);
+    }
+    spin_unlock_irqrestore(&d->arch.vgic.pending_lpi_lock, flags);
+
+    return p;
+err:
+    spin_unlock_irqrestore(&d->arch.vgic.pending_lpi_lock, flags);
+
+    return NULL;
+}
+
+struct irq_desc *delete_irq_desc(struct radix_tree_root *root_node, int irq)
+{
+    unsigned long flags;
+    struct irq_desc *desc;
+
+    spin_lock_irqsave(&radix_tree_desc_lock, flags);
+    desc = radix_tree_delete(root_node, irq);
+    spin_unlock_irqrestore(&radix_tree_desc_lock, flags);
+
+    return desc;
+}
+
+struct pending_irq *delete_pending_irq_desc(struct domain *d, int irq)
+{
+    unsigned long flags;
+    struct pending_irq *p;
+
+    spin_lock_irqsave(&d->arch.vgic.pending_lpi_lock, flags);
+    p = radix_tree_delete(&d->arch.vgic.pending_lpis, irq);
+    spin_unlock_irqrestore(&d->arch.vgic.pending_lpi_lock, flags);
+
+    return p; 
+}
+
 irq_desc_t *__irq_to_desc(int irq)
 {
+    struct irq_desc *desc = NULL;
+
     if (irq < NR_LOCAL_IRQS) return &this_cpu(local_irq_desc)[irq];
-    return &irq_desc[irq-NR_LOCAL_IRQS];
+    else if ( irq >= NR_LOCAL_IRQS && irq < NR_IRQS)
+        return &irq_desc[irq-NR_LOCAL_IRQS];
+    else
+    {
+        if ( is_lpi(irq) )
+            desc = find_irq_desc(&desc_root, irq);
+        else
+            BUG();
+    }
+
+    return desc;
 }
 
-int __init arch_init_one_irq_desc(struct irq_desc *desc)
+int arch_init_one_irq_desc(struct irq_desc *desc)
 {
     desc->arch.type = DT_IRQ_TYPE_INVALID;
     return 0;
 }
 
+static void init_one_irq_data(int irq, struct irq_desc *desc)
+{
+        init_one_irq_desc(desc);
+        desc->irq = irq;
+        desc->arch.virq = 0;
+        desc->action  = NULL;
+        desc->arch.dev = NULL;
+}
 
 static int __init init_irq_data(void)
 {
@@ -72,7 +205,9 @@ static int __init init_irq_data(void)
         struct irq_desc *desc = irq_to_desc(irq);
         init_one_irq_desc(desc);
         desc->irq = irq;
+        desc->arch.virq = 0;
         desc->action  = NULL;
+        desc->arch.dev = NULL;
     }
 
     return 0;
@@ -141,6 +276,7 @@ void __init init_IRQ(void)
 
     BUG_ON(init_local_irq_data() < 0);
     BUG_ON(init_irq_data() < 0);
+    radix_tree_init(&desc_root);
 }
 
 void __cpuinit init_secondary_IRQ(void)
@@ -286,11 +422,15 @@ out_no_end:
 void release_irq(unsigned int irq, const void *dev_id)
 {
     struct irq_desc *desc;
+    struct pending_irq *p;
     unsigned long flags;
     struct irqaction *action, **action_ptr;
+    struct vcpu *v = current;
 
     desc = irq_to_desc(irq);
 
+    if ( !desc ) return;
+
     spin_lock_irqsave(&desc->lock,flags);
 
     action_ptr = &desc->action;
@@ -327,6 +467,14 @@ void release_irq(unsigned int irq, const void *dev_id)
 
     if ( action->free_on_release )
         xfree(action);
+
+    if ( is_lpi(irq) )
+    {
+        desc = delete_irq_desc(&desc_root, irq);
+        p = delete_pending_irq_desc(v->domain, irq);
+        xfree(desc);
+        xfree(p);
+    }
 }
 
 static int __setup_irq(struct irq_desc *desc, unsigned int irqflags,
@@ -365,6 +513,8 @@ int setup_irq(unsigned int irq, unsigned int irqflags, 
struct irqaction *new)
 
     desc = irq_to_desc(irq);
 
+    ASSERT(desc != NULL);
+
     spin_lock_irqsave(&desc->lock, flags);
 
     if ( test_bit(_IRQ_GUEST, &desc->status) )
@@ -408,7 +558,8 @@ int route_irq_to_guest(struct domain *d, unsigned int irq,
                        const char * devname)
 {
     struct irqaction *action;
-    struct irq_desc *desc = irq_to_desc(irq);
+    struct irq_desc *desc;
+    struct pending_irq *p;
     unsigned long flags;
     int retval = 0;
 
@@ -420,6 +571,20 @@ int route_irq_to_guest(struct domain *d, unsigned int irq,
     action->name = devname;
     action->free_on_release = 1;
 
+    if ( is_lpi(irq) )
+    {
+        desc = insert_irq_desc(&desc_root, irq);
+        if ( !desc )
+            return -ENOMEM;
+        init_one_irq_data(irq, desc);
+
+        p = insert_pending_irq_desc(d, irq);
+        if ( !p )
+            return -ENOMEM;
+    }
+    else
+        desc = irq_to_desc(irq);
+
     spin_lock_irqsave(&desc->lock, flags);
 
     /* If the IRQ is already used by someone
@@ -558,6 +723,7 @@ int platform_get_irq(const struct dt_device_node *device, 
int index)
 {
     struct dt_irq dt_irq;
     unsigned int type, irq;
+    struct irq_desc *desc;
     int res;
 
     res = dt_device_get_irq(device, index, &dt_irq);
@@ -567,6 +733,17 @@ int platform_get_irq(const struct dt_device_node *device, 
int index)
     irq = dt_irq.irq;
     type = dt_irq.type;
 
+    if ( is_lpi(irq) )
+    {
+        desc = insert_irq_desc(&desc_root, irq);
+        if ( !desc )
+            return -ENOMEM;
+        init_one_irq_data(irq, desc);
+        /* XXX: Here we don't know which is the domain.
+         * So pending irq structure is allocate when required
+         */
+    }
+
     /* Setup the IRQ type */
     if ( irq < NR_LOCAL_IRQS )
         res = irq_local_set_type(irq, type);
diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
index c14d79d..6fc8df1 100644
--- a/xen/arch/arm/vgic.c
+++ b/xen/arch/arm/vgic.c
@@ -30,6 +30,7 @@
 
 #include <asm/mmio.h>
 #include <asm/gic.h>
+#include <asm/gic-its.h>
 #include <asm/vgic.h>
 
 static inline struct vgic_irq_rank *vgic_get_rank(struct vcpu *v, int rank)
@@ -108,6 +109,9 @@ int domain_vgic_init(struct domain *d)
     for (i=0; i<DOMAIN_NR_RANKS(d); i++)
         spin_lock_init(&d->arch.vgic.shared_irqs[i].lock);
 
+    radix_tree_init(&d->arch.vgic.pending_lpis);
+    spin_lock_init(&d->arch.vgic.pending_lpi_lock);
+
     d->arch.vgic.handler->domain_init(d);
 
     d->arch.vgic.allocated_irqs =
@@ -127,11 +131,18 @@ void register_vgic_ops(struct domain *d, const struct 
vgic_ops *ops)
    d->arch.vgic.handler = ops;
 }
 
+void free_pending_lpis(void *ptr)
+{
+   struct pending_irq *pending_desc = ptr;
+   xfree(pending_desc);
+}
+
 void domain_vgic_free(struct domain *d)
 {
     xfree(d->arch.vgic.shared_irqs);
     xfree(d->arch.vgic.pending_irqs);
     xfree(d->arch.vgic.allocated_irqs);
+    radix_tree_destroy(&d->arch.vgic.pending_lpis, free_pending_lpis);
 }
 
 int vcpu_vgic_init(struct vcpu *v)
@@ -358,13 +369,18 @@ int vgic_to_sgi(struct vcpu *v, register_t sgir, enum 
gic_sgi_mode irqmode, int
 
 struct pending_irq *irq_to_pending(struct vcpu *v, unsigned int irq)
 {
-    struct pending_irq *n;
+    struct pending_irq *n = NULL;
     /* Pending irqs allocation strategy: the first vgic.nr_spis irqs
      * are used for SPIs; the rests are used for per cpu irqs */
     if ( irq < 32 )
         n = &v->arch.vgic.pending_irqs[irq];
-    else
+    else if ( irq < 1024 )
         n = &v->domain->arch.vgic.pending_irqs[irq - 32];
+    else
+    {
+        if ( is_lpi(irq) )
+            n = find_pending_irq_desc(v->domain, irq);
+    }
     return n;
 }
 
diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h
index 7202f93..027ffd3 100644
--- a/xen/include/asm-arm/domain.h
+++ b/xen/include/asm-arm/domain.h
@@ -11,6 +11,7 @@
 #include <asm/gic.h>
 #include <public/hvm/params.h>
 #include <xen/serial.h>
+#include <xen/radix-tree.h>
 #include <xen/hvm/iommu.h>
 
 struct hvm_domain
@@ -97,6 +98,9 @@ struct arch_domain
          * struct arch_vcpu.
          */
         struct pending_irq *pending_irqs;
+        /* Lock for managing pending lpi in radix tree */
+        spinlock_t pending_lpi_lock;
+        struct radix_tree_root pending_lpis;
         /* Base address for guest GIC */
         paddr_t dbase; /* Distributor base address */
         paddr_t cbase; /* CPU base address */
diff --git a/xen/include/asm-arm/gic.h b/xen/include/asm-arm/gic.h
index e4555e8..b4f4904 100644
--- a/xen/include/asm-arm/gic.h
+++ b/xen/include/asm-arm/gic.h
@@ -163,6 +163,7 @@
 #define DT_MATCH_GIC_V3 DT_MATCH_COMPATIBLE("arm,gic-v3")
 
 #define is_lpi(lpi) (lpi >= NR_GIC_LPI)
+
 /*
  * GICv3 registers that needs to be saved/restored
  */
diff --git a/xen/include/asm-arm/irq.h b/xen/include/asm-arm/irq.h
index f091739..8568b96 100644
--- a/xen/include/asm-arm/irq.h
+++ b/xen/include/asm-arm/irq.h
@@ -2,6 +2,7 @@
 #define _ASM_HW_IRQ_H
 
 #include <xen/config.h>
+#include <xen/radix-tree.h>
 #include <xen/device_tree.h>
 
 #define NR_VECTORS 256 /* XXX */
@@ -29,6 +30,7 @@ struct arch_irq_desc {
 #define arch_hwdom_irqs(domid) NR_IRQS
 
 struct irq_desc;
+struct pending_irq;
 struct irqaction;
 
 struct irq_desc *__irq_to_desc(int irq);
@@ -55,6 +57,14 @@ int platform_get_irq(const struct dt_device_node *device, 
int index);
 
 void irq_set_affinity(struct irq_desc *desc, const cpumask_t *cpu_mask);
 
+struct irq_desc *find_irq_desc(struct radix_tree_root *root_node, int irq);
+struct irq_desc *insert_irq_desc(struct radix_tree_root *root_node, int irq);
+struct irq_desc *delete_irq_desc(struct radix_tree_root *root_node, int irq);
+
+struct pending_irq *insert_pending_irq_desc(struct domain *d, int irq);
+struct pending_irq *find_pending_irq_desc(struct domain *d, int irq);
+struct pending_irq *delete_pending_irq_desc(struct domain *d, int irq);
+
 #endif /* _ASM_HW_IRQ_H */
 /*
  * Local variables:
-- 
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®.