diff mbox

[Xen-devel,v4,03/10] xen/arm: support HW interrupts, do not request maintenance_interrupts

Message ID 1395232325-19226-3-git-send-email-stefano.stabellini@eu.citrix.com
State New
Headers show

Commit Message

Stefano Stabellini March 19, 2014, 12:31 p.m. UTC
If the irq to be injected is an hardware irq (p->desc != NULL), set
GICH_LR_HW. Do not set GICH_LR_MAINTENANCE_IRQ.

Remove the code to EOI a physical interrupt on behalf of the guest
because it has become unnecessary.

Introduce a new function, gic_clear_lrs, that goes over the GICH_LR
registers, clear the invalid ones and free the corresponding interrupts
from the inflight queue if appropriate. Add the interrupt to lr_pending
if the GIC_IRQ_GUEST_PENDING is still set.

Call gic_clear_lrs from gic_restore_state and on return to guest
(gic_inject).

In vgic_vcpu_inject_irq, if the target is a vcpu running on another cpu,
send and SGI to it to interrupt it and force it to clear the old LRs.

Signed-off-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>

---

Changes in v4:
- merged patch #3 and #4 into a single patch.

Changes in v2:
- remove the EOI code, now unnecessary;
- do not assume physical IRQ == virtual IRQ;
- refactor gic_set_lr.
---
 xen/arch/arm/gic.c  |  135 ++++++++++++++++++++++-----------------------------
 xen/arch/arm/vgic.c |    3 +-
 2 files changed, 60 insertions(+), 78 deletions(-)

Comments

Julien Grall March 19, 2014, 1:42 p.m. UTC | #1
Hi Stefano,

On 03/19/2014 12:31 PM, Stefano Stabellini wrote:

[..]

> @@ -625,16 +628,19 @@ int __init setup_dt_irq(const struct dt_irq *irq, struct irqaction *new)
>  static inline void gic_set_lr(int lr, struct pending_irq *p,
>          unsigned int state)
>  {
> -    int maintenance_int = GICH_LR_MAINTENANCE_IRQ;
> +    uint32_t lr_reg;
>  
>      BUG_ON(lr >= nr_lrs);
>      BUG_ON(lr < 0);
>      BUG_ON(state & ~(GICH_LR_STATE_MASK<<GICH_LR_STATE_SHIFT));
>  
> -    GICH[GICH_LR + lr] = state |
> -        maintenance_int |
> -        ((p->priority >> 3) << GICH_LR_PRIORITY_SHIFT) |
> +    lr_reg = state | ((p->priority >> 3) << GICH_LR_PRIORITY_SHIFT) |
>          ((p->irq & GICH_LR_VIRTUAL_MASK) << GICH_LR_VIRTUAL_SHIFT);
> +    if ( p->desc != NULL )
> +        lr_reg |= GICH_LR_HW |
> +            ((p->desc->irq & GICH_LR_PHYSICAL_MASK) << GICH_LR_PHYSICAL_SHIFT);
> +
> +    GICH[GICH_LR + lr] = lr_reg;
>  
>      set_bit(GIC_IRQ_GUEST_VISIBLE, &p->status);
>      clear_bit(GIC_IRQ_GUEST_PENDING, &p->status);
> @@ -669,7 +675,7 @@ void gic_remove_from_queues(struct vcpu *v, unsigned int virtual_irq)
>      spin_unlock_irqrestore(&gic.lock, flags);
>  }
>  
> -void gic_set_guest_irq(struct vcpu *v, unsigned int virtual_irq,
> +void gic_set_guest_irq(struct vcpu *v, unsigned int irq,

Any reason to rename virtual_irq into irq?

[..]

> +static void gic_clear_lrs(struct vcpu *v)
> +{
> +    struct pending_irq *p;
> +    int i = 0, irq;
> +    uint32_t lr;
> +    bool_t inflight;
> +
> +    ASSERT(!local_irq_is_enabled());
> +
> +    while ((i = find_next_bit((const long unsigned int *) &this_cpu(lr_mask),
> +                              nr_lrs, i)) < nr_lrs) {
> +        lr = GICH[GICH_LR + i];
> +        if ( !(lr & (GICH_LR_PENDING|GICH_LR_ACTIVE)) )
> +        {
> +            inflight = 0;
> +            GICH[GICH_LR + i] = 0;
> +            clear_bit(i, &this_cpu(lr_mask));
> +
> +            irq = (lr >> GICH_LR_VIRTUAL_SHIFT) & GICH_LR_VIRTUAL_MASK;
> +            spin_lock(&gic.lock);

Not completely related to this patch ... taking gic.lock seems a bit too
strong here. The critical section only update data for the current
domain. It seems a bit stupid to block the other interrupt to handle
their interrupts at the same time.

Maybe introducing a dist lock would be a better solution?

[..]

>  void gic_dump_info(struct vcpu *v)
> diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
> index aab490c..566f0ff 100644
> --- a/xen/arch/arm/vgic.c
> +++ b/xen/arch/arm/vgic.c
> @@ -701,8 +701,7 @@ void vgic_vcpu_inject_irq(struct vcpu *v, unsigned int irq)
>          if ( (irq != current->domain->arch.evtchn_irq) ||
>               (!test_bit(GIC_IRQ_GUEST_VISIBLE, &n->status)) )
>              set_bit(GIC_IRQ_GUEST_PENDING, &n->status);
> -        spin_unlock_irqrestore(&v->arch.vgic.lock, flags);
> -        return;
> +        goto out;

We don't want to kick the other VCPU every time. I think it's enough
when the interrupt is updated.

Regards,
Stefano Stabellini March 19, 2014, 2:43 p.m. UTC | #2
On Wed, 19 Mar 2014, Julien Grall wrote:
> Hi Stefano,
> 
> On 03/19/2014 12:31 PM, Stefano Stabellini wrote:
> 
> [..]
> 
> > @@ -625,16 +628,19 @@ int __init setup_dt_irq(const struct dt_irq *irq, struct irqaction *new)
> >  static inline void gic_set_lr(int lr, struct pending_irq *p,
> >          unsigned int state)
> >  {
> > -    int maintenance_int = GICH_LR_MAINTENANCE_IRQ;
> > +    uint32_t lr_reg;
> >  
> >      BUG_ON(lr >= nr_lrs);
> >      BUG_ON(lr < 0);
> >      BUG_ON(state & ~(GICH_LR_STATE_MASK<<GICH_LR_STATE_SHIFT));
> >  
> > -    GICH[GICH_LR + lr] = state |
> > -        maintenance_int |
> > -        ((p->priority >> 3) << GICH_LR_PRIORITY_SHIFT) |
> > +    lr_reg = state | ((p->priority >> 3) << GICH_LR_PRIORITY_SHIFT) |
> >          ((p->irq & GICH_LR_VIRTUAL_MASK) << GICH_LR_VIRTUAL_SHIFT);
> > +    if ( p->desc != NULL )
> > +        lr_reg |= GICH_LR_HW |
> > +            ((p->desc->irq & GICH_LR_PHYSICAL_MASK) << GICH_LR_PHYSICAL_SHIFT);
> > +
> > +    GICH[GICH_LR + lr] = lr_reg;
> >  
> >      set_bit(GIC_IRQ_GUEST_VISIBLE, &p->status);
> >      clear_bit(GIC_IRQ_GUEST_PENDING, &p->status);
> > @@ -669,7 +675,7 @@ void gic_remove_from_queues(struct vcpu *v, unsigned int virtual_irq)
> >      spin_unlock_irqrestore(&gic.lock, flags);
> >  }
> >  
> > -void gic_set_guest_irq(struct vcpu *v, unsigned int virtual_irq,
> > +void gic_set_guest_irq(struct vcpu *v, unsigned int irq,
> 
> Any reason to rename virtual_irq into irq?

Just that with this patch we also use this function to inject hw irqs.


> [..]
> 
> > +static void gic_clear_lrs(struct vcpu *v)
> > +{
> > +    struct pending_irq *p;
> > +    int i = 0, irq;
> > +    uint32_t lr;
> > +    bool_t inflight;
> > +
> > +    ASSERT(!local_irq_is_enabled());
> > +
> > +    while ((i = find_next_bit((const long unsigned int *) &this_cpu(lr_mask),
> > +                              nr_lrs, i)) < nr_lrs) {
> > +        lr = GICH[GICH_LR + i];
> > +        if ( !(lr & (GICH_LR_PENDING|GICH_LR_ACTIVE)) )
> > +        {
> > +            inflight = 0;
> > +            GICH[GICH_LR + i] = 0;
> > +            clear_bit(i, &this_cpu(lr_mask));
> > +
> > +            irq = (lr >> GICH_LR_VIRTUAL_SHIFT) & GICH_LR_VIRTUAL_MASK;
> > +            spin_lock(&gic.lock);
> 
> Not completely related to this patch ... taking gic.lock seems a bit too
> strong here. The critical section only update data for the current
> domain. It seems a bit stupid to block the other interrupt to handle
> their interrupts at the same time.
> 
> Maybe introducing a dist lock would be a better solution?

It gets removed by a later patch: "don't protect GICH and lr_queue
accesses with gic.lock".


> [..]
> 
> >  void gic_dump_info(struct vcpu *v)
> > diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
> > index aab490c..566f0ff 100644
> > --- a/xen/arch/arm/vgic.c
> > +++ b/xen/arch/arm/vgic.c
> > @@ -701,8 +701,7 @@ void vgic_vcpu_inject_irq(struct vcpu *v, unsigned int irq)
> >          if ( (irq != current->domain->arch.evtchn_irq) ||
> >               (!test_bit(GIC_IRQ_GUEST_VISIBLE, &n->status)) )
> >              set_bit(GIC_IRQ_GUEST_PENDING, &n->status);
> > -        spin_unlock_irqrestore(&v->arch.vgic.lock, flags);
> > -        return;
> > +        goto out;
> 
> We don't want to kick the other VCPU every time. I think it's enough
> when the interrupt is updated.

Without maintenance interrupts, the other vcpu potentially might never
return. We need to send an SGI to it to make sure it gets interrupted.
Once it is interrupted, it is going to run gic_clear_lrs that clears the
old LR and inject the new interrupt.
Julien Grall March 19, 2014, 3:11 p.m. UTC | #3
Hi Stefano,

On 03/19/2014 02:43 PM, Stefano Stabellini wrote:
> On Wed, 19 Mar 2014, Julien Grall wrote:
>> On 03/19/2014 12:31 PM, Stefano Stabellini wrote:
>>
>> [..]
>>
>>> @@ -625,16 +628,19 @@ int __init setup_dt_irq(const struct dt_irq *irq, struct irqaction *new)
>>>  static inline void gic_set_lr(int lr, struct pending_irq *p,
>>>          unsigned int state)
>>>  {
>>> -    int maintenance_int = GICH_LR_MAINTENANCE_IRQ;
>>> +    uint32_t lr_reg;
>>>  
>>>      BUG_ON(lr >= nr_lrs);
>>>      BUG_ON(lr < 0);
>>>      BUG_ON(state & ~(GICH_LR_STATE_MASK<<GICH_LR_STATE_SHIFT));
>>>  
>>> -    GICH[GICH_LR + lr] = state |
>>> -        maintenance_int |
>>> -        ((p->priority >> 3) << GICH_LR_PRIORITY_SHIFT) |
>>> +    lr_reg = state | ((p->priority >> 3) << GICH_LR_PRIORITY_SHIFT) |
>>>          ((p->irq & GICH_LR_VIRTUAL_MASK) << GICH_LR_VIRTUAL_SHIFT);
>>> +    if ( p->desc != NULL )
>>> +        lr_reg |= GICH_LR_HW |
>>> +            ((p->desc->irq & GICH_LR_PHYSICAL_MASK) << GICH_LR_PHYSICAL_SHIFT);
>>> +
>>> +    GICH[GICH_LR + lr] = lr_reg;
>>>  
>>>      set_bit(GIC_IRQ_GUEST_VISIBLE, &p->status);
>>>      clear_bit(GIC_IRQ_GUEST_PENDING, &p->status);
>>> @@ -669,7 +675,7 @@ void gic_remove_from_queues(struct vcpu *v, unsigned int virtual_irq)
>>>      spin_unlock_irqrestore(&gic.lock, flags);
>>>  }
>>>  
>>> -void gic_set_guest_irq(struct vcpu *v, unsigned int virtual_irq,
>>> +void gic_set_guest_irq(struct vcpu *v, unsigned int irq,
>>
>> Any reason to rename virtual_irq into irq?
> 
> Just that with this patch we also use this function to inject hw irqs.

As I understand the code, this function still takes a VIRQ. If Xen
injects an hw irq, it will before translate it to a VIRQ.

By VIRQ I mean the IRQ number from guest POV.

>> [..]
>>
>>> +static void gic_clear_lrs(struct vcpu *v)
>>> +{
>>> +    struct pending_irq *p;
>>> +    int i = 0, irq;
>>> +    uint32_t lr;
>>> +    bool_t inflight;
>>> +
>>> +    ASSERT(!local_irq_is_enabled());
>>> +
>>> +    while ((i = find_next_bit((const long unsigned int *) &this_cpu(lr_mask),
>>> +                              nr_lrs, i)) < nr_lrs) {
>>> +        lr = GICH[GICH_LR + i];
>>> +        if ( !(lr & (GICH_LR_PENDING|GICH_LR_ACTIVE)) )
>>> +        {
>>> +            inflight = 0;
>>> +            GICH[GICH_LR + i] = 0;
>>> +            clear_bit(i, &this_cpu(lr_mask));
>>> +
>>> +            irq = (lr >> GICH_LR_VIRTUAL_SHIFT) & GICH_LR_VIRTUAL_MASK;
>>> +            spin_lock(&gic.lock);
>>
>> Not completely related to this patch ... taking gic.lock seems a bit too
>> strong here. The critical section only update data for the current
>> domain. It seems a bit stupid to block the other interrupt to handle
>> their interrupts at the same time.
>>
>> Maybe introducing a dist lock would be a better solution?
> 
> It gets removed by a later patch: "don't protect GICH and lr_queue
> accesses with gic.lock".

Ok.

>> [..]
>>
>>>  void gic_dump_info(struct vcpu *v)
>>> diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
>>> index aab490c..566f0ff 100644
>>> --- a/xen/arch/arm/vgic.c
>>> +++ b/xen/arch/arm/vgic.c
>>> @@ -701,8 +701,7 @@ void vgic_vcpu_inject_irq(struct vcpu *v, unsigned int irq)
>>>          if ( (irq != current->domain->arch.evtchn_irq) ||
>>>               (!test_bit(GIC_IRQ_GUEST_VISIBLE, &n->status)) )
>>>              set_bit(GIC_IRQ_GUEST_PENDING, &n->status);
>>> -        spin_unlock_irqrestore(&v->arch.vgic.lock, flags);
>>> -        return;
>>> +        goto out;
>>
>> We don't want to kick the other VCPU every time. I think it's enough
>> when the interrupt is updated.
> 
> Without maintenance interrupts, the other vcpu potentially might never
> return. We need to send an SGI to it to make sure it gets interrupted.
> Once it is interrupted, it is going to run gic_clear_lrs that clears the
> old LR and inject the new interrupt.

I'm not entirely convince, we only need to update when you set
GIC_IRQ_GUEST_PENDING in &n->status (e.g in the if sentence). If you
don't update the IRQ status, you don't need to kick the VCPUs because
either he is already unblock or it will be schedule soon.

Anyway this case only happen when we inject the evtchn IRQ. I think it
might need some comment as reading only the code is unclear what is done
here...

Regards,
Stefano Stabellini March 19, 2014, 3:53 p.m. UTC | #4
On Wed, 19 Mar 2014, Julien Grall wrote:
> >> [..]
> >>
> >>>  void gic_dump_info(struct vcpu *v)
> >>> diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
> >>> index aab490c..566f0ff 100644
> >>> --- a/xen/arch/arm/vgic.c
> >>> +++ b/xen/arch/arm/vgic.c
> >>> @@ -701,8 +701,7 @@ void vgic_vcpu_inject_irq(struct vcpu *v, unsigned int irq)
> >>>          if ( (irq != current->domain->arch.evtchn_irq) ||
> >>>               (!test_bit(GIC_IRQ_GUEST_VISIBLE, &n->status)) )
> >>>              set_bit(GIC_IRQ_GUEST_PENDING, &n->status);
> >>> -        spin_unlock_irqrestore(&v->arch.vgic.lock, flags);
> >>> -        return;
> >>> +        goto out;
> >>
> >> We don't want to kick the other VCPU every time. I think it's enough
> >> when the interrupt is updated.
> > 
> > Without maintenance interrupts, the other vcpu potentially might never
> > return. We need to send an SGI to it to make sure it gets interrupted.
> > Once it is interrupted, it is going to run gic_clear_lrs that clears the
> > old LR and inject the new interrupt.
> 
> I'm not entirely convince, we only need to update when you set
> GIC_IRQ_GUEST_PENDING in &n->status (e.g in the if sentence). If you
> don't update the IRQ status, you don't need to kick the VCPUs because
> either he is already unblock or it will be schedule soon.
> 
> Anyway this case only happen when we inject the evtchn IRQ. I think it
> might need some comment as reading only the code is unclear what is done
> here...

Reading the code again and your reply I think you might be right, it
introduces few unneeded interruptions to the other cpu.

However this is one of the bits of code that gets changed by another
patch, see "second irq injection while the first irq is still inflight".
That patch fixes this issue and more.
Julien Grall March 19, 2014, 4:10 p.m. UTC | #5
On 03/19/2014 03:53 PM, Stefano Stabellini wrote:
> However this is one of the bits of code that gets changed by another
> patch, see "second irq injection while the first irq is still inflight".
> That patch fixes this issue and more.

Thanks, I didn't yet look at this patch.

Regards,
Ian Campbell March 21, 2014, 1:04 p.m. UTC | #6
On Wed, 2014-03-19 at 12:31 +0000, Stefano Stabellini wrote:
> If the irq to be injected is an hardware irq (p->desc != NULL), set
> GICH_LR_HW. Do not set GICH_LR_MAINTENANCE_IRQ.
> 
> Remove the code to EOI a physical interrupt on behalf of the guest
> because it has become unnecessary.

Stupid question: there is no possibility of a h/w interrupt which for
one reason or another cannot be injected using these GIC facilities?

> 
> Introduce a new function, gic_clear_lrs, that goes over the GICH_LR
> registers, clear the invalid ones and free the corresponding interrupts
> from the inflight queue if appropriate. Add the interrupt to lr_pending
> if the GIC_IRQ_GUEST_PENDING is still set.
> 
> Call gic_clear_lrs from gic_restore_state and on return to guest
> (gic_inject).
> 
> In vgic_vcpu_inject_irq, if the target is a vcpu running on another cpu,
> send and SGI to it to interrupt it and force it to clear the old LRs.

s/and/an/

Is the SGI just there to cause it to flush its LRs? Does it not also
serve the purpose of causing the pcpu to pick up the potential new
interrupt?

> 
> Signed-off-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
> 
> ---
> 
> Changes in v4:
> - merged patch #3 and #4 into a single patch.
> 
> Changes in v2:
> - remove the EOI code, now unnecessary;
> - do not assume physical IRQ == virtual IRQ;
> - refactor gic_set_lr.
> ---
>  xen/arch/arm/gic.c  |  135 ++++++++++++++++++++++-----------------------------
>  xen/arch/arm/vgic.c |    3 +-
>  2 files changed, 60 insertions(+), 78 deletions(-)
> 
> diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
> index dbba5d3..32d3bea 100644
> --- a/xen/arch/arm/gic.c
> +++ b/xen/arch/arm/gic.c
> @@ -128,6 +130,7 @@ void gic_restore_state(struct vcpu *v)
>      GICH[GICH_HCR] = GICH_HCR_EN;
>      isb();
>  
> +    gic_clear_lrs(v);
>      gic_restore_pending_irqs(v);

Not related to this patch exactly, but is this function badly named? It
seems to not actually be restoring anything but is actually looking for
any newly pending irqs which it can now inject, is that right?

Both callers of gic_restore_pending_irqs call gic_clear_lrs. Which
suggests that the clear should be done there (which also seems logical).

>  }
>  
> @@ -625,16 +628,19 @@ int __init setup_dt_irq(const struct dt_irq *irq, struct irqaction *new)
>  static inline void gic_set_lr(int lr, struct pending_irq *p,
>          unsigned int state)
>  {
> -    int maintenance_int = GICH_LR_MAINTENANCE_IRQ;
> +    uint32_t lr_reg;
>  
>      BUG_ON(lr >= nr_lrs);
>      BUG_ON(lr < 0);
>      BUG_ON(state & ~(GICH_LR_STATE_MASK<<GICH_LR_STATE_SHIFT));
>  
> -    GICH[GICH_LR + lr] = state |
> -        maintenance_int |
> -        ((p->priority >> 3) << GICH_LR_PRIORITY_SHIFT) |
> +    lr_reg = state | ((p->priority >> 3) << GICH_LR_PRIORITY_SHIFT) |
>          ((p->irq & GICH_LR_VIRTUAL_MASK) << GICH_LR_VIRTUAL_SHIFT);
> +    if ( p->desc != NULL )
> +        lr_reg |= GICH_LR_HW |
> +            ((p->desc->irq & GICH_LR_PHYSICAL_MASK) << GICH_LR_PHYSICAL_SHIFT);

It seems like it would be a silicon design bug if this were ever true.
So BUG_ON(p->desc->irq & ~GICH_LR_PHYSICAL_MASK)?

I think this should be checked at the time the interrupt is routed
instead of here, which gets it out of this hotter path.

Another future cleanup: I think a lot of this register frobbing code
would be cleaner if GICH_LR_FOO_MASK was already shifted.

[...]
> +static void gic_clear_lrs(struct vcpu *v)
> +{
> +    struct pending_irq *p;
> +    int i = 0, irq;
> +    uint32_t lr;
> +    bool_t inflight;
> +
> +    ASSERT(!local_irq_is_enabled());
> +
> +    while ((i = find_next_bit((const long unsigned int *) &this_cpu(lr_mask),

"long unsigned int" is an odd one isn't it?

It seems like all of the other places which do bitops on lr_mask manage
to avoid any case at all.

> +                              nr_lrs, i)) < nr_lrs) {
> +        lr = GICH[GICH_LR + i];
> +        if ( !(lr & (GICH_LR_PENDING|GICH_LR_ACTIVE)) )

This state means the lr is "completed" (or inactive, however you want to
think about it)? A little helper macro lr_completed(lr) would clarify
this without needing a comment.

Isn't that condition also true for an LR which was never injected? Won't
we then try and complete a non-existent interrupt? Ah, this is protected
by the lr_mask isn't it.

> +        {
> +            inflight = 0;
> +            GICH[GICH_LR + i] = 0;
> +            clear_bit(i, &this_cpu(lr_mask));
> +
> +            irq = (lr >> GICH_LR_VIRTUAL_SHIFT) & GICH_LR_VIRTUAL_MASK;
> +            spin_lock(&gic.lock);
> +            p = irq_to_pending(v, irq);
> +            if ( p->desc != NULL )
> +                p->desc->status &= ~IRQ_INPROGRESS;
> +            clear_bit(GIC_IRQ_GUEST_VISIBLE, &p->status);
> +            if ( test_bit(GIC_IRQ_GUEST_PENDING, &p->status) &&
> +                    test_bit(GIC_IRQ_GUEST_ENABLED, &p->status))
> +            {
> +                inflight = 1;
> +                gic_set_guest_irq(v, irq, GICH_LR_PENDING, p->priority);
> +            }
> +            spin_unlock(&gic.lock);

Can an interrupt arrive at this point and make this interrupt "inflight"
again, such that the following list remove is actually wrong?

> +            if ( !inflight )
> +            {
> +                spin_lock(&v->arch.vgic.lock);
> +                list_del_init(&p->inflight);
> +                spin_unlock(&v->arch.vgic.lock);
> +            }
> +
> +        }
> +
> +        i++;
> +    }
> +}
> +
>  static void gic_restore_pending_irqs(struct vcpu *v)
>  {
>      int i;
>  static void maintenance_interrupt(int irq, void *dev_id, struct cpu_user_regs *regs)
>  {
[...]

Is now empty but not removed?

>  }
>  
>  void gic_dump_info(struct vcpu *v)
> diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
> index aab490c..566f0ff 100644
> --- a/xen/arch/arm/vgic.c
> +++ b/xen/arch/arm/vgic.c
> @@ -701,8 +701,7 @@ void vgic_vcpu_inject_irq(struct vcpu *v, unsigned int irq)
>          if ( (irq != current->domain->arch.evtchn_irq) ||
>               (!test_bit(GIC_IRQ_GUEST_VISIBLE, &n->status)) )
>              set_bit(GIC_IRQ_GUEST_PENDING, &n->status);
> -        spin_unlock_irqrestore(&v->arch.vgic.lock, flags);
> -        return;
> +        goto out;
>      }
>  
>      /* vcpu offline */
Stefano Stabellini March 21, 2014, 3:55 p.m. UTC | #7
On Fri, 21 Mar 2014, Ian Campbell wrote:
> On Wed, 2014-03-19 at 12:31 +0000, Stefano Stabellini wrote:
> > If the irq to be injected is an hardware irq (p->desc != NULL), set
> > GICH_LR_HW. Do not set GICH_LR_MAINTENANCE_IRQ.
> > 
> > Remove the code to EOI a physical interrupt on behalf of the guest
> > because it has become unnecessary.
> 
> Stupid question: there is no possibility of a h/w interrupt which for
> one reason or another cannot be injected using these GIC facilities?

I don't think so. Nothing is written in spec about such a case.


> > Introduce a new function, gic_clear_lrs, that goes over the GICH_LR
> > registers, clear the invalid ones and free the corresponding interrupts
> > from the inflight queue if appropriate. Add the interrupt to lr_pending
> > if the GIC_IRQ_GUEST_PENDING is still set.
> > 
> > Call gic_clear_lrs from gic_restore_state and on return to guest
> > (gic_inject).
> > 
> > In vgic_vcpu_inject_irq, if the target is a vcpu running on another cpu,
> > send and SGI to it to interrupt it and force it to clear the old LRs.
> 
> s/and/an/
> 
> Is the SGI just there to cause it to flush its LRs? Does it not also
> serve the purpose of causing the pcpu to pick up the potential new
> interrupt?

Yes. Before this patch we were already sending an SGI to the other pcpu
so that it would pick up the new IRQ.
Now we also send an SGI to the other pcpu even if the IRQ is already
inflight, so that the second pcpu can clear the LR corresponding to the
previous injection as well as injecting the new interrupt.


> > Signed-off-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
> > 
> > ---
> > 
> > Changes in v4:
> > - merged patch #3 and #4 into a single patch.
> > 
> > Changes in v2:
> > - remove the EOI code, now unnecessary;
> > - do not assume physical IRQ == virtual IRQ;
> > - refactor gic_set_lr.
> > ---
> >  xen/arch/arm/gic.c  |  135 ++++++++++++++++++++++-----------------------------
> >  xen/arch/arm/vgic.c |    3 +-
> >  2 files changed, 60 insertions(+), 78 deletions(-)
> > 
> > diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
> > index dbba5d3..32d3bea 100644
> > --- a/xen/arch/arm/gic.c
> > +++ b/xen/arch/arm/gic.c
> > @@ -128,6 +130,7 @@ void gic_restore_state(struct vcpu *v)
> >      GICH[GICH_HCR] = GICH_HCR_EN;
> >      isb();
> >  
> > +    gic_clear_lrs(v);
> >      gic_restore_pending_irqs(v);
> 
> Not related to this patch exactly, but is this function badly named? It
> seems to not actually be restoring anything but is actually looking for
> any newly pending irqs which it can now inject, is that right?

Yeah, that is correct.


> Both callers of gic_restore_pending_irqs call gic_clear_lrs. Which
> suggests that the clear should be done there (which also seems logical).

It is just temporary: patch "call gic_clear_lrs on entry to the
hypervisor" moves the call to gic_clear_lrs to traps.c.


> >  }
> >  
> > @@ -625,16 +628,19 @@ int __init setup_dt_irq(const struct dt_irq *irq, struct irqaction *new)
> >  static inline void gic_set_lr(int lr, struct pending_irq *p,
> >          unsigned int state)
> >  {
> > -    int maintenance_int = GICH_LR_MAINTENANCE_IRQ;
> > +    uint32_t lr_reg;
> >  
> >      BUG_ON(lr >= nr_lrs);
> >      BUG_ON(lr < 0);
> >      BUG_ON(state & ~(GICH_LR_STATE_MASK<<GICH_LR_STATE_SHIFT));
> >  
> > -    GICH[GICH_LR + lr] = state |
> > -        maintenance_int |
> > -        ((p->priority >> 3) << GICH_LR_PRIORITY_SHIFT) |
> > +    lr_reg = state | ((p->priority >> 3) << GICH_LR_PRIORITY_SHIFT) |
> >          ((p->irq & GICH_LR_VIRTUAL_MASK) << GICH_LR_VIRTUAL_SHIFT);
> > +    if ( p->desc != NULL )
> > +        lr_reg |= GICH_LR_HW |
> > +            ((p->desc->irq & GICH_LR_PHYSICAL_MASK) << GICH_LR_PHYSICAL_SHIFT);
> 
> It seems like it would be a silicon design bug if this were ever true.
> So BUG_ON(p->desc->irq & ~GICH_LR_PHYSICAL_MASK)?
 
Right, I'll remove it.


> I think this should be checked at the time the interrupt is routed
> instead of here, which gets it out of this hotter path.

Actually it cannot happen as desc->irq is initialized by init_irq_data
in a for loop up to NR_IRQS (1024).


> Another future cleanup: I think a lot of this register frobbing code
> would be cleaner if GICH_LR_FOO_MASK was already shifted.
> 
> [...]
> > +static void gic_clear_lrs(struct vcpu *v)
> > +{
> > +    struct pending_irq *p;
> > +    int i = 0, irq;
> > +    uint32_t lr;
> > +    bool_t inflight;
> > +
> > +    ASSERT(!local_irq_is_enabled());
> > +
> > +    while ((i = find_next_bit((const long unsigned int *) &this_cpu(lr_mask),
> 
> "long unsigned int" is an odd one isn't it?
> 
> It seems like all of the other places which do bitops on lr_mask manage
> to avoid any case at all.

_find_next_bit_le requires a const unsigned long *p as first argument.
I'll make the change to const unsigned long.


> > +                              nr_lrs, i)) < nr_lrs) {
> > +        lr = GICH[GICH_LR + i];
> > +        if ( !(lr & (GICH_LR_PENDING|GICH_LR_ACTIVE)) )
> 
> This state means the lr is "completed" (or inactive, however you want to
> think about it)? A little helper macro lr_completed(lr) would clarify
> this without needing a comment.

This is another one of those pieces of code that are going to disappear
in the following patches. I think the final version is going to look
nicer, even without macros.


> Isn't that condition also true for an LR which was never injected? Won't
> we then try and complete a non-existent interrupt? Ah, this is protected
> by the lr_mask isn't it.

We are only iterating over lr_mask, that means that we have written to
this lr in the recent past.


> > +        {
> > +            inflight = 0;
> > +            GICH[GICH_LR + i] = 0;
> > +            clear_bit(i, &this_cpu(lr_mask));
> > +
> > +            irq = (lr >> GICH_LR_VIRTUAL_SHIFT) & GICH_LR_VIRTUAL_MASK;
> > +            spin_lock(&gic.lock);
> > +            p = irq_to_pending(v, irq);
> > +            if ( p->desc != NULL )
> > +                p->desc->status &= ~IRQ_INPROGRESS;
> > +            clear_bit(GIC_IRQ_GUEST_VISIBLE, &p->status);
> > +            if ( test_bit(GIC_IRQ_GUEST_PENDING, &p->status) &&
> > +                    test_bit(GIC_IRQ_GUEST_ENABLED, &p->status))
> > +            {
> > +                inflight = 1;
> > +                gic_set_guest_irq(v, irq, GICH_LR_PENDING, p->priority);
> > +            }
> > +            spin_unlock(&gic.lock);
> 
> Can an interrupt arrive at this point and make this interrupt "inflight"
> again, such that the following list remove is actually wrong?

gic_clear_lrs is always called with irq disabled, see the ASSERT at the
beginning of the function


> > +            {
> > +                spin_lock(&v->arch.vgic.lock);
> > +                list_del_init(&p->inflight);
> > +                spin_unlock(&v->arch.vgic.lock);
> > +            }
> > +
> > +        }
> > +
> > +        i++;
> > +    }
> > +}
> > +
> >  static void gic_restore_pending_irqs(struct vcpu *v)
> >  {
> >      int i;
> >  static void maintenance_interrupt(int irq, void *dev_id, struct cpu_user_regs *regs)
> >  {
> [...]
> 
> Is now empty but not removed?

Yes. We want to keep receiving maintenance interrupts, but we don't need
to do anything anymore in the handler because everything we need to do
is done on return to guest.
Ian Campbell March 21, 2014, 4:16 p.m. UTC | #8
On Fri, 2014-03-21 at 15:55 +0000, Stefano Stabellini wrote:
> On Fri, 21 Mar 2014, Ian Campbell wrote:
> > On Wed, 2014-03-19 at 12:31 +0000, Stefano Stabellini wrote:
> > > If the irq to be injected is an hardware irq (p->desc != NULL), set
> > > GICH_LR_HW. Do not set GICH_LR_MAINTENANCE_IRQ.
> > > 
> > > Remove the code to EOI a physical interrupt on behalf of the guest
> > > because it has become unnecessary.
> > 
> > Stupid question: there is no possibility of a h/w interrupt which for
> > one reason or another cannot be injected using these GIC facilities?
> 
> I don't think so. Nothing is written in spec about such a case.
> 
> 
> > > Introduce a new function, gic_clear_lrs, that goes over the GICH_LR
> > > registers, clear the invalid ones and free the corresponding interrupts
> > > from the inflight queue if appropriate. Add the interrupt to lr_pending
> > > if the GIC_IRQ_GUEST_PENDING is still set.
> > > 
> > > Call gic_clear_lrs from gic_restore_state and on return to guest
> > > (gic_inject).
> > > 
> > > In vgic_vcpu_inject_irq, if the target is a vcpu running on another cpu,
> > > send and SGI to it to interrupt it and force it to clear the old LRs.
> > 
> > s/and/an/
> > 
> > Is the SGI just there to cause it to flush its LRs? Does it not also
> > serve the purpose of causing the pcpu to pick up the potential new
> > interrupt?
> 
> Yes. Before this patch we were already sending an SGI to the other pcpu
> so that it would pick up the new IRQ.
> Now we also send an SGI to the other pcpu even if the IRQ is already
> inflight, so that the second pcpu can clear the LR corresponding to the
> previous injection as well as injecting the new interrupt.

Can you clarify that in the commit message please? (pretty much
inserting what you just said will do).

I assume that there is no chance we will send two SGIs?

> > Both callers of gic_restore_pending_irqs call gic_clear_lrs. Which
> > suggests that the clear should be done there (which also seems logical).
> 
> It is just temporary: patch "call gic_clear_lrs on entry to the
> hypervisor" moves the call to gic_clear_lrs to traps.c.

I noticed that later, any reason for taking this roundabout path to the
final destination?

> > > @@ -625,16 +628,19 @@ int __init setup_dt_irq(const struct dt_irq *irq, struct irqaction *new)
> > >  static inline void gic_set_lr(int lr, struct pending_irq *p,
> > >          unsigned int state)
> > >  {
> > > -    int maintenance_int = GICH_LR_MAINTENANCE_IRQ;
> > > +    uint32_t lr_reg;
> > >  
> > >      BUG_ON(lr >= nr_lrs);
> > >      BUG_ON(lr < 0);
> > >      BUG_ON(state & ~(GICH_LR_STATE_MASK<<GICH_LR_STATE_SHIFT));
> > >  
> > > -    GICH[GICH_LR + lr] = state |
> > > -        maintenance_int |
> > > -        ((p->priority >> 3) << GICH_LR_PRIORITY_SHIFT) |
> > > +    lr_reg = state | ((p->priority >> 3) << GICH_LR_PRIORITY_SHIFT) |
> > >          ((p->irq & GICH_LR_VIRTUAL_MASK) << GICH_LR_VIRTUAL_SHIFT);
> > > +    if ( p->desc != NULL )
> > > +        lr_reg |= GICH_LR_HW |
> > > +            ((p->desc->irq & GICH_LR_PHYSICAL_MASK) << GICH_LR_PHYSICAL_SHIFT);
> > 
> > It seems like it would be a silicon design bug if this were ever true.
> > So BUG_ON(p->desc->irq & ~GICH_LR_PHYSICAL_MASK)?
>  
> Right, I'll remove it.
> 
> 
> > I think this should be checked at the time the interrupt is routed
> > instead of here, which gets it out of this hotter path.
> 
> Actually it cannot happen as desc->irq is initialized by init_irq_data
> in a for loop up to NR_IRQS (1024).

Excellent.

(although beware gic v3 ;-))


> > > +        {
> > > +            inflight = 0;
> > > +            GICH[GICH_LR + i] = 0;
> > > +            clear_bit(i, &this_cpu(lr_mask));
> > > +
> > > +            irq = (lr >> GICH_LR_VIRTUAL_SHIFT) & GICH_LR_VIRTUAL_MASK;
> > > +            spin_lock(&gic.lock);
> > > +            p = irq_to_pending(v, irq);
> > > +            if ( p->desc != NULL )
> > > +                p->desc->status &= ~IRQ_INPROGRESS;
> > > +            clear_bit(GIC_IRQ_GUEST_VISIBLE, &p->status);
> > > +            if ( test_bit(GIC_IRQ_GUEST_PENDING, &p->status) &&
> > > +                    test_bit(GIC_IRQ_GUEST_ENABLED, &p->status))
> > > +            {
> > > +                inflight = 1;
> > > +                gic_set_guest_irq(v, irq, GICH_LR_PENDING, p->priority);
> > > +            }
> > > +            spin_unlock(&gic.lock);
> > 
> > Can an interrupt arrive at this point and make this interrupt "inflight"
> > again, such that the following list remove is actually wrong?
> 
> gic_clear_lrs is always called with irq disabled, see the ASSERT at the
> beginning of the function

Great.

> > 
> > Is now empty but not removed?
> 
> Yes. We want to keep receiving maintenance interrupts, but we don't need
> to do anything anymore in the handler because everything we need to do
> is done on return to guest.

I figured that out on the next patch -- a comment in any empty interrupt
handler would be very useful.

Ian.
Stefano Stabellini March 24, 2014, 12:11 p.m. UTC | #9
On Fri, 21 Mar 2014, Ian Campbell wrote:
> > > Both callers of gic_restore_pending_irqs call gic_clear_lrs. Which
> > > suggests that the clear should be done there (which also seems logical).
> > 
> > It is just temporary: patch "call gic_clear_lrs on entry to the
> > hypervisor" moves the call to gic_clear_lrs to traps.c.
> 
> I noticed that later, any reason for taking this roundabout path to the
> final destination?

I can merge the two patches together if you prefer.
I tried to keep all the logical changes separated to allow better
debugging and easier reviews. Also it is much easier to merge patches
later upon request :-)
Ian Campbell March 24, 2014, 12:15 p.m. UTC | #10
On Mon, 2014-03-24 at 12:11 +0000, Stefano Stabellini wrote:
> On Fri, 21 Mar 2014, Ian Campbell wrote:
> > > > Both callers of gic_restore_pending_irqs call gic_clear_lrs. Which
> > > > suggests that the clear should be done there (which also seems logical).
> > > 
> > > It is just temporary: patch "call gic_clear_lrs on entry to the
> > > hypervisor" moves the call to gic_clear_lrs to traps.c.
> > 
> > I noticed that later, any reason for taking this roundabout path to the
> > final destination?
> 
> I can merge the two patches together if you prefer.
> I tried to keep all the logical changes separated to allow better
> debugging and easier reviews. Also it is much easier to merge patches
> later upon request :-)

The flip side is that reviewers now and up looking at some intermediate
state which isn't actually useful (and is actually something of a waste
of their time).

Ian.
diff mbox

Patch

diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
index dbba5d3..32d3bea 100644
--- a/xen/arch/arm/gic.c
+++ b/xen/arch/arm/gic.c
@@ -67,6 +67,8 @@  static DEFINE_PER_CPU(u8, gic_cpu_id);
 /* Maximum cpu interface per GIC */
 #define NR_GIC_CPU_IF 8
 
+static void gic_clear_lrs(struct vcpu *v);
+
 static unsigned int gic_cpu_mask(const cpumask_t *cpumask)
 {
     unsigned int cpu;
@@ -128,6 +130,7 @@  void gic_restore_state(struct vcpu *v)
     GICH[GICH_HCR] = GICH_HCR_EN;
     isb();
 
+    gic_clear_lrs(v);
     gic_restore_pending_irqs(v);
 }
 
@@ -625,16 +628,19 @@  int __init setup_dt_irq(const struct dt_irq *irq, struct irqaction *new)
 static inline void gic_set_lr(int lr, struct pending_irq *p,
         unsigned int state)
 {
-    int maintenance_int = GICH_LR_MAINTENANCE_IRQ;
+    uint32_t lr_reg;
 
     BUG_ON(lr >= nr_lrs);
     BUG_ON(lr < 0);
     BUG_ON(state & ~(GICH_LR_STATE_MASK<<GICH_LR_STATE_SHIFT));
 
-    GICH[GICH_LR + lr] = state |
-        maintenance_int |
-        ((p->priority >> 3) << GICH_LR_PRIORITY_SHIFT) |
+    lr_reg = state | ((p->priority >> 3) << GICH_LR_PRIORITY_SHIFT) |
         ((p->irq & GICH_LR_VIRTUAL_MASK) << GICH_LR_VIRTUAL_SHIFT);
+    if ( p->desc != NULL )
+        lr_reg |= GICH_LR_HW |
+            ((p->desc->irq & GICH_LR_PHYSICAL_MASK) << GICH_LR_PHYSICAL_SHIFT);
+
+    GICH[GICH_LR + lr] = lr_reg;
 
     set_bit(GIC_IRQ_GUEST_VISIBLE, &p->status);
     clear_bit(GIC_IRQ_GUEST_PENDING, &p->status);
@@ -669,7 +675,7 @@  void gic_remove_from_queues(struct vcpu *v, unsigned int virtual_irq)
     spin_unlock_irqrestore(&gic.lock, flags);
 }
 
-void gic_set_guest_irq(struct vcpu *v, unsigned int virtual_irq,
+void gic_set_guest_irq(struct vcpu *v, unsigned int irq,
         unsigned int state, unsigned int priority)
 {
     int i;
@@ -682,18 +688,62 @@  void gic_set_guest_irq(struct vcpu *v, unsigned int virtual_irq,
         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, irq_to_pending(v, virtual_irq), state);
+            gic_set_lr(i, irq_to_pending(v, irq), state);
             goto out;
         }
     }
 
-    gic_add_to_lr_pending(v, irq_to_pending(v, virtual_irq));
+    gic_add_to_lr_pending(v, irq_to_pending(v, irq));
 
 out:
     spin_unlock_irqrestore(&gic.lock, flags);
     return;
 }
 
+static void gic_clear_lrs(struct vcpu *v)
+{
+    struct pending_irq *p;
+    int i = 0, irq;
+    uint32_t lr;
+    bool_t inflight;
+
+    ASSERT(!local_irq_is_enabled());
+
+    while ((i = find_next_bit((const long unsigned int *) &this_cpu(lr_mask),
+                              nr_lrs, i)) < nr_lrs) {
+        lr = GICH[GICH_LR + i];
+        if ( !(lr & (GICH_LR_PENDING|GICH_LR_ACTIVE)) )
+        {
+            inflight = 0;
+            GICH[GICH_LR + i] = 0;
+            clear_bit(i, &this_cpu(lr_mask));
+
+            irq = (lr >> GICH_LR_VIRTUAL_SHIFT) & GICH_LR_VIRTUAL_MASK;
+            spin_lock(&gic.lock);
+            p = irq_to_pending(v, irq);
+            if ( p->desc != NULL )
+                p->desc->status &= ~IRQ_INPROGRESS;
+            clear_bit(GIC_IRQ_GUEST_VISIBLE, &p->status);
+            if ( test_bit(GIC_IRQ_GUEST_PENDING, &p->status) &&
+                    test_bit(GIC_IRQ_GUEST_ENABLED, &p->status))
+            {
+                inflight = 1;
+                gic_set_guest_irq(v, irq, GICH_LR_PENDING, p->priority);
+            }
+            spin_unlock(&gic.lock);
+            if ( !inflight )
+            {
+                spin_lock(&v->arch.vgic.lock);
+                list_del_init(&p->inflight);
+                spin_unlock(&v->arch.vgic.lock);
+            }
+
+        }
+
+        i++;
+    }
+}
+
 static void gic_restore_pending_irqs(struct vcpu *v)
 {
     int i;
@@ -734,6 +784,8 @@  int gic_events_need_delivery(void)
 
 void gic_inject(void)
 {
+    gic_clear_lrs(current);
+
     if ( vcpu_info(current, evtchn_upcall_pending) )
         vgic_vcpu_inject_irq(current, current->domain->arch.evtchn_irq);
 
@@ -887,77 +939,8 @@  int gicv_setup(struct domain *d)
 
 }
 
-static void gic_irq_eoi(void *info)
-{
-    int virq = (uintptr_t) info;
-    GICC[GICC_DIR] = virq;
-}
-
 static void maintenance_interrupt(int irq, void *dev_id, struct cpu_user_regs *regs)
 {
-    int i = 0, virq, pirq = -1;
-    uint32_t lr;
-    struct vcpu *v = current;
-    uint64_t eisr = GICH[GICH_EISR0] | (((uint64_t) GICH[GICH_EISR1]) << 32);
-
-    while ((i = find_next_bit((const long unsigned int *) &eisr,
-                              64, i)) < 64) {
-        struct pending_irq *p, *p2;
-        int cpu;
-        bool_t inflight;
-
-        cpu = -1;
-        inflight = 0;
-
-        spin_lock_irq(&gic.lock);
-        lr = GICH[GICH_LR + i];
-        virq = lr & GICH_LR_VIRTUAL_MASK;
-        GICH[GICH_LR + i] = 0;
-        clear_bit(i, &this_cpu(lr_mask));
-
-        p = irq_to_pending(v, virq);
-        if ( p->desc != NULL ) {
-            p->desc->status &= ~IRQ_INPROGRESS;
-            /* Assume only one pcpu needs to EOI the irq */
-            cpu = p->desc->arch.eoi_cpu;
-            pirq = p->desc->irq;
-        }
-        if ( test_bit(GIC_IRQ_GUEST_PENDING, &p->status) &&
-             test_bit(GIC_IRQ_GUEST_ENABLED, &p->status))
-        {
-            inflight = 1;
-            gic_add_to_lr_pending(v, p);
-        }
-
-        clear_bit(GIC_IRQ_GUEST_VISIBLE, &p->status);
-
-        if ( !list_empty(&v->arch.vgic.lr_pending) ) {
-            p2 = list_entry(v->arch.vgic.lr_pending.next, typeof(*p2), lr_queue);
-            gic_set_lr(i, p2, GICH_LR_PENDING);
-            list_del_init(&p2->lr_queue);
-            set_bit(i, &this_cpu(lr_mask));
-        }
-        spin_unlock_irq(&gic.lock);
-
-        if ( !inflight )
-        {
-            spin_lock_irq(&v->arch.vgic.lock);
-            list_del_init(&p->inflight);
-            spin_unlock_irq(&v->arch.vgic.lock);
-        }
-
-        if ( p->desc != NULL ) {
-            /* this is not racy because we can't receive another irq of the
-             * same type until we EOI it.  */
-            if ( cpu == smp_processor_id() )
-                gic_irq_eoi((void*)(uintptr_t)pirq);
-            else
-                on_selected_cpus(cpumask_of(cpu),
-                                 gic_irq_eoi, (void*)(uintptr_t)pirq, 0);
-        }
-
-        i++;
-    }
 }
 
 void gic_dump_info(struct vcpu *v)
diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
index aab490c..566f0ff 100644
--- a/xen/arch/arm/vgic.c
+++ b/xen/arch/arm/vgic.c
@@ -701,8 +701,7 @@  void vgic_vcpu_inject_irq(struct vcpu *v, unsigned int irq)
         if ( (irq != current->domain->arch.evtchn_irq) ||
              (!test_bit(GIC_IRQ_GUEST_VISIBLE, &n->status)) )
             set_bit(GIC_IRQ_GUEST_PENDING, &n->status);
-        spin_unlock_irqrestore(&v->arch.vgic.lock, flags);
-        return;
+        goto out;
     }
 
     /* vcpu offline */