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

[Xen-devel] [PATCH] xen/arm: implement GICD_ICACTIVER read/write



Implement GICD_ICACTIVER and GICD_ISACTIVER reads by looking for the
GIC_IRQ_GUEST_ACTIVE bit in the relevant struct pending_irq. However
given that the pending to active transaction for irqs in LRs in done in
hardware, the GIC_IRQ_GUEST_ACTIVE bit might be out of date. We'll have
to live with that.

Implement GICD_ICACTIVER writes by checking the state of the irq in our
queues: if the irq is present in an LR, remove the hardware ACTIVE bit.
If the irq is present in an LR of another vcpu, send an IPI. Set the
GIC_IRQ_GUEST_DEACTIVATE bit to tell the receiving vcpu that the active
bit needs to be deactivated.

Signed-off-by: Stefano Stabellini <stefano.stabellini@xxxxxxxxxxxxx>
---
 xen/arch/arm/gic.c         |   40 +++++++++++++++++++++++++++++++++++++++
 xen/arch/arm/vgic-v2.c     |   45 ++++++++++++++++++++++++++++++++++++++------
 xen/arch/arm/vgic-v3.c     |   44 ++++++++++++++++++++++++++++++++++++++-----
 xen/include/asm-arm/gic.h  |    1 +
 xen/include/asm-arm/vgic.h |    4 ++++
 5 files changed, 123 insertions(+), 11 deletions(-)

diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
index 1e1e5ba..75c1f52 100644
--- a/xen/arch/arm/gic.c
+++ b/xen/arch/arm/gic.c
@@ -414,6 +414,15 @@ static void gic_update_one_lr(struct vcpu *v, int i)
     gic_hw_ops->read_lr(i, &lr_val);
     irq = lr_val.virq;
     p = irq_to_pending(v, irq);
+
+    if ( test_and_clear_bit(GIC_IRQ_GUEST_DEACTIVATE, &p->status) &&
+         (lr_val.state & GICH_LR_ACTIVE) )
+    {
+        clear_bit(GIC_IRQ_GUEST_ACTIVE, &p->status);
+        lr_val.state &= ~GICH_LR_ACTIVE;
+        gic_hw_ops->write_lr(i, &lr_val);
+    }
+
     if ( lr_val.state & GICH_LR_ACTIVE )
     {
         set_bit(GIC_IRQ_GUEST_ACTIVE, &p->status);
@@ -489,6 +498,37 @@ void gic_clear_lrs(struct vcpu *v)
     spin_unlock_irqrestore(&v->arch.vgic.lock, flags);
 }
 
