[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [xen-unstable] arm: support fewer LR registers than virtual irqs
# HG changeset patch # User Stefano Stabellini <stefano.stabellini@xxxxxxxxxxxxx> # Date 1338542431 -3600 # Node ID ded5cb662d6ed8d14417c37c0565d2683dbde92d # Parent d7318231cfe3498947bf75f09d6675407d7b4e76 arm: support fewer LR registers than virtual irqs If the vgic needs to inject a virtual irq into the guest, but no free LR registers are available, add the irq to a list and return. Whenever an LR register becomes available we add the queued irq to it and remove it from the list. We use the gic lock to protect the list and the bitmask. Signed-off-by: Stefano Stabellini <stefano.stabellini@xxxxxxxxxxxxx> Acked-by: Ian Campbell <ian.campbell@xxxxxxxxxx> Committed-by: Ian Campbell <ian.campbell@xxxxxxxxxx> --- diff -r d7318231cfe3 -r ded5cb662d6e xen/arch/arm/gic.c --- a/xen/arch/arm/gic.c Thu May 31 10:18:52 2012 +0200 +++ b/xen/arch/arm/gic.c Fri Jun 01 10:20:31 2012 +0100 @@ -25,6 +25,7 @@ #include <xen/sched.h> #include <xen/errno.h> #include <xen/softirq.h> +#include <xen/list.h> #include <asm/p2m.h> #include <asm/domain.h> @@ -45,6 +46,14 @@ static struct { unsigned int lines; unsigned int cpus; spinlock_t lock; + uint64_t lr_mask; + /* lr_pending is used to queue IRQs (struct pending_irq) that the + * vgic tried to inject in the guest (calling gic_set_guest_irq) but + * no LRs were available at the time. + * As soon as an LR is freed we remove the first IRQ from this + * list and write it to the LR register. + * lr_pending is a subset of vgic.inflight_irqs. */ + struct list_head lr_pending; } gic; irq_desc_t irq_desc[NR_IRQS]; @@ -283,6 +292,9 @@ int __init gic_init(void) gic_cpu_init(); gic_hyp_init(); + gic.lr_mask = 0ULL; + INIT_LIST_HEAD(&gic.lr_pending); + spin_unlock(&gic.lock); return gic.cpus; @@ -377,14 +389,48 @@ int __init setup_irq(unsigned int irq, s return rc; } +static inline void gic_set_lr(int lr, unsigned int virtual_irq, + unsigned int state, unsigned int priority) +{ + BUG_ON(lr > nr_lrs); + GICH[GICH_LR + lr] = state | + GICH_LR_MAINTENANCE_IRQ | + ((priority >> 3) << GICH_LR_PRIORITY_SHIFT) | + ((virtual_irq & GICH_LR_VIRTUAL_MASK) << GICH_LR_VIRTUAL_SHIFT); +} + void gic_set_guest_irq(unsigned int virtual_irq, unsigned int state, unsigned int priority) { - BUG_ON(virtual_irq > nr_lrs); - GICH[GICH_LR + virtual_irq] = state | - GICH_LR_MAINTENANCE_IRQ | - ((priority >> 3) << GICH_LR_PRIORITY_SHIFT) | - ((virtual_irq & GICH_LR_VIRTUAL_MASK) << GICH_LR_VIRTUAL_SHIFT); + int i; + struct pending_irq *iter, *n; + + spin_lock(&gic.lock); + + if ( list_empty(&gic.lr_pending) ) + { + i = find_first_zero_bit(&gic.lr_mask, nr_lrs); + if (i < nr_lrs) { + set_bit(i, &gic.lr_mask); + gic_set_lr(i, virtual_irq, state, priority); + goto out; + } + } + + n = irq_to_pending(current, virtual_irq); + list_for_each_entry ( iter, &gic.lr_pending, lr_queue ) + { + if ( iter->priority > priority ) + { + list_add_tail(&n->lr_queue, &iter->lr_queue); + goto out; + } + } + list_add_tail(&n->lr_queue, &gic.lr_pending); + +out: + spin_unlock(&gic.lock); + return; } void gic_inject_irq_start(void) @@ -463,30 +509,42 @@ void gicv_setup(struct domain *d) static void maintenance_interrupt(int irq, void *dev_id, struct cpu_user_regs *regs) { - int i, virq; + int i = 0, virq; uint32_t lr; uint64_t eisr = GICH[GICH_EISR0] | (((uint64_t) GICH[GICH_EISR1]) << 32); - for ( i = 0; i < 64; i++ ) { - if ( eisr & ((uint64_t)1 << i) ) { - struct pending_irq *p; + while ((i = find_next_bit((const long unsigned int *) &eisr, + sizeof(eisr), i)) < sizeof(eisr)) { + struct pending_irq *p; - lr = GICH[GICH_LR + i]; - virq = lr & GICH_LR_VIRTUAL_MASK; - GICH[GICH_LR + i] = 0; + spin_lock(&gic.lock); + lr = GICH[GICH_LR + i]; + virq = lr & GICH_LR_VIRTUAL_MASK; + GICH[GICH_LR + i] = 0; + clear_bit(i, &gic.lr_mask); - spin_lock(¤t->arch.vgic.lock); - p = irq_to_pending(current, virq); - if ( p->desc != NULL ) { - p->desc->status &= ~IRQ_INPROGRESS; - GICC[GICC_DIR] = virq; - } + if ( !list_empty(&gic.lr_pending) ) { + p = list_entry(gic.lr_pending.next, typeof(*p), lr_queue); + gic_set_lr(i, p->irq, GICH_LR_PENDING, p->priority); + list_del_init(&p->lr_queue); + set_bit(i, &gic.lr_mask); + } else { gic_inject_irq_stop(); - list_del(&p->inflight); - INIT_LIST_HEAD(&p->inflight); - cpu_raise_softirq(current->processor, VGIC_SOFTIRQ); - spin_unlock(¤t->arch.vgic.lock); } + spin_unlock(&gic.lock); + + spin_lock(¤t->arch.vgic.lock); + p = irq_to_pending(current, virq); + if ( p->desc != NULL ) { + p->desc->status &= ~IRQ_INPROGRESS; + GICC[GICC_DIR] = virq; + } + list_del(&p->inflight); + INIT_LIST_HEAD(&p->inflight); + cpu_raise_softirq(current->processor, VGIC_SOFTIRQ); + spin_unlock(¤t->arch.vgic.lock); + + i++; } } diff -r d7318231cfe3 -r ded5cb662d6e xen/include/asm-arm/domain.h --- a/xen/include/asm-arm/domain.h Thu May 31 10:18:52 2012 +0200 +++ b/xen/include/asm-arm/domain.h Fri Jun 01 10:20:31 2012 +0100 @@ -23,6 +23,9 @@ struct pending_irq /* inflight is used to append instances of pending_irq to * vgic.inflight_irqs */ struct list_head inflight; + /* lr_queue is used to append instances of pending_irq to + * gic.lr_pending */ + struct list_head lr_queue; }; struct arch_domain @@ -75,6 +78,13 @@ struct arch_vcpu struct { struct vgic_irq_rank private_irqs; + /* This list is ordered by IRQ priority and it is used to keep + * track of the IRQs that the VGIC injected into the guest. + * Depending on the availability of LR registers, the IRQs might + * actually be in an LR, and therefore injected into the guest, + * or queued in gic.lr_pending. + * As soon as an IRQ is EOI'd by the guest and removed from the + * corresponding LR it is also removed from this list. */ struct list_head inflight_irqs; spinlock_t lock; } vgic; _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxx http://lists.xensource.com/xen-changelog |
Lists.xenproject.org is hosted with RackSpace, monitoring our |