[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH] arm/acpi: Fix the deadlock in function vgic_lock_rank()
On Fri, 27 May 2016, Julien Grall wrote: > Hello Shanker, > > On 27/05/16 01:39, Shanker Donthineni wrote: > > Commit 9d77b3c01d1261c (Configure SPI interrupt type and route to > > Dom0 dynamically) causing dead loop inside the spinlock function. > > Note that spinlocks in XEN are not recursive. Re-acquiring a spinlock > > that has already held by calling CPU leads to deadlock. This happens > > whenever dom0 does writes to GICD regs ISENABLER/ICENABLER. > > Thank you for spotting it, I have not noticed it while I was reviewing, only > tested on a model without any SPIs. > > > The following call trace explains the problem. > > > > DOM0 writes GICD_ISENABLER/GICD_ICENABLER > > vgic_v3_distr_common_mmio_write() > > vgic_lock_rank() --> acquiring first time > > vgic_enable_irqs() > > route_irq_to_guest() > > gic_route_irq_to_guest() > > vgic_get_target_vcpu() > > vgic_lock_rank() --> attemping acquired lock > > > > The simple fix release spinlock before calling vgic_enable_irqs() > > and vgic_disable_irqs(). > > You should explain why you think it is valid to release the lock earlier. > > In this case, I think the fix is not correct because the lock is protecting > both the register value and the internal state in Xen (modified by > vgic_enable_irqs). By releasing the lock earlier, they may become inconsistent > if another vCPU is disabling the IRQs at the same time. I agree, the vgic_enable_irqs call need to stay within the vgic_lock_rank/vgic_unlock_rank region. > I cannot find an easy fix which does not involve release the lock. When I was > reviewing this patch, I suggested to split the IRQ configuration from the > routing. Yes, the routing doesn't need to be done from vgic_enable_irqs. It is not nice. That would be the ideal fix, but it is not trivial. For 4.7 we could consider reverting 9d77b3c01d1261c. The only other thing that I can come up with which is simple would be improving gic_route_irq_to_guest to cope with callers that have the vgic rank lock already held (see below, untested) but it's pretty ugly. diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c index 2bfe4de..57f3f3f 100644 --- a/xen/arch/arm/gic.c +++ b/xen/arch/arm/gic.c @@ -127,15 +127,12 @@ void gic_route_irq_to_xen(struct irq_desc *desc, const cpumask_t *cpu_mask, /* Program the GIC to route an interrupt to a guest * - desc.lock must be held + * - rank lock must be held */ -int gic_route_irq_to_guest(struct domain *d, unsigned int virq, +static int __gic_route_irq_to_guest(struct domain *d, unsigned int virq, struct irq_desc *desc, unsigned int priority) { - unsigned long flags; - /* Use vcpu0 to retrieve the pending_irq struct. Given that we only - * route SPIs to guests, it doesn't make any difference. */ - struct vcpu *v_target = vgic_get_target_vcpu(d->vcpu[0], virq); - struct vgic_irq_rank *rank = vgic_rank_irq(v_target, virq); + struct vcpu *v_target = __vgic_get_target_vcpu(d->vcpu[0], virq); struct pending_irq *p = irq_to_pending(v_target, virq); int res = -EBUSY; @@ -144,12 +141,10 @@ int gic_route_irq_to_guest(struct domain *d, unsigned int virq, ASSERT(virq >= 32); ASSERT(virq < vgic_num_irqs(d)); - vgic_lock_rank(v_target, rank, flags); - if ( p->desc || /* The VIRQ should not be already enabled by the guest */ test_bit(GIC_IRQ_GUEST_ENABLED, &p->status) ) - goto out; + return res; desc->handler = gic_hw_ops->gic_guest_irq_type; set_bit(_IRQ_GUEST, &desc->status); @@ -159,12 +154,36 @@ int gic_route_irq_to_guest(struct domain *d, unsigned int virq, p->desc = desc; res = 0; -out: - vgic_unlock_rank(v_target, rank, flags); - return res; } +int gic_route_irq_to_guest(struct domain *d, unsigned int virq, + struct irq_desc *desc, unsigned int priority) +{ + unsigned long flags; + int lock = 0, retval; + struct vgic_irq_rank *rank; + + /* Use vcpu0 to retrieve the pending_irq struct. Given that we only + * route SPIs to guests, it doesn't make any difference. */ + rank = vgic_rank_irq(d->vcpu[0], virq); + + /* Take the rank spinlock unless it has already been taken by the + * caller. */ + if ( !spin_is_locked(&rank->lock) ) { + vgic_lock_rank(d->vcpu[0], rank, flags); + lock = 1; + } + + retval = __gic_route_irq_to_guest(d, virq, desc, GIC_PRI_IRQ); + + if ( lock ) + vgic_unlock_rank(d->vcpu[0], rank, flags); + + return retval; + +} + /* This function only works with SPIs for now */ int gic_remove_irq_from_guest(struct domain *d, unsigned int virq, struct irq_desc *desc) diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c index aa420bb..e45669f 100644 --- a/xen/arch/arm/vgic.c +++ b/xen/arch/arm/vgic.c @@ -215,7 +215,7 @@ int vcpu_vgic_free(struct vcpu *v) } /* The function should be called by rank lock taken. */ -static struct vcpu *__vgic_get_target_vcpu(struct vcpu *v, unsigned int virq) +struct vcpu *__vgic_get_target_vcpu(struct vcpu *v, unsigned int virq) { struct vgic_irq_rank *rank = vgic_rank_irq(v, virq); diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h index a2fccc0..726e690 100644 --- a/xen/include/asm-arm/vgic.h +++ b/xen/include/asm-arm/vgic.h @@ -343,6 +343,7 @@ void vgic_v3_setup_hw(paddr_t dbase, const struct rdist_region *regions, uint32_t rdist_stride); #endif +struct vcpu *__vgic_get_target_vcpu(struct vcpu *v, unsigned int virq); #endif /* __ASM_ARM_VGIC_H__ */ _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |