[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH] xen/arm: implement GICD_ICACTIVER read/write
Implement GICD_ICACTIVER and GICD_ISACTIVER reads by looking for the GIC_IRQ_GUEST_ACTIVE bit in the relevant struct pending_irq. However given that the pending to active transaction for irqs in LRs in done in hardware, the GIC_IRQ_GUEST_ACTIVE bit might be out of date. We'll have to live with that. Implement GICD_ICACTIVER writes by checking the state of the irq in our queues: if the irq is present in an LR, remove the hardware ACTIVE bit. If the irq is present in an LR of another vcpu, send an IPI. Set the GIC_IRQ_GUEST_DEACTIVATE bit to tell the receiving vcpu that the active bit needs to be deactivated. Signed-off-by: Stefano Stabellini <stefano.stabellini@xxxxxxxxxxxxx> --- xen/arch/arm/gic.c | 40 +++++++++++++++++++++++++++++++++++++++ xen/arch/arm/vgic-v2.c | 45 ++++++++++++++++++++++++++++++++++++++------ xen/arch/arm/vgic-v3.c | 44 ++++++++++++++++++++++++++++++++++++++----- xen/include/asm-arm/gic.h | 1 + xen/include/asm-arm/vgic.h | 4 ++++ 5 files changed, 123 insertions(+), 11 deletions(-) diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c index 1e1e5ba..75c1f52 100644 --- a/xen/arch/arm/gic.c +++ b/xen/arch/arm/gic.c @@ -414,6 +414,15 @@ static void gic_update_one_lr(struct vcpu *v, int i) gic_hw_ops->read_lr(i, &lr_val); irq = lr_val.virq; p = irq_to_pending(v, irq); + + if ( test_and_clear_bit(GIC_IRQ_GUEST_DEACTIVATE, &p->status) && + (lr_val.state & GICH_LR_ACTIVE) ) + { + clear_bit(GIC_IRQ_GUEST_ACTIVE, &p->status); + lr_val.state &= ~GICH_LR_ACTIVE; + gic_hw_ops->write_lr(i, &lr_val); + } + if ( lr_val.state & GICH_LR_ACTIVE ) { set_bit(GIC_IRQ_GUEST_ACTIVE, &p->status); @@ -489,6 +498,37 @@ void gic_clear_lrs(struct vcpu *v) spin_unlock_irqrestore(&v->arch.vgic.lock, flags); } +/* called with rank lock held */ +void gic_deactivate_irq(struct vcpu *v, unsigned int irq) +{ + unsigned long flags; + struct pending_irq *p; + struct vcpu *v_target = v->domain->arch.vgic.handler->get_target_vcpu(v, irq); + + spin_lock_irqsave(&v_target->arch.vgic.lock, flags); + + p = irq_to_pending(v_target, irq); + /* the interrupt is not even in an LR */ + if ( list_empty(&p->inflight) || !list_empty(&p->lr_queue) ) + { + spin_unlock_irqrestore(&v_target->arch.vgic.lock, flags); + return; + } + + /* it is in an LR, let's check */ + set_bit(GIC_IRQ_GUEST_DEACTIVATE, &p->status); + if ( v_target == current ) + { + gic_update_one_lr(v_target, p->lr); + spin_unlock_irqrestore(&v_target->arch.vgic.lock, flags); + } else { + spin_unlock_irqrestore(&v_target->arch.vgic.lock, flags); + vcpu_unblock(v_target); + if (v_target->is_running ) + smp_send_event_check_mask(cpumask_of(v_target->processor)); + } +} + static void gic_restore_pending_irqs(struct vcpu *v) { int lr = 0; diff --git a/xen/arch/arm/vgic-v2.c b/xen/arch/arm/vgic-v2.c index f7d784b..9042062 100644 --- a/xen/arch/arm/vgic-v2.c +++ b/xen/arch/arm/vgic-v2.c @@ -126,8 +126,31 @@ static int vgic_v2_distr_mmio_read(struct vcpu *v, mmio_info_t *info, /* Read the active status of an IRQ via GICD is not supported */ case GICD_ISACTIVER ... GICD_ISACTIVERN: case GICD_ICACTIVER ... GICD_ICACTIVERN: - goto read_as_zero; - + { + unsigned int i = 0, irq = 0; + struct pending_irq *p; + if ( dabt.size != DABT_WORD ) goto bad_width; + rank = vgic_rank_offset(v, 1, gicd_reg - GICD_ICACTIVER, DABT_WORD); + if ( rank == NULL) goto read_as_zero; + vgic_lock_rank(v, rank, flags); + *r = 0; + irq = (gicd_reg - GICD_ICACTIVER) << 3; + for (i = 0; i < 32; i++) + { + p = irq_to_pending(v, i + irq); + /* + * This information is likely out of date because we don't + * actually know which interrupts have become ACTIVE from + * PENDING in the LRs of other processors at it happens + * transparently in hardware. We would have to interrupt + * all other running vcpus to get an accurate snapshot. + * Let's not do that. + */ + *r |= test_bit(GIC_IRQ_GUEST_ACTIVE, &p->status) ? (1 << i) : 0; + } + vgic_unlock_rank(v, rank, flags); + return 1; + } case GICD_ITARGETSR ... GICD_ITARGETSRN: if ( dabt.size != DABT_BYTE && dabt.size != DABT_WORD ) goto bad_width; rank = vgic_rank_offset(v, 8, gicd_reg - GICD_ITARGETSR, DABT_WORD); @@ -332,11 +355,21 @@ static int vgic_v2_distr_mmio_write(struct vcpu *v, mmio_info_t *info, return 0; case GICD_ICACTIVER ... GICD_ICACTIVERN: + { + unsigned int i = 0, irq; if ( dabt.size != DABT_WORD ) goto bad_width; - printk(XENLOG_G_ERR - "%pv: vGICD: unhandled word write %#"PRIregister" to ICACTIVER%d\n", - v, r, gicd_reg - GICD_ICACTIVER); - return 0; + rank = vgic_rank_offset(v, 1, gicd_reg - GICD_ICACTIVER, DABT_WORD); + if ( rank == NULL) goto write_ignore; + vgic_lock_rank(v, rank, flags); + irq = (gicd_reg - GICD_ICACTIVER) << 3; + while ( (i = find_next_bit(&r, 32, i)) < 32 ) + { + gic_deactivate_irq(v, i + irq); + i++; + } + vgic_unlock_rank(v, rank, flags); + return 1; + } case GICD_ITARGETSR ... GICD_ITARGETSR + 7: /* SGI/PPI target is read only */ diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c index b5249ff..c779f75 100644 --- a/xen/arch/arm/vgic-v3.c +++ b/xen/arch/arm/vgic-v3.c @@ -325,7 +325,31 @@ static int __vgic_v3_distr_common_mmio_read(const char *name, struct vcpu *v, /* Read the active status of an IRQ via GICD/GICR is not supported */ case GICD_ISACTIVER ... GICD_ISACTIVERN: case GICD_ICACTIVER ... GICD_ICACTIVERN: - goto read_as_zero; + { + unsigned int i = 0, irq = 0; + struct pending_irq *p; + if ( dabt.size != DABT_WORD ) goto bad_width; + rank = vgic_rank_offset(v, 1, reg - GICD_ICACTIVER, DABT_WORD); + if ( rank == NULL) goto read_as_zero; + vgic_lock_rank(v, rank, flags); + *r = 0; + irq = (reg - GICD_ICACTIVER) << 3; + for (i = 0; i < 32; i++) + { + p = irq_to_pending(v, i + irq); + /* + * This information is likely out of date because we don't + * actually know which interrupts have become ACTIVE from + * PENDING in the LRs of other processors at it happens + * transparently in hardware. We would have to interrupt + * all other running vcpus to get an accurate snapshot. + * Let's not do that. + */ + *r |= test_bit(GIC_IRQ_GUEST_ACTIVE, &p->status) ? (1 << i) : 0; + } + vgic_unlock_rank(v, rank, flags); + return 1; + } case GICD_IPRIORITYR ... GICD_IPRIORITYRN: if ( dabt.size != DABT_BYTE && dabt.size != DABT_WORD ) goto bad_width; @@ -421,11 +445,21 @@ static int __vgic_v3_distr_common_mmio_write(const char *name, struct vcpu *v, return 0; case GICD_ICACTIVER ... GICD_ICACTIVERN: + { + unsigned int i = 0, irq; if ( dabt.size != DABT_WORD ) goto bad_width; - printk(XENLOG_G_ERR - "%pv: %s: unhandled word write %#"PRIregister" to ICACTIVER%d\n", - v, name, r, reg - GICD_ICACTIVER); - return 0; + rank = vgic_rank_offset(v, 1, reg - GICD_ICACTIVER, DABT_WORD); + if ( rank == NULL) goto write_ignore; + vgic_lock_rank(v, rank, flags); + irq = (reg - GICD_ICACTIVER) << 3; + while ( (i = find_next_bit(&r, 32, i)) < 32 ) + { + gic_deactivate_irq(v, i + irq); + i++; + } + vgic_unlock_rank(v, rank, flags); + return 1; + } case GICD_IPRIORITYR ... GICD_IPRIORITYRN: if ( dabt.size != DABT_BYTE && dabt.size != DABT_WORD ) goto bad_width; diff --git a/xen/include/asm-arm/gic.h b/xen/include/asm-arm/gic.h index 0116481..0061368 100644 --- a/xen/include/asm-arm/gic.h +++ b/xen/include/asm-arm/gic.h @@ -287,6 +287,7 @@ extern unsigned int gic_number_lines(void); int gic_irq_xlate(const u32 *intspec, unsigned int intsize, unsigned int *out_hwirq, unsigned int *out_type); void gic_clear_lrs(struct vcpu *v); +void gic_deactivate_irq(struct vcpu *v, unsigned int irq); struct gic_info { /* GIC version */ diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h index cb51a9e..9845c13 100644 --- a/xen/include/asm-arm/vgic.h +++ b/xen/include/asm-arm/vgic.h @@ -59,12 +59,16 @@ struct pending_irq * vcpu while it is still inflight and on an GICH_LR register on the * old vcpu. * + * GIC_IRQ_GUEST_DEACTIVATE: an explicit deactivation request has + * been made by the guest (for example writing to GICD_ICACTIVER). + * */ #define GIC_IRQ_GUEST_QUEUED 0 #define GIC_IRQ_GUEST_ACTIVE 1 #define GIC_IRQ_GUEST_VISIBLE 2 #define GIC_IRQ_GUEST_ENABLED 3 #define GIC_IRQ_GUEST_MIGRATING 4 +#define GIC_IRQ_GUEST_DEACTIVATE 5 unsigned long status; struct irq_desc *desc; /* only set it the irq corresponds to a physical irq */ unsigned int irq; -- 1.7.10.4 _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |