[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
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |