|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [RFC PATCH 16/19] 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 | 2 +
xen/include/asm-arm/irq.h | 10 +++
5 files changed, 214 insertions(+), 5 deletions(-)
diff --git a/xen/arch/arm/irq.c b/xen/arch/arm/irq.c
index d52ee0c..13583b4 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->virq = 0;
+ desc->action = NULL;
+ desc->data = 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->virq = 0;
desc->action = NULL;
+ desc->data = NULL;
}
return 0;
@@ -133,6 +268,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)
@@ -278,11 +414,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;
@@ -319,6 +459,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,
@@ -357,6 +505,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) )
@@ -400,7 +550,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;
@@ -412,6 +563,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
@@ -550,6 +715,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);
@@ -559,6 +725,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 b272d86..3bf9ef3 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);
return 0;
@@ -118,10 +122,17 @@ 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);
+ radix_tree_destroy(&d->arch.vgic.pending_lpis, free_pending_lpis);
}
int vcpu_vgic_init(struct vcpu *v)
@@ -348,13 +359,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 b6d85b8..715eb7e 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
@@ -96,6 +97,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 eac738f..6f2237f 100644
--- a/xen/include/asm-arm/gic.h
+++ b/xen/include/asm-arm/gic.h
@@ -167,6 +167,8 @@
#define DT_MATCH_GIC_V3 DT_MATCH_COMPATIBLE(DT_COMPAT_GIC_V3)
+#define is_lpi(lpi) ((lpi) >= 8192)
+
/*
* 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 de029e4..1159a6d 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 */
@@ -27,6 +28,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);
@@ -52,6 +54,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
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |