[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Xen-devel] [PATCH v4 2/6] xen/arm: track the state of guest IRQs



On Thu, 12 Dec 2013, Ian Campbell wrote:
> On Wed, 2013-12-11 at 19:07 +0000, Stefano Stabellini wrote:
> > Introduce a status field in struct pending_irq. Valid states are
> > GUEST_PENDING, GUEST_VISIBLE and GUEST_ENABLED and they are not mutually
> > exclusive.  See the in-code comment for an explanation of the states and
> > how they are used.
> > Use atomic operations to set and clear the status bits. Note that
> > setting GIC_IRQ_GUEST_VISIBLE and clearing GIC_IRQ_GUEST_PENDING can be
> > done in two separate operations as the underlying pending status is
> > actually only cleared on the LR after the guest ACKs the interrupts.
> > Until that happens it's not possible to receive another interrupt.
> > 
> > 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 add the irq back into
> > the lr_pending queue so that it's going to be reinjected one more time,
> > if the interrupt is still enabled at the vgicd level.
> > If it is not, it is going to be injected as soon as the guest renables
> > the interrupt.
> > 
> > One exception is evtchn_irq: in that case we don't want to
> > set the GIC_IRQ_GUEST_PENDING bit if it is already GUEST_VISIBLE,
> > because as part of the event handling loop, the guest would realize that
> > new events are present even without a new notification.
> > Also we already have a way to figure out exactly when we do need to
> > inject a second notification if vgic_vcpu_inject_irq is called after the
> > end of the guest event handling loop and before the guest EOIs the
> > interrupt (see db453468d92369e7182663fb13e14d83ec4ce456 "arm: vgic: fix
> > race between evtchn upcall and evtchnop_send").
> > 
> > In maintenance_interrupt only stop injecting interrupts if no new
> > interrupts have been added to the LRs.
> > 
> > Signed-off-by: Stefano Stabellini <stefano.stabellini@xxxxxxxxxxxxx>
> > 
> > ---
> > Changes in v2:
> > - use atomic operations to modify the pending_irq status bits, remove
> > the now unnecessary locks;
> > - make status unsigned long;
> > - in maintenance_interrupt only stops injecting interrupts if no new
> > interrupts have been added to the LRs;
> > - add a state to keep track whether the guest irq is enabled at the
> > vgicd level;
> > 
> > Changes in v3:
> > - do not set the GUEST_PENDING bit for evtchn_irq if the irq is already
> > guest visible.
> > 
> > Changes in v4:
> > - move set_bit _GIC_IRQ_GUEST_VISIBLE and clear_bit
> > _GIC_IRQ_GUEST_PENDING to gic_set_lr;
> > - turn set_int into a bool_t;
> > - remove raw GIC_IRQ_GUEST values;
> > - in maintenance_interrupt if the irq is still PENDING, add it back into
> > the lr_pending queue instead of immediately reinjecting it.
> > ---
> >  xen/arch/arm/gic.c           |   54 
> > ++++++++++++++++++++++++++++++++----------
> >  xen/arch/arm/vgic.c          |   17 ++++++++++---
> >  xen/include/asm-arm/domain.h |   37 +++++++++++++++++++++++++++++
> >  3 files changed, 93 insertions(+), 15 deletions(-)
> > 
> > diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
> > index 5f7a548..19e0ae4 100644
> > --- a/xen/arch/arm/gic.c
> > +++ b/xen/arch/arm/gic.c
> > @@ -615,6 +615,7 @@ static inline void gic_set_lr(int lr, unsigned int 
> > virtual_irq,
> >          unsigned int state, unsigned int priority)
> >  {
> >      int maintenance_int = GICH_LR_MAINTENANCE_IRQ;
> > +    struct pending_irq *p = irq_to_pending(current, virtual_irq);
> >  
> >      BUG_ON(lr >= nr_lrs);
> >      BUG_ON(lr < 0);
> > @@ -624,6 +625,9 @@ static inline void gic_set_lr(int lr, unsigned int 
> > virtual_irq,
> >          maintenance_int |
> >          ((priority >> 3) << GICH_LR_PRIORITY_SHIFT) |
> >          ((virtual_irq & GICH_LR_VIRTUAL_MASK) << GICH_LR_VIRTUAL_SHIFT);
> > +
> > +    set_bit(GIC_IRQ_GUEST_VISIBLE, &p->status);
> > +    clear_bit(GIC_IRQ_GUEST_PENDING, &p->status);
> 
> So WRT races in vgic_vcpu_inject_irq we were discussing on v3. The above
> vs:
> 
> +        if ( (irq != current->domain->arch.evtchn_irq) ||
> +             (!test_bit(_GIC_IRQ_GUEST_VISIBLE, &n->status)) )
> +            set_bit(_GIC_IRQ_GUEST_PENDING, &n->status);
> 
> If one cpu (A) is in gic_set_lr while another (B) is in
> vgic_vcpu_inject_irq, is there any ordering which fails to do the right
> thing.
> 
>         A -- set VISIBLE
>         B -- test VISIBLE
>         B -- set PENDING
>         A -- clear PENDING
> 
> Doesn't seem right?

You got the test wrong.
Assuming irq != evtchn_irq, it would be:

A -- set VISIBLE
B -- set PENDING
A -- clear PENDING

So the guest would loose a potential future interrupt notification. On
the other hand the guest hasn't acked the interrupt yet, so it is not
supposed to be able to receive a second interrupt notification at this
time. Moreover consider that a second interrupt at this time can only
happen with hardware edge-trigger interrupts. This race exists even on
bare metal between the second interrupt and the guest ACKing the first.
I think it should be OK.


>         B -- test VISIBLE
>         A -- set VISIBLE
>         A -- clear PENDING
>         B -- doesn't set PENDING
>         
> Seems OK.

Still assuming irq != evtchn_irq, the other case would be:

A -- set VISIBLE
A -- clear PENDING
B -- set PENDING

so the guest would get one notification more from the hardware.
It can only happen with hardware edge-trigger interrupts.

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.