[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH 2/6] xen/arm: track the state of guest IRQs
On Mon, 9 Dec 2013, Ian Campbell wrote: > On Fri, 2013-12-06 at 17:26 +0000, Stefano Stabellini wrote: > > Introduce a status field in struct pending_irq. Valid states are > > GUEST_PENDING and GUEST_VISIBLE and they are not mutually exclusive. > > Are all four combinations valid? In particular is !PENDING and VISIBLE > valid? Yes, they are all valid. > > See the in-code comment for an explanation of the states and how they > > are used. Protect pending_irq state manipulations with the vgic lock. > > > > The main effect of this patch is that an IRQ can be set to GUEST_PENDING > > while it is being serviced by the guest. In maintenance_interrupt we > > check whether GUEST_PENDING is set and if it is we reinject the IRQ one > > more time. > > > > Signed-off-by: Stefano Stabellini <stefano.stabellini@xxxxxxxxxxxxx> > > > Pulling the referenced comment to the top: > diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h > index d5cae2e..5e7bb58 100644 > > --- a/xen/include/asm-arm/domain.h > > +++ b/xen/include/asm-arm/domain.h > > @@ -22,6 +22,35 @@ struct vgic_irq_rank { > > struct pending_irq > > { > > int irq; > > + /* > > + * The following two states track the lifecycle of the guest irq. > > + * However because we are not sure and we don't want to track > > + * whether an irq added to an LR register is PENDING or ACTIVE, the > > + * following states are just an approximation. > > + * > > + * GIC_IRQ_GUEST_PENDING: the irq is asserted > > + * > > + * GIC_IRQ_GUEST_VISIBLE: the irq has been added to an LR register, > > + * therefore the guest is aware of it. From the guest point of view > > + * the irq can be pending (if the guest has not acked the irq yet) > > + * or active (after acking the irq). > > > > Is this visibility "sticky" in the case where the the use of the LR is > preempted by a new higher priority IRQ? (I suppose this is only a > theoretical concern right now). Yes, I think it should be "sticky". > + * In order for the state machine to be fully accurate, for level > > + * interrupts, we should keep the GIC_IRQ_GUEST_PENDING state until > > + * the guest deactivates the irq. However because we are not sure > > + * when that happens, we simply remove the GIC_IRQ_GUEST_PENDING > > + * state when we add the irq to an LR register. We add it back when > > + * we receive another interrupt notification. > > + * Therefore it is possible to set GIC_IRQ_GUEST_PENDING while the > > + * irq is GIC_IRQ_GUEST_VISIBLE. We could also change the state of > > + * the guest irq in the LR register from active to active and > > + * pending, but for simplicity we simply inject a second irq after > > + * the guest EOIs the first one. > > > > This last paragraph is just saying that we don't necessarily reuse the > same LR slot, right? Yes, if by reuse you mean change the status on the existing LR. > + * > > + */ > > +#define GIC_IRQ_GUEST_PENDING (1<<1) > > +#define GIC_IRQ_GUEST_VISIBLE (1<<2) > > + int status; > > > > bit flags should be unsigned I think. OK > struct irq_desc *desc; /* only set it the irq corresponds to a physical > irq */ > > uint8_t priority; > > /* inflight is used to append instances of pending_irq to > > > > > diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c > > index 7e87acb..a8a5c2a 100644 > > --- a/xen/arch/arm/gic.c > > +++ b/xen/arch/arm/gic.c > > @@ -626,6 +626,7 @@ static inline void gic_set_lr(int lr, unsigned int > > virtual_irq, > > ((virtual_irq & GICH_LR_VIRTUAL_MASK) << GICH_LR_VIRTUAL_SHIFT); > > } > > > > +/* needs to be called with vgic lock held */ > > void gic_set_guest_irq(struct vcpu *v, unsigned int virtual_irq, > > unsigned int state, unsigned int priority) > > { > > @@ -635,17 +636,20 @@ void gic_set_guest_irq(struct vcpu *v, unsigned int > > virtual_irq, > > > > spin_lock_irqsave(&gic.lock, flags); > > > > + n = irq_to_pending(v, virtual_irq); > > + > > if ( v == current && list_empty(&v->arch.vgic.lr_pending) ) > > { > > i = find_first_zero_bit(&this_cpu(lr_mask), nr_lrs); > > if (i < nr_lrs) { > > set_bit(i, &this_cpu(lr_mask)); > > gic_set_lr(i, virtual_irq, state, priority); > > + n->status |= GIC_IRQ_GUEST_VISIBLE; > > + n->status &= ~GIC_IRQ_GUEST_PENDING; > > Would using set/clear_bit here allow the locking restrictions to be > relaxed? Yes, good idea. > > goto out; > > } > > } > > > > - n = irq_to_pending(v, virtual_irq); > > if ( !list_empty(&n->lr_queue) ) > > goto out; > > > > @@ -679,6 +683,8 @@ static void gic_restore_pending_irqs(struct vcpu *v) > > gic_set_lr(i, p->irq, GICH_LR_PENDING, p->priority); > > list_del_init(&p->lr_queue); > > set_bit(i, &this_cpu(lr_mask)); > > + p->status |= GIC_IRQ_GUEST_VISIBLE; > > + p->status &= ~GIC_IRQ_GUEST_PENDING; > > spin_unlock_irqrestore(&gic.lock, flags); > > } > > > > @@ -884,6 +890,7 @@ static void maintenance_interrupt(int irq, void > > *dev_id, struct cpu_user_regs *r > > uint32_t lr; > > struct vcpu *v = current; > > uint64_t eisr = GICH[GICH_EISR0] | (((uint64_t) GICH[GICH_EISR1]) << > > 32); > > + unsigned long flags; > > > > while ((i = find_next_bit((const long unsigned int *) &eisr, > > 64, i)) < 64) { > > @@ -893,23 +900,13 @@ static void maintenance_interrupt(int irq, void > > *dev_id, struct cpu_user_regs *r > > cpu = -1; > > eoi = 0; > > > > - spin_lock_irq(&gic.lock); > > + spin_lock(&v->arch.vgic.lock); > > + spin_lock_irqsave(&gic.lock, flags); > > Is the locking hierarchy for vgic.lock, gic.lock and desc.lock written > down somewhere? > > Having irqsave on the inner lock is weird, isn't it? The outer one will > clobber the irq status anyway. > > You haven't changed the contexts where maintenance_interrupt can be > called either -- have you? This goes away thanks to the atomic bit operations, see next version of the patch. > > lr = GICH[GICH_LR + i]; > > virq = lr & GICH_LR_VIRTUAL_MASK; > > GICH[GICH_LR + i] = 0; > > clear_bit(i, &this_cpu(lr_mask)); > > > > - if ( !list_empty(&v->arch.vgic.lr_pending) ) { > > - p = list_entry(v->arch.vgic.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, &this_cpu(lr_mask)); > > - } else { > > - gic_inject_irq_stop(); > > - } > > - spin_unlock_irq(&gic.lock); > > - > > - spin_lock_irq(&v->arch.vgic.lock); > > p = irq_to_pending(v, virq); > > if ( p->desc != NULL ) { > > p->desc->status &= ~IRQ_INPROGRESS; > > @@ -918,8 +915,32 @@ static void maintenance_interrupt(int irq, void > > *dev_id, struct cpu_user_regs *r > > eoi = 1; > > pirq = p->desc->irq; > > } > > + if ( p->status & GIC_IRQ_GUEST_PENDING ) > > + { > > + gic_set_lr(i, p->irq, GICH_LR_PENDING, p->priority); > > Does this conflict with what the last para of the main comment said? > i.e. we do infact reuse the slot via gic_set_lr here rather than > reinjecting? I see where the confusion is: reusing or not exactly the same LR register doesn't make a difference. What does is that we do not reuse the same interrupt notification by changing the status of the live LR before it is EOIed by the guest. > > + p->status &= ~GIC_IRQ_GUEST_PENDING; > > Don't need to mess with VISIBLE here? Nope because it is still visible after gic_set_lr (the second notification is visible). > > + i++; > > + spin_unlock_irqrestore(&gic.lock, flags); > > + spin_unlock(&v->arch.vgic.lock); > > + continue; > > + } > > + > > + p->status &= ~GIC_IRQ_GUEST_VISIBLE; > > list_del_init(&p->inflight); > > - spin_unlock_irq(&v->arch.vgic.lock); > > + > > + if ( !list_empty(&v->arch.vgic.lr_pending) ) { > > + p = list_entry(v->arch.vgic.lr_pending.next, typeof(*p), > > lr_queue); > > + gic_set_lr(i, p->irq, GICH_LR_PENDING, p->priority); > > + p->status |= GIC_IRQ_GUEST_VISIBLE; > > + p->status &= ~GIC_IRQ_GUEST_PENDING; > > + list_del_init(&p->lr_queue); > > + set_bit(i, &this_cpu(lr_mask)); > > + } else { > > + gic_inject_irq_stop(); > > + } > > + > > + spin_unlock_irqrestore(&gic.lock, flags); > > + spin_unlock(&v->arch.vgic.lock); > > > > if ( eoi ) { > > /* this is not racy because we can't receive another irq of the > > diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c > > index 2e4b11f..c71db37 100644 > > --- a/xen/arch/arm/vgic.c > > +++ b/xen/arch/arm/vgic.c > > @@ -365,6 +365,9 @@ static void vgic_enable_irqs(struct vcpu *v, uint32_t > > r, int n) > > struct pending_irq *p; > > unsigned int irq; > > int i = 0; > > + unsigned long f; > > "flags" is conventional. this also goes away after switching to atomic bits operations. > > + > > + spin_lock_irqsave(&v->arch.vgic.lock, f); > > > > while ( (i = find_next_bit((const long unsigned int *) &r, 32, i)) < > > 32 ) { > > irq = i + (32 * n); > > @@ -373,6 +376,8 @@ static void vgic_enable_irqs(struct vcpu *v, uint32_t > > r, int n) > > gic_set_guest_irq(v, irq, GICH_LR_PENDING, p->priority); > > i++; > > } > > + > > + spin_unlock_irqrestore(&v->arch.vgic.lock, f); > > } > > > > static inline int is_vcpu_running(struct domain *d, int vcpuid) > > @@ -672,8 +677,15 @@ void vgic_vcpu_inject_irq(struct vcpu *v, unsigned int > > irq, int virtual) > > > > spin_lock_irqsave(&v->arch.vgic.lock, flags); > > > > - /* vcpu offline or irq already pending */ > > - if (test_bit(_VPF_down, &v->pause_flags) || !list_empty(&n->inflight)) > > + if ( !list_empty(&n->inflight) ) > > + { > > + n->status |= GIC_IRQ_GUEST_PENDING; > > + spin_unlock_irqrestore(&v->arch.vgic.lock, flags); > > + return; > > + } > > + > > + /* vcpu offline */ > > You should test for this first I think? Otherwise you would end up > injecting it. I don't think so: a cpu can receive an interrupt from the distributor even the cpu is not responding. Of course the vcpu cannot actually receive the interrupt until is woken up. > > + if ( test_bit(_VPF_down, &v->pause_flags) ) > > { > > spin_unlock_irqrestore(&v->arch.vgic.lock, flags); > > return; > > @@ -682,6 +694,7 @@ void vgic_vcpu_inject_irq(struct vcpu *v, unsigned int > > irq, int virtual) > > priority = byte_read(rank->ipriority[REG_RANK_INDEX(8, idx)], 0, byte); > > > > n->irq = irq; > > + n->status = GIC_IRQ_GUEST_PENDING; > > Should this not be |=? Otherwise we potentially lose the fact that the > IRQ is VISIBLE? Yep > > n->priority = priority; > > if (!virtual) > > n->desc = irq_to_desc(irq); _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |