diff mbox

[Xen-devel,v9,12/12] xen/arm: gic_events_need_delivery and irq priorities

Message ID 1402409240-28114-12-git-send-email-stefano.stabellini@eu.citrix.com
State New
Headers show

Commit Message

Stefano Stabellini June 10, 2014, 2:07 p.m. UTC
Introduce GIC_IRQ_GUEST_ACTIVE to track which irqs are currently
active in the guest.

gic_events_need_delivery should only return positive if an outstanding
pending irq has an higher group priority than the currently active group
priotity and the priority mask.
Read GICH_APR to find the active group priority.
Read GICH_VMCR to find the priority mask.
Find the highest priority non-active enabled irq by going through the
inflight list.

In gic_restore_pending_irqs replace lower priority pending (and not
active) irqs in GICH_LRs with higher priority irqs if no more GICH_LRs
are available.

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

---

Changes in v9:
- gic_events_need_delivery: read GICH_APR to find the active group
priority;
- gic_events_need_delivery: simplify the search loop.

Changes in v8:
- in gic_restore_pending_irqs rename i to lr;
- add in code comments in gic_restore_pending_irqs and
gic_events_need_delivery;
- add << 3 to mask_priority.

Changes in v7:
- fix locking for the list_empty case in gic_restore_pending_irqs;
- add in code comment;
- gic_events_need_delivery: break out of the loop as soon as we find the
active irq as inflight_irqs is ordered by priority;
- gic_events_need_delivery: break out of the loop if p->priority is
lower than mask_priority as inflight_irqs is ordered by priority;
- use find_next_zero_bit instead of find_first_zero_bit;
- in gic_restore_pending_irqs remember the last position of the inner
loop search and continue from there;
- in gic_restore_pending_irqs use a priority check to get out of the
inner loop.

Changes in v5:
- improve in code comments;
- use list_for_each_entry_reverse instead of writing my own list walker.

Changes in v4:
- in gic_events_need_delivery go through inflight_irqs and only consider
enabled irqs.
---
 xen/arch/arm/gic.c           |   85 ++++++++++++++++++++++++++++++++++++++----
 xen/include/asm-arm/domain.h |    5 ++-
 xen/include/asm-arm/gic.h    |    3 ++
 3 files changed, 83 insertions(+), 10 deletions(-)

Comments

Ian Campbell June 18, 2014, 10:36 a.m. UTC | #1
On Tue, 2014-06-10 at 15:07 +0100, Stefano Stabellini wrote:
> Introduce GIC_IRQ_GUEST_ACTIVE to track which irqs are currently
> active in the guest.
> 
> gic_events_need_delivery should only return positive if an outstanding
> pending irq has an higher group priority than the currently active group
> priotity and the priority mask.
> Read GICH_APR to find the active group priority.
> Read GICH_VMCR to find the priority mask.
> Find the highest priority non-active enabled irq by going through the
> inflight list.
> 
> In gic_restore_pending_irqs replace lower priority pending (and not
> active) irqs in GICH_LRs with higher priority irqs if no more GICH_LRs
> are available.
> 
> Signed-off-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
> 
> ---
> 
> Changes in v9:
> - gic_events_need_delivery: read GICH_APR to find the active group
> priority;
> - gic_events_need_delivery: simplify the search loop.

Thanks, I can actually grok this version ;-)

Acked this + #9.

AIUI there is no relationship here with "xen/arm: make accesses to
desc->status flags atomic" which fixes an issue which already present
before this series.

There applied (this series, not the atomic flags one, yet). thanks.

Ian.
Ian Campbell June 18, 2014, 10:37 a.m. UTC | #2
On Tue, 2014-06-10 at 15:07 +0100, Stefano Stabellini wrote:
> Introduce GIC_IRQ_GUEST_ACTIVE to track which irqs are currently
> active in the guest.
> 
> gic_events_need_delivery should only return positive if an outstanding
> pending irq has an higher group priority than the currently active group
> priotity and the priority mask.
> Read GICH_APR to find the active group priority.
> Read GICH_VMCR to find the priority mask.
> Find the highest priority non-active enabled irq by going through the
> inflight list.
> 
> In gic_restore_pending_irqs replace lower priority pending (and not
> active) irqs in GICH_LRs with higher priority irqs if no more GICH_LRs
> are available.
> 
> Signed-off-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
> 
> ---
> 
> Changes in v9:
> - gic_events_need_delivery: read GICH_APR to find the active group
> priority;
> - gic_events_need_delivery: simplify the search loop.

Thanks, I can actually grok this version ;-)

Acked this + #9.

AIUI there is no relationship here with "xen/arm: make accesses to
desc->status flags atomic" which fixes an issue which already present
before this series.

There applied (this series, not the atomic flags one, yet). thanks.

Ian.
Julien Grall June 18, 2014, 11:47 a.m. UTC | #3
On 06/18/2014 11:36 AM, Ian Campbell wrote:
> AIUI there is no relationship here with "xen/arm: make accesses to
> desc->status flags atomic" which fixes an issue which already present
> before this series.

The error was not really there before. We were "safe" because the flags
were cleared before EOIing the IRQ.

Here, we are clearing after EOIing (it has been made by the guest). So
this race condition can happen more often.

Regards,
Stefano Stabellini June 18, 2014, 12:51 p.m. UTC | #4
On Wed, 18 Jun 2014, Julien Grall wrote:
> On 06/18/2014 11:36 AM, Ian Campbell wrote:
> > AIUI there is no relationship here with "xen/arm: make accesses to
> > desc->status flags atomic" which fixes an issue which already present
> > before this series.
> 
> The error was not really there before. We were "safe" because the flags
> were cleared before EOIing the IRQ.
> 
> Here, we are clearing after EOIing (it has been made by the guest). So
> this race condition can happen more often.

However considering that when IRQ_GUEST is set, IRQ_INPROGRESS is only
read by irq.c, I think the window for errors is extremely small.

That doesn't mean we should expose ourselves to risks, so I still
recommend we apply "make accesses to desc->status flags atomic".
Julien Grall June 18, 2014, 12:59 p.m. UTC | #5
On 06/18/2014 01:51 PM, Stefano Stabellini wrote:
> On Wed, 18 Jun 2014, Julien Grall wrote:
>> On 06/18/2014 11:36 AM, Ian Campbell wrote:
>>> AIUI there is no relationship here with "xen/arm: make accesses to
>>> desc->status flags atomic" which fixes an issue which already present
>>> before this series.
>>
>> The error was not really there before. We were "safe" because the flags
>> were cleared before EOIing the IRQ.
>>
>> Here, we are clearing after EOIing (it has been made by the guest). So
>> this race condition can happen more often.
> 
> However considering that when IRQ_GUEST is set, IRQ_INPROGRESS is only
> read by irq.c, I think the window for errors is extremely small.
> 
> That doesn't mean we should expose ourselves to risks, so I still
> recommend we apply "make accesses to desc->status flags atomic".

FYI, with my upcoming device passthrough series, I rely of
IRQ_INPROGRESS in release_irq to know if we need to EOI an IRQ routed to
the guest.

So this series is definitely necessary, otherwise we loose the interrupt
for ever.

Regards,
Ian Campbell June 18, 2014, 1:17 p.m. UTC | #6
On Wed, 2014-06-18 at 13:51 +0100, Stefano Stabellini wrote:
> On Wed, 18 Jun 2014, Julien Grall wrote:
> > On 06/18/2014 11:36 AM, Ian Campbell wrote:
> > > AIUI there is no relationship here with "xen/arm: make accesses to
> > > desc->status flags atomic" which fixes an issue which already present
> > > before this series.
> > 
> > The error was not really there before. We were "safe" because the flags
> > were cleared before EOIing the IRQ.
> > 
> > Here, we are clearing after EOIing (it has been made by the guest). So
> > this race condition can happen more often.
> 
> However considering that when IRQ_GUEST is set, IRQ_INPROGRESS is only
> read by irq.c, I think the window for errors is extremely small.
> 
> That doesn't mean we should expose ourselves to risks, so I still
> recommend we apply "make accesses to desc->status flags atomic".

Agreed, I'll see how sufficiently acked it is when I get that far
through my queue.

Ian.
diff mbox

Patch

diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
index 3728182..5e502df 100644
--- a/xen/arch/arm/gic.c
+++ b/xen/arch/arm/gic.c
@@ -650,6 +650,7 @@  static void gic_update_one_lr(struct vcpu *v, int i)
     p = irq_to_pending(v, irq);
     if ( lr & GICH_LR_ACTIVE )
     {
+        set_bit(GIC_IRQ_GUEST_ACTIVE, &p->status);
         if ( test_bit(GIC_IRQ_GUEST_ENABLED, &p->status) &&
              test_and_clear_bit(GIC_IRQ_GUEST_QUEUED, &p->status) )
         {
@@ -673,6 +674,7 @@  static void gic_update_one_lr(struct vcpu *v, int i)
         if ( p->desc != NULL )
             p->desc->status &= ~IRQ_INPROGRESS;
         clear_bit(GIC_IRQ_GUEST_VISIBLE, &p->status);
+        clear_bit(GIC_IRQ_GUEST_ACTIVE, &p->status);
         p->lr = GIC_INVALID_LR;
         if ( test_bit(GIC_IRQ_GUEST_ENABLED, &p->status) &&
              test_bit(GIC_IRQ_GUEST_QUEUED, &p->status) )
@@ -706,20 +708,55 @@  void gic_clear_lrs(struct vcpu *v)
 
 static void gic_restore_pending_irqs(struct vcpu *v)
 {
-    int i;
-    struct pending_irq *p, *t;
+    int lr = 0, lrs = nr_lrs;
+    struct pending_irq *p, *t, *p_r;
+    struct list_head *inflight_r;
     unsigned long flags;
 
     spin_lock_irqsave(&v->arch.vgic.lock, flags);
+
+    if ( list_empty(&v->arch.vgic.lr_pending) )
+        goto out;
+
+    inflight_r = &v->arch.vgic.inflight_irqs;
     list_for_each_entry_safe ( p, t, &v->arch.vgic.lr_pending, lr_queue )
     {
-        i = find_first_zero_bit(&this_cpu(lr_mask), nr_lrs);
-        if ( i >= nr_lrs ) return;
+        lr = find_next_zero_bit(&this_cpu(lr_mask), nr_lrs, lr);
+        if ( lr >= nr_lrs )
+        {
+            /* No more free LRs: find a lower priority irq to evict */
+            list_for_each_entry_reverse( p_r, inflight_r, inflight )
+            {
+                if ( p_r->priority == p->priority )
+                    goto out;
+                if ( test_bit(GIC_IRQ_GUEST_VISIBLE, &p_r->status) &&
+                     !test_bit(GIC_IRQ_GUEST_ACTIVE, &p_r->status) )
+                    goto found;
+            }
+            /* We didn't find a victim this time, and we won't next
+             * time, so quit */
+            goto out;
+
+found:
+            lr = p_r->lr;
+            p_r->lr = GIC_INVALID_LR;
+            set_bit(GIC_IRQ_GUEST_QUEUED, &p_r->status);
+            clear_bit(GIC_IRQ_GUEST_VISIBLE, &p_r->status);
+            gic_add_to_lr_pending(v, p_r);
+            inflight_r = &p_r->inflight; 
+        }
 
-        gic_set_lr(i, p, GICH_LR_PENDING);
+        gic_set_lr(lr, p, GICH_LR_PENDING);
         list_del_init(&p->lr_queue);
-        set_bit(i, &this_cpu(lr_mask));
+        set_bit(lr, &this_cpu(lr_mask));
+
+        /* We can only evict nr_lrs entries */
+        lrs--;
+        if ( lrs == 0 )
+            break;
     }
+
+out:
     spin_unlock_irqrestore(&v->arch.vgic.lock, flags);
 }
 
@@ -736,8 +773,40 @@  void gic_clear_pending_irqs(struct vcpu *v)
 
 int gic_events_need_delivery(void)
 {
-    return (!list_empty(&current->arch.vgic.lr_pending) ||
-            this_cpu(lr_mask));
+    struct vcpu *v = current;
+    struct pending_irq *p;
+    unsigned long flags;
+    const unsigned long apr = GICH[GICH_APR];
+    int mask_priority;
+    int active_priority;
+    int rc = 0;
+
+    mask_priority = (GICH[GICH_VMCR] >> GICH_VMCR_PRIORITY_SHIFT) & GICH_VMCR_PRIORITY_MASK;
+    active_priority = find_next_bit(&apr, 32, 0);
+
+    spin_lock_irqsave(&v->arch.vgic.lock, flags);
+
+    /* TODO: We order the guest irqs by priority, but we don't change
+     * the priority of host irqs. */
+
+    /* find the first enabled non-active irq, the queue is already
+     * ordered by priority */
+    list_for_each_entry( p, &v->arch.vgic.inflight_irqs, inflight )
+    {
+        if ( GIC_PRI_TO_GUEST(p->priority) >= mask_priority )
+            goto out;
+        if ( GIC_PRI_TO_GUEST(p->priority) >= active_priority )
+            goto out;
+        if ( test_bit(GIC_IRQ_GUEST_ENABLED, &p->status) )
+        {
+            rc = 1;
+            goto out;
+        }
+    }
+
+out:
+    spin_unlock_irqrestore(&v->arch.vgic.lock, flags);
+    return rc;
 }
 
 void gic_inject(void)
diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h
index 59ce196..d689675 100644
--- a/xen/include/asm-arm/domain.h
+++ b/xen/include/asm-arm/domain.h
@@ -56,8 +56,9 @@  struct pending_irq
      *
      */
 #define GIC_IRQ_GUEST_QUEUED   0
-#define GIC_IRQ_GUEST_VISIBLE  1
-#define GIC_IRQ_GUEST_ENABLED  2
+#define GIC_IRQ_GUEST_ACTIVE   1
+#define GIC_IRQ_GUEST_VISIBLE  2
+#define GIC_IRQ_GUEST_ENABLED  3
     unsigned long status;
     struct irq_desc *desc; /* only set it the irq corresponds to a physical irq */
     int irq;
diff --git a/xen/include/asm-arm/gic.h b/xen/include/asm-arm/gic.h
index aede45c..bf6fb1e 100644
--- a/xen/include/asm-arm/gic.h
+++ b/xen/include/asm-arm/gic.h
@@ -129,6 +129,9 @@ 
 #define GICH_LR_CPUID_SHIFT     9
 #define GICH_VTR_NRLRGS         0x3f
 
+#define GICH_VMCR_PRIORITY_MASK   0x1f
+#define GICH_VMCR_PRIORITY_SHIFT  27
+
 /*
  * The minimum GICC_BPR is required to be in the range 0-3. We set
  * GICC_BPR to 0 but we must expect that it might be 3. This means we