diff mbox

[Xen-devel,v5,08/10] xen/arm: second irq injection while the first irq is still inflight

Message ID 1395686975-12649-8-git-send-email-stefano.stabellini@eu.citrix.com
State New
Headers show

Commit Message

Stefano Stabellini March 24, 2014, 6:49 p.m. UTC
Set GICH_LR_PENDING in the corresponding GICH_LR to inject a second irq
while the first one is still active.
If the first irq is already pending (not active), just clear
GIC_IRQ_GUEST_PENDING because the irq has already been injected and is
already visible by the guest.
If the irq has already been EOI'ed then just clear the GICH_LR right
away and move the interrupt to lr_pending so that it is going to be
reinjected by gic_restore_pending_irqs on return to guest.

If the target cpu is not the current cpu, then set GIC_IRQ_GUEST_PENDING
and send an SGI. The target cpu is going to be interrupted and call
gic_clear_lrs, that is going to take the same actions.

Unify the inflight and non-inflight code paths in vgic_vcpu_inject_irq.

Do not call vgic_vcpu_inject_irq from gic_inject if
evtchn_upcall_pending is set. If we remove that call, we don't need to
special case evtchn_irq in vgic_vcpu_inject_irq anymore.
We also need to force the first injection of evtchn_irq (call
gic_vcpu_inject_irq) from vgic_enable_irqs because evtchn_upcall_pending
is already set by common code on vcpu creation.

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

---

Changes in v3:
- do not use the PENDING and ACTIVE state for HW interrupts;
- unify the inflight and non-inflight code paths in
vgic_vcpu_inject_irq.
---
 xen/arch/arm/gic.c  |   41 ++++++++++++++++++++++++-----------------
 xen/arch/arm/vgic.c |   33 +++++++++++++++++----------------
 2 files changed, 41 insertions(+), 33 deletions(-)

Comments

Ian Campbell April 1, 2014, 12:14 p.m. UTC | #1
On Mon, 2014-03-24 at 18:49 +0000, Stefano Stabellini wrote:
> Set GICH_LR_PENDING in the corresponding GICH_LR to inject a second irq
> while the first one is still active.
> If the first irq is already pending (not active), just clear
> GIC_IRQ_GUEST_PENDING because the irq has already been injected and is
> already visible by the guest.

I'm confused by this.

If the interrupt is pending but not active then the guest has yet to
read GICC_IAR, so while it might be "visible" to the guest it has not
been observed by the guest.

The comment says:

     * GIC_IRQ_GUEST_PENDING: the irq is asserted

I'm not sure how a second irq arriving would correspond to deasserting
the IRQ.

Also if you are clearing GIC_IRQ_GUEST_PENDING without clearing the
pending bit in the LR that's rather confusing -- I thought the state was
supposed to correspond to (the most recently observed) LR state.

(having been told that there should be a v6 I'm going to stop here while
I figure out where it went...)

Ian.
Stefano Stabellini April 2, 2014, 3:31 p.m. UTC | #2
On Tue, 1 Apr 2014, Ian Campbell wrote:
> On Mon, 2014-03-24 at 18:49 +0000, Stefano Stabellini wrote:
> > Set GICH_LR_PENDING in the corresponding GICH_LR to inject a second irq
> > while the first one is still active.
> > If the first irq is already pending (not active), just clear
> > GIC_IRQ_GUEST_PENDING because the irq has already been injected and is
> > already visible by the guest.
> 
> I'm confused by this.
> 
> If the interrupt is pending but not active then the guest has yet to
> read GICC_IAR, so while it might be "visible" to the guest it has not
> been observed by the guest.
 
Yes, it is visible to the guest VM but not yet been observed by the
guest operating system.


> The comment says:
> 
>      * GIC_IRQ_GUEST_PENDING: the irq is asserted
> 
> I'm not sure how a second irq arriving would correspond to deasserting
> the IRQ.

I see where the confusion is coming from.
This comment is not quite correct unfortunately.

GIC_IRQ_GUEST_PENDING is set when the irq needs to be asserted (by
adding it to the LRs). Once the irq has become guest visible,
GIC_IRQ_GUEST_PENDING is cleared.

Going back to the top of the commit message:

"If the first irq is already pending (not active), just clear
GIC_IRQ_GUEST_PENDING because the irq has already been injected and is
already visible by the guest"

means that if the first irq has already been added to the LRs but it is
still in pending state, we cannot add it a second time, so just go ahead
and clear GIC_IRQ_GUEST_PENDING as if we did add it to the LRs, because
the guest is still going to receive a notification.


> Also if you are clearing GIC_IRQ_GUEST_PENDING without clearing the
> pending bit in the LR that's rather confusing -- I thought the state was
> supposed to correspond to (the most recently observed) LR state.

Unfortunately not quite: GIC_IRQ_GUEST_PENDING is set by
vgic_vcpu_inject_irq and cleared by gic_set_lr and gic_clear_one_lr.
Later in this series it can also be set by gic_restore_pending_irqs in
case we want to evict an irq from an LR to leave room for an higher
priority irq.


> (having been told that there should be a v6 I'm going to stop here while
> I figure out where it went...)

This patch is unchanged in v6.
Ian Campbell April 2, 2014, 3:43 p.m. UTC | #3
On Wed, 2014-04-02 at 16:31 +0100, Stefano Stabellini wrote:
> On Tue, 1 Apr 2014, Ian Campbell wrote:
> > On Mon, 2014-03-24 at 18:49 +0000, Stefano Stabellini wrote:
> > > Set GICH_LR_PENDING in the corresponding GICH_LR to inject a second irq
> > > while the first one is still active.
> > > If the first irq is already pending (not active), just clear
> > > GIC_IRQ_GUEST_PENDING because the irq has already been injected and is
> > > already visible by the guest.
> > 
> > I'm confused by this.
> > 
> > If the interrupt is pending but not active then the guest has yet to
> > read GICC_IAR, so while it might be "visible" to the guest it has not
> > been observed by the guest.
>  
> Yes, it is visible to the guest VM but not yet been observed by the
> guest operating system.
> 
> 
> > The comment says:
> > 
> >      * GIC_IRQ_GUEST_PENDING: the irq is asserted
> > 
> > I'm not sure how a second irq arriving would correspond to deasserting
> > the IRQ.
> 
> I see where the confusion is coming from.
> This comment is not quite correct unfortunately.
> 
> GIC_IRQ_GUEST_PENDING is set when the irq needs to be asserted (by
> adding it to the LRs). Once the irq has become guest visible,
> GIC_IRQ_GUEST_PENDING is cleared.
> 
> Going back to the top of the commit message:
> 
> "If the first irq is already pending (not active), just clear
> GIC_IRQ_GUEST_PENDING because the irq has already been injected and is
> already visible by the guest"
> 
> means that if the first irq has already been added to the LRs but it is
> still in pending state, we cannot add it a second time, so just go ahead
> and clear GIC_IRQ_GUEST_PENDING as if we did add it to the LRs, because
> the guest is still going to receive a notification.

OK, so when you use lower case pending/active you are talking about the
LR state. But when you use upper case GIC_IRQ_GUEST_PENDING this is a
completely separate state related to the queueing of interrupts within
Xen itself?

Can we s/GIC_IRQ_GUEST_PENDING/GIC_IRQ_GUEST_QUEUED/ perhaps? The
comment would be something like:
     * GIC_IRQ_GUEST_QUEUED: the irq is asserted and queued for
       injection into the guests LRs.

> 
> 
> > Also if you are clearing GIC_IRQ_GUEST_PENDING without clearing the
> > pending bit in the LR that's rather confusing -- I thought the state was
> > supposed to correspond to (the most recently observed) LR state.
> 
> Unfortunately not quite: GIC_IRQ_GUEST_PENDING is set by
> vgic_vcpu_inject_irq and cleared by gic_set_lr and gic_clear_one_lr.
> Later in this series it can also be set by gic_restore_pending_irqs in
> case we want to evict an irq from an LR to leave room for an higher
> priority irq.
> 
> 
> > (having been told that there should be a v6 I'm going to stop here while
> > I figure out where it went...)
> 
> This patch is unchanged in v6.

Thanks. I'll look into v6 ASAP, probably tomorrow AM.

Ian.
Stefano Stabellini April 6, 2014, 4:58 p.m. UTC | #4
On Wed, 2 Apr 2014, Ian Campbell wrote:
> On Wed, 2014-04-02 at 16:31 +0100, Stefano Stabellini wrote:
> > On Tue, 1 Apr 2014, Ian Campbell wrote:
> > > On Mon, 2014-03-24 at 18:49 +0000, Stefano Stabellini wrote:
> > > > Set GICH_LR_PENDING in the corresponding GICH_LR to inject a second irq
> > > > while the first one is still active.
> > > > If the first irq is already pending (not active), just clear
> > > > GIC_IRQ_GUEST_PENDING because the irq has already been injected and is
> > > > already visible by the guest.
> > > 
> > > I'm confused by this.
> > > 
> > > If the interrupt is pending but not active then the guest has yet to
> > > read GICC_IAR, so while it might be "visible" to the guest it has not
> > > been observed by the guest.
> >  
> > Yes, it is visible to the guest VM but not yet been observed by the
> > guest operating system.
> > 
> > 
> > > The comment says:
> > > 
> > >      * GIC_IRQ_GUEST_PENDING: the irq is asserted
> > > 
> > > I'm not sure how a second irq arriving would correspond to deasserting
> > > the IRQ.
> > 
> > I see where the confusion is coming from.
> > This comment is not quite correct unfortunately.
> > 
> > GIC_IRQ_GUEST_PENDING is set when the irq needs to be asserted (by
> > adding it to the LRs). Once the irq has become guest visible,
> > GIC_IRQ_GUEST_PENDING is cleared.
> > 
> > Going back to the top of the commit message:
> > 
> > "If the first irq is already pending (not active), just clear
> > GIC_IRQ_GUEST_PENDING because the irq has already been injected and is
> > already visible by the guest"
> > 
> > means that if the first irq has already been added to the LRs but it is
> > still in pending state, we cannot add it a second time, so just go ahead
> > and clear GIC_IRQ_GUEST_PENDING as if we did add it to the LRs, because
> > the guest is still going to receive a notification.
> 
> OK, so when you use lower case pending/active you are talking about the
> LR state. But when you use upper case GIC_IRQ_GUEST_PENDING this is a
> completely separate state related to the queueing of interrupts within
> Xen itself?
> 
> Can we s/GIC_IRQ_GUEST_PENDING/GIC_IRQ_GUEST_QUEUED/ perhaps? The
> comment would be something like:
>      * GIC_IRQ_GUEST_QUEUED: the irq is asserted and queued for
>        injection into the guests LRs.

I realize now that the current naming scheme could be confusing.  When I
talk about pending, active and GICH_LR_PENDING in this commit message, I
am talking about the irq status in the LR register. When I talk about
GIC_IRQ_GUEST_PENDING, I am referring to the GIC internal state tracker.

Because we don't actually know when the guest irq goes from pending to
active, we are clearing GIC_IRQ_GUEST_PENDING as soon as we add an irq
to an LR, as stated in the comment in xen/include/asm-arm/domain.h.
However as a result GIC_IRQ_GUEST_PENDING is more like
"GIC_IRQ_GUEST_QUEUED" rather than "GIC_IRQ_GUEST_PENDING", like you
wrote.

I'll add a patch to do the renaming as suggested.
Ian Campbell April 7, 2014, 8:48 a.m. UTC | #5
On Sun, 2014-04-06 at 17:58 +0100, Stefano Stabellini wrote:

> I realize now that the current naming scheme could be confusing.  When I
> talk about pending, active and GICH_LR_PENDING in this commit message, I
> am talking about the irq status in the LR register. When I talk about
> GIC_IRQ_GUEST_PENDING, I am referring to the GIC internal state tracker.
> 
> Because we don't actually know when the guest irq goes from pending to
> active, we are clearing GIC_IRQ_GUEST_PENDING as soon as we add an irq
> to an LR, as stated in the comment in xen/include/asm-arm/domain.h.
> However as a result GIC_IRQ_GUEST_PENDING is more like
> "GIC_IRQ_GUEST_QUEUED" rather than "GIC_IRQ_GUEST_PENDING", like you
> wrote.
> 
> I'll add a patch to do the renaming as suggested.

Thanks, in that case I guess I won't need the cold towel on my forehead
when I think about this stuff ;-)

Ian.
diff mbox

Patch

diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
index 0ebfbf0..77bdfe7 100644
--- a/xen/arch/arm/gic.c
+++ b/xen/arch/arm/gic.c
@@ -680,6 +680,14 @@  void gic_raise_guest_irq(struct vcpu *v, unsigned int virtual_irq,
 {
     int i;
     unsigned long flags;
+    struct pending_irq *n = irq_to_pending(v, virtual_irq);
+
+    if ( test_bit(GIC_IRQ_GUEST_VISIBLE, &n->status))
+    {
+        if ( v == current )
+            gic_clear_one_lr(v, n->lr);
+        return;
+    }
 
     spin_lock_irqsave(&gic.lock, flags);
 
@@ -705,21 +713,28 @@  static void gic_clear_one_lr(struct vcpu *v, int i)
     struct pending_irq *p;
     uint32_t lr;
     int irq;
-    bool_t inflight;
 
     ASSERT(!local_irq_is_enabled());
     ASSERT(spin_is_locked(&v->arch.vgic.lock));
 
     lr = GICH[GICH_LR + i];
-    if ( !(lr & (GICH_LR_PENDING|GICH_LR_ACTIVE)) )
+    irq = (lr >> GICH_LR_VIRTUAL_SHIFT) & GICH_LR_VIRTUAL_MASK;
+    p = irq_to_pending(v, irq);
+    if ( lr & GICH_LR_ACTIVE )
     {
-        inflight = 0;
+        /* HW interrupts cannot be ACTIVE and PENDING */
+        if ( p->desc == NULL &&
+             test_bit(GIC_IRQ_GUEST_ENABLED, &p->status) &&
+             test_and_clear_bit(GIC_IRQ_GUEST_PENDING, &p->status) )
+            GICH[GICH_LR + i] = lr | GICH_LR_PENDING;
+    } else if ( lr & GICH_LR_PENDING ) {
+        clear_bit(GIC_IRQ_GUEST_PENDING, &p->status);
+    } else {
+        spin_lock(&gic.lock);
+
         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);
@@ -727,16 +742,11 @@  static void gic_clear_one_lr(struct vcpu *v, int i)
         if ( test_bit(GIC_IRQ_GUEST_PENDING, &p->status) &&
                 test_bit(GIC_IRQ_GUEST_ENABLED, &p->status))
         {
-            inflight = 1;
             gic_raise_guest_irq(v, irq, p->priority);
-        }
-        spin_unlock(&gic.lock);
-        if ( !inflight )
-        {
-            spin_lock(&v->arch.vgic.lock);
+        } else
             list_del_init(&p->inflight);
-            spin_unlock(&v->arch.vgic.lock);
-        }
+
+        spin_unlock(&gic.lock);
     }
 }
 
@@ -796,9 +806,6 @@  int gic_events_need_delivery(void)
 
 void gic_inject(void)
 {
-    if ( vcpu_info(current, evtchn_upcall_pending) )
-        vgic_vcpu_inject_irq(current, current->domain->arch.evtchn_irq);
-
     gic_restore_pending_irqs(current);
 
     if ( !list_empty(&current->arch.vgic.lr_pending) && lr_all_full() )
diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
index 3913cf5..dc3a75f 100644
--- a/xen/arch/arm/vgic.c
+++ b/xen/arch/arm/vgic.c
@@ -389,7 +389,11 @@  static void vgic_enable_irqs(struct vcpu *v, uint32_t r, int n)
         irq = i + (32 * n);
         p = irq_to_pending(v, irq);
         set_bit(GIC_IRQ_GUEST_ENABLED, &p->status);
-        if ( !list_empty(&p->inflight) && !test_bit(GIC_IRQ_GUEST_VISIBLE, &p->status) )
+        if ( irq == v->domain->arch.evtchn_irq &&
+             vcpu_info(current, evtchn_upcall_pending) &&
+             list_empty(&p->inflight) )
+            vgic_vcpu_inject_irq(v, irq);
+        else if ( !list_empty(&p->inflight) && !test_bit(GIC_IRQ_GUEST_VISIBLE, &p->status) )
             gic_raise_guest_irq(v, irq, p->priority);
         if ( p->desc != NULL )
             p->desc->handler->enable(p->desc);
@@ -696,14 +700,6 @@  void vgic_vcpu_inject_irq(struct vcpu *v, unsigned int irq)
 
     spin_lock_irqsave(&v->arch.vgic.lock, flags);
 
-    if ( !list_empty(&n->inflight) )
-    {
-        if ( (irq != current->domain->arch.evtchn_irq) ||
-             (!test_bit(GIC_IRQ_GUEST_VISIBLE, &n->status)) )
-            set_bit(GIC_IRQ_GUEST_PENDING, &n->status);
-        goto out;
-    }
-
     /* vcpu offline */
     if ( test_bit(_VPF_down, &v->pause_flags) )
     {
@@ -715,21 +711,26 @@  void vgic_vcpu_inject_irq(struct vcpu *v, unsigned int irq)
 
     n->irq = irq;
     set_bit(GIC_IRQ_GUEST_PENDING, &n->status);
-    n->priority = priority;
 
     /* the irq is enabled */
     if ( test_bit(GIC_IRQ_GUEST_ENABLED, &n->status) )
         gic_raise_guest_irq(v, irq, priority);
 
-    list_for_each_entry ( iter, &v->arch.vgic.inflight_irqs, inflight )
+    if ( list_empty(&n->inflight) )
     {
-        if ( iter->priority > priority )
+        n->priority = priority;
+        list_for_each_entry ( iter, &v->arch.vgic.inflight_irqs, inflight )
         {
-            list_add_tail(&n->inflight, &iter->inflight);
-            goto out;
+            if ( iter->priority > priority )
+            {
+                list_add_tail(&n->inflight, &iter->inflight);
+                goto out;
+            }
         }
-    }
-    list_add_tail(&n->inflight, &v->arch.vgic.inflight_irqs);
+        list_add_tail(&n->inflight, &v->arch.vgic.inflight_irqs);
+    } else if ( n->priority != priority )
+        gdprintk(XENLOG_WARNING, "Changing priority of an inflight interrupt is not supported");
+
 out:
     spin_unlock_irqrestore(&v->arch.vgic.lock, flags);
     /* we have a new higher priority irq, inject it into the guest */