+/* called with rank lock held */
+void gic_deactivate_irq(struct vcpu *v, unsigned int irq)
+{
+    unsigned long flags;
+    struct pending_irq *p;
+    struct vcpu *v_target = v->domain->arch.vgic.handler->get_target_vcpu(v, 
irq);
+
+    spin_lock_irqsave(&v_target->arch.vgic.lock, flags);
+
+    p = irq_to_pending(v_target, irq);
+    /* the interrupt is not even in an LR */
+    if ( list_empty(&p->inflight) || !list_empty(&p->lr_queue) )
+    {
+        spin_unlock_irqrestore(&v_target->arch.vgic.lock, flags);
+        return;
+    }
+
+    /* it is in an LR, let's check */
+    set_bit(GIC_IRQ_GUEST_DEACTIVATE, &p->status);
+    if ( v_target == current )
+    {
+        gic_update_one_lr(v_target, p->lr);
+        spin_unlock_irqrestore(&v_target->arch.vgic.lock, flags);
+    } else {
+        spin_unlock_irqrestore(&v_target->arch.vgic.lock, flags);
+        vcpu_unblock(v_target);
+        if (v_target->is_running )
+            smp_send_event_check_mask(cpumask_of(v_target->processor));
+    }
+}
+
 static void gic_restore_pending_irqs(struct vcpu *v)
 {
     int lr = 0;
diff --git a/xen/arch/arm/vgic-v2.c b/xen/arch/arm/vgic-v2.c
index f7d784b..9042062 100644
--- a/xen/arch/arm/vgic-v2.c
+++ b/xen/arch/arm/vgic-v2.c
@@ -126,8 +126,31 @@ static int vgic_v2_distr_mmio_read(struct vcpu *v, 
mmio_info_t *info,
     /* Read the active status of an IRQ via GICD is not supported */
     case GICD_ISACTIVER ... GICD_ISACTIVERN:
     case GICD_ICACTIVER ... GICD_ICACTIVERN:
-        goto read_as_zero;
-
+    {
+        unsigned int i = 0, irq = 0;
+        struct pending_irq *p;
+        if ( dabt.size != DABT_WORD ) goto bad_width;
+        rank = vgic_rank_offset(v, 1, gicd_reg - GICD_ICACTIVER, DABT_WORD);
+        if ( rank == NULL) goto read_as_zero;
+        vgic_lock_rank(v, rank, flags);
+        *r = 0;
+        irq = (gicd_reg - GICD_ICACTIVER) << 3;
+        for (i = 0; i < 32; i++)
+        {
+            p = irq_to_pending(v, i + irq);
+            /*
+             * This information is likely out of date because we don't
+             * actually know which interrupts have become ACTIVE from
+             * PENDING in the LRs of other processors at it happens
+             * transparently in hardware.  We would have to interrupt
+             * all other running vcpus to get an accurate snapshot.
+             * Let's not do that.
+             */
+            *r |= test_bit(GIC_IRQ_GUEST_ACTIVE, &p->status) ? (1 << i) : 0;
+        }
+        vgic_unlock_rank(v, rank, flags);
+        return 1;
+    }
     case GICD_ITARGETSR ... GICD_ITARGETSRN:
         if ( dabt.size != DABT_BYTE && dabt.size != DABT_WORD ) goto bad_width;
         rank = vgic_rank_offset(v, 8, gicd_reg - GICD_ITARGETSR, DABT_WORD);
@@ -332,11 +355,21 @@ static int vgic_v2_distr_mmio_write(struct vcpu *v, 
mmio_info_t *info,
         return 0;
 
     case GICD_ICACTIVER ... GICD_ICACTIVERN:
+    {
+        unsigned int i = 0, irq;
         if ( dabt.size != DABT_WORD ) goto bad_width;
-        printk(XENLOG_G_ERR
-               "%pv: vGICD: unhandled word write %#"PRIregister" to 
ICACTIVER%d\n",
-               v, r, gicd_reg - GICD_ICACTIVER);
-        return 0;
+        rank = vgic_rank_offset(v, 1, gicd_reg - GICD_ICACTIVER, DABT_WORD);
+        if ( rank == NULL) goto write_ignore;
+        vgic_lock_rank(v, rank, flags);
+        irq = (gicd_reg - GICD_ICACTIVER) << 3;
+        while ( (i = find_next_bit(&r, 32, i)) < 32 )
+        {
+            gic_deactivate_irq(v, i + irq);
+            i++;
+        }
+        vgic_unlock_rank(v, rank, flags);
+        return 1;
+    }
 
     case GICD_ITARGETSR ... GICD_ITARGETSR + 7:
         /* SGI/PPI target is read only */
diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
index b5249ff..c779f75 100644
--- a/xen/arch/arm/vgic-v3.c
+++ b/xen/arch/arm/vgic-v3.c
@@ -325,7 +325,31 @@ static int __vgic_v3_distr_common_mmio_read(const char 
*name, struct vcpu *v,
     /* Read the active status of an IRQ via GICD/GICR is not supported */
     case GICD_ISACTIVER ... GICD_ISACTIVERN:
     case GICD_ICACTIVER ... GICD_ICACTIVERN:
-        goto read_as_zero;
+    {
+        unsigned int i = 0, irq = 0;
+        struct pending_irq *p;
+        if ( dabt.size != DABT_WORD ) goto bad_width;
+        rank = vgic_rank_offset(v, 1, reg - GICD_ICACTIVER, DABT_WORD);
+        if ( rank == NULL) goto read_as_zero;
+        vgic_lock_rank(v, rank, flags);
+        *r = 0;
+        irq = (reg - GICD_ICACTIVER) << 3;
+        for (i = 0; i < 32; i++)
+        {
+            p = irq_to_pending(v, i + irq);
+            /*
+             * This information is likely out of date because we don't
+             * actually know which interrupts have become ACTIVE from
+             * PENDING in the LRs of other processors at it happens
+             * transparently in hardware.  We would have to interrupt
+             * all other running vcpus to get an accurate snapshot.
+             * Let's not do that.
+             */
+            *r |= test_bit(GIC_IRQ_GUEST_ACTIVE, &p->status) ? (1 << i) : 0;
+        }
+        vgic_unlock_rank(v, rank, flags);
+        return 1;
+    }
 
     case GICD_IPRIORITYR ... GICD_IPRIORITYRN:
         if ( dabt.size != DABT_BYTE && dabt.size != DABT_WORD ) goto bad_width;
@@ -421,11 +445,21 @@ static int __vgic_v3_distr_common_mmio_write(const char 
*name, struct vcpu *v,
         return 0;
 
     case GICD_ICACTIVER ... GICD_ICACTIVERN:
+    {
+        unsigned int i = 0, irq;
         if ( dabt.size != DABT_WORD ) goto bad_width;
-        printk(XENLOG_G_ERR
-               "%pv: %s: unhandled word write %#"PRIregister" to 
ICACTIVER%d\n",
-               v, name, r, reg - GICD_ICACTIVER);
-        return 0;
+        rank = vgic_rank_offset(v, 1, reg - GICD_ICACTIVER, DABT_WORD);
+        if ( rank == NULL) goto write_ignore;
+        vgic_lock_rank(v, rank, flags);
+        irq = (reg - GICD_ICACTIVER) << 3;
+        while ( (i = find_next_bit(&r, 32, i)) < 32 )
+        {
+            gic_deactivate_irq(v, i + irq);
+            i++;
+        }
+        vgic_unlock_rank(v, rank, flags);
+        return 1;
+    }
 
     case GICD_IPRIORITYR ... GICD_IPRIORITYRN:
         if ( dabt.size != DABT_BYTE && dabt.size != DABT_WORD ) goto bad_width;
diff --git a/xen/include/asm-arm/gic.h b/xen/include/asm-arm/gic.h
index 0116481..0061368 100644
--- a/xen/include/asm-arm/gic.h
+++ b/xen/include/asm-arm/gic.h
@@ -287,6 +287,7 @@ extern unsigned int gic_number_lines(void);
 int gic_irq_xlate(const u32 *intspec, unsigned int intsize,
                   unsigned int *out_hwirq, unsigned int *out_type);
 void gic_clear_lrs(struct vcpu *v);
+void gic_deactivate_irq(struct vcpu *v, unsigned int irq);
 
 struct gic_info {
     /* GIC version */
diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h
index cb51a9e..9845c13 100644
--- a/xen/include/asm-arm/vgic.h
+++ b/xen/include/asm-arm/vgic.h
@@ -59,12 +59,16 @@ struct pending_irq
      * vcpu while it is still inflight and on an GICH_LR register on the
      * old vcpu.
      *
+     * GIC_IRQ_GUEST_DEACTIVATE: an explicit deactivation request has
+     * been made by the guest (for example writing to GICD_ICACTIVER).
+     *
      */
 #define GIC_IRQ_GUEST_QUEUED   0
 #define GIC_IRQ_GUEST_ACTIVE   1
 #define GIC_IRQ_GUEST_VISIBLE  2
 #define GIC_IRQ_GUEST_ENABLED  3
 #define GIC_IRQ_GUEST_MIGRATING   4
+#define GIC_IRQ_GUEST_DEACTIVATE  5
     unsigned long status;
     struct irq_desc *desc; /* only set it the irq corresponds to a physical 
irq */
     unsigned int irq;
-- 
1.7.10.4


_______________________________________________
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®.