diff mbox series

[XEN,v3,09/11] xen: arm: gic: supporting routing a PPI to the current vcpu.

Message ID 20191115201037.44982-5-stewart.hildebrand@dornerworks.com (mailing list archive)
State New, archived
Headers show
Series xen: arm: context switch vtimer PPI state | expand

Commit Message

Stewart Hildebrand Nov. 15, 2019, 8:10 p.m. UTC
From: Ian Campbell <ian.campbell@citrix.com>

That is whichever vcpu is resident when the interrupt fires. An
interrupt is in this state when both IRQ_GUEST and IRQ_PER_CPU are set
in the descriptor status. Only PPIs can be in this mode.

This requires some peripheral specific code to make use of the
previously introduced functionality to save and restore the PPI state.
The vtimer driver will do so shortly.

Signed-off-by: Ian Campbell <ian.campbell@citrix.com>
Signed-off-by: Stewart Hildebrand <stewart.hildebrand@dornerworks.com>

---
v3:
  * Change calls to gic_set_irq_properties() to gic_set_irq_type() and
    gic_set_irq_priority() due to following commits:
    16580cde5a xen/arm: gic: Do not configure affinity during routing
    23e8118b8e xen/arm: gic: split set_irq_properties
  * Partially address feedback from v2 [1]:
  * Clarify a comment.
  * Switch loglevel back to XENLOG_G_ERR and bump a parameter to the
    next line to comply with line length coding style.
  * Call vgic_get_hw_irq_desc from gic_save_and_mask_hwppi
  * Call vgic_connect_hw_irq from gic_restore_hwppi

---
Note: I have not yet addressed feedback from [1] regarding
differentiating between CPU0/CPU1 in the error message.

I also have not yet given much thought to Julien's comment in [1] "Why
do you set the parameter virq to irq?"

I hope to investigate further if time allows, but if anyone has any
input I'd like to hear it.

[1] https://lists.xenproject.org/archives/html/xen-devel/2015-11/msg01064.html
---
 xen/arch/arm/gic.c        | 33 ++++++++++++++--
 xen/arch/arm/irq.c        | 80 +++++++++++++++++++++++++++++++++++----
 xen/include/asm-arm/gic.h |  2 +
 xen/include/asm-arm/irq.h |  1 +
 4 files changed, 104 insertions(+), 12 deletions(-)

Comments

Stewart Hildebrand Nov. 17, 2019, 11:11 p.m. UTC | #1
On Friday, November 15, 2019 3:11 PM, Stewart Hildebrand wrote:
>diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
>index 75921724dd..982afaadbd 100644
>--- a/xen/arch/arm/gic.c
>+++ b/xen/arch/arm/gic.c
>@@ -92,8 +92,7 @@ void gic_save_state(struct vcpu *v)
> void gic_save_and_mask_hwppi(struct vcpu *v, const unsigned virq,
>                              struct hwppi_state *s)
> {
>-    struct pending_irq *p = irq_to_pending(v, virq);
>-    struct irq_desc *desc = p->desc;
>+    struct irq_desc *desc = vgic_get_hw_irq_desc(NULL, v, virq);
>
>     spin_lock(&desc->lock);
>
>@@ -123,7 +122,6 @@ void gic_restore_hwppi(struct vcpu *v,
>                        const unsigned virq,
>                        const struct hwppi_state *s)
> {
>-    struct pending_irq *p = irq_to_pending(v, virq);
>     struct irq_desc *desc = irq_to_desc(s->irq);
>
>     spin_lock(&desc->lock);
>@@ -131,7 +129,8 @@ void gic_restore_hwppi(struct vcpu *v,
>     ASSERT(virq >= 16 && virq < 32);
>     ASSERT(!is_idle_vcpu(v));
>
>-    p->desc = desc; /* Migrate to new physical processor */
>+    /* Migrate to new physical processor */
>+    vgic_connect_hw_irq(v->domain, v, virq, desc, true);
>
>     irq_set_virq(desc, virq);
>

This snippet was intended to be rolled into [XEN PATCH v3 05/11] xen:
arm: add interfaces to save/restore the state of a PPI.
diff mbox series

Patch

diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
index 75921724dd..982afaadbd 100644
--- a/xen/arch/arm/gic.c
+++ b/xen/arch/arm/gic.c
@@ -92,8 +92,7 @@  void gic_save_state(struct vcpu *v)
 void gic_save_and_mask_hwppi(struct vcpu *v, const unsigned virq,
                              struct hwppi_state *s)
 {
-    struct pending_irq *p = irq_to_pending(v, virq);
-    struct irq_desc *desc = p->desc;
+    struct irq_desc *desc = vgic_get_hw_irq_desc(NULL, v, virq);
 
     spin_lock(&desc->lock);
 
@@ -123,7 +122,6 @@  void gic_restore_hwppi(struct vcpu *v,
                        const unsigned virq,
                        const struct hwppi_state *s)
 {
-    struct pending_irq *p = irq_to_pending(v, virq);
     struct irq_desc *desc = irq_to_desc(s->irq);
 
     spin_lock(&desc->lock);
@@ -131,7 +129,8 @@  void gic_restore_hwppi(struct vcpu *v,
     ASSERT(virq >= 16 && virq < 32);
     ASSERT(!is_idle_vcpu(v));
 
-    p->desc = desc; /* Migrate to new physical processor */
+    /* Migrate to new physical processor */
+    vgic_connect_hw_irq(v->domain, v, virq, desc, true);
 
     irq_set_virq(desc, virq);
 
@@ -178,6 +177,32 @@  void gic_route_irq_to_xen(struct irq_desc *desc, unsigned int priority)
     gic_set_irq_priority(desc, priority);
 }
 
+/*
+ * Program the GIC to route an interrupt to the current guest.
+ *
+ * That is, the IRQ is delivered to whichever VCPU happens to be
+ * resident on the PCPU when the interrupt arrives.
+ *
+ * Currently the interrupt *must* be a PPI and the code responsible
+ * for the related hardware must save and restore the PPI with
+ * gic_save_and_mask_hwppi/gic_restore_hwppi.
+ */
+int gic_route_irq_to_current_guest(struct irq_desc *desc,
+                                   unsigned int priority)
+{
+    ASSERT(spin_is_locked(&desc->lock));
+    ASSERT(desc->irq >= 16 && desc->irq < 32);
+
+    desc->handler = gic_hw_ops->gic_guest_irq_type;
+    set_bit(_IRQ_GUEST, &desc->status);
+    set_bit(_IRQ_PER_CPU, &desc->status);
+
+    gic_set_irq_type(desc, desc->arch.type);
+    gic_set_irq_priority(desc, GIC_PRI_IRQ);
+
+    return 0;
+}
+
 /* Program the GIC to route an interrupt to a guest
  *   - desc.lock must be held
  */
diff --git a/xen/arch/arm/irq.c b/xen/arch/arm/irq.c
index 1a8e599c2e..17dec64203 100644
--- a/xen/arch/arm/irq.c
+++ b/xen/arch/arm/irq.c
@@ -236,6 +236,7 @@  void do_IRQ(struct cpu_user_regs *regs, unsigned int irq, int is_fiq)
     if ( test_bit(_IRQ_GUEST, &desc->status) )
     {
         struct irq_guest *info = irq_get_guest_info(desc);
+        struct vcpu *v;
 
         perfc_incr(guest_irqs);
         desc->handler->end(desc);
@@ -243,10 +244,15 @@  void do_IRQ(struct cpu_user_regs *regs, unsigned int irq, int is_fiq)
         set_bit(_IRQ_INPROGRESS, &desc->status);
 
         /*
-         * The irq cannot be a PPI, we only support delivery of SPIs to
-         * guests.
+         * A PPI exposed to a guest must always be in IRQ_GUEST|IRQ_PER_CPU
+         * mode ("route to active VCPU"), so we use current.
+         *
+         * For SPI, we use NULL. In this case, vgic_inject_irq() will look up
+         * the required target for delivery to a specific guest.
          */
-        vgic_inject_irq(info->d, NULL, info->virq, true);
+        v = test_bit(_IRQ_PER_CPU, &desc->status) ? current : NULL;
+        vgic_inject_irq(info->d, v, info->virq, true);
+
         goto out_no_end;
     }
 
@@ -362,11 +368,15 @@  int setup_irq(unsigned int irq, unsigned int irqflags, struct irqaction *new)
 
     if ( test_bit(_IRQ_GUEST, &desc->status) )
     {
-        struct domain *d = irq_get_domain(desc);
+        struct irq_guest *info = irq_get_guest_info(desc);
 
         spin_unlock_irqrestore(&desc->lock, flags);
-        printk(XENLOG_ERR "ERROR: IRQ %u is already in use by the domain %u\n",
-               irq, d->domain_id);
+        if ( !test_bit(_IRQ_PER_CPU, &desc->status) )
+            printk(XENLOG_ERR "ERROR: IRQ %u is already in use by domain %u\n",
+                   irq, info->d->domain_id);
+        else
+            printk(XENLOG_ERR
+                   "ERROR: IRQ %u is already in use by <current-vcpu>\n", irq);
         return -EBUSY;
     }
 
@@ -450,8 +460,14 @@  static int setup_guest_irq(struct irq_desc *desc, unsigned int virq,
 
             if ( d != ad )
             {
-                printk(XENLOG_G_ERR "IRQ %u is already used by domain %u\n",
-                       irq, ad->domain_id);
+                if ( !test_bit(_IRQ_PER_CPU, &desc->status) )
+                    printk(XENLOG_G_ERR
+                           "ERROR: IRQ %u is already used by domain %u\n",
+                           irq, ad->domain_id);
+                else
+                    printk(XENLOG_G_ERR
+                           "ERROR: IRQ %u is already used by <current-vcpu>\n",
+                           irq);
                 retval = -EBUSY;
             }
             else if ( irq_get_guest_info(desc)->virq != virq )
@@ -552,6 +568,54 @@  free_info:
     return retval;
 }
 
+/*
+ * Route a PPI such that it is always delivered to the current vcpu on
+ * the pcpu. The driver for the peripheral must use
+ * gic_{save_and_mask,restore}_hwppi as part of the context switch.
+ */
+int route_hwppi_to_current_vcpu(unsigned int irq, const char *devname)
+{
+    struct irq_guest *info;
+    struct irq_desc *desc;
+    unsigned long flags;
+    int retval = 0;
+
+    /* Can only route PPIs to current VCPU */
+    if ( irq < 16 || irq >= 32 )
+        return -EINVAL;
+
+    desc = irq_to_desc(irq);
+
+    info = xmalloc(struct irq_guest);
+    if ( !info )
+        return -ENOMEM;
+
+    info->d = NULL; /* Routed to current vcpu, so no specific domain */
+    /* info->virq is set by gic_restore_hwppi. */
+
+    spin_lock_irqsave(&desc->lock, flags);
+
+    retval = setup_guest_irq(desc, irq, flags, info, devname);
+    if ( retval )
+    {
+        xfree(info);
+        return retval;
+    }
+
+    retval = gic_route_irq_to_current_guest(desc, GIC_PRI_IRQ);
+
+    spin_unlock_irqrestore(&desc->lock, flags);
+
+    if ( retval )
+    {
+        release_irq(desc->irq, info);
+        xfree(info);
+        return retval;
+    }
+
+    return 0;
+}
+
 int release_guest_irq(struct domain *d, unsigned int virq)
 {
     struct irq_desc *desc;
diff --git a/xen/include/asm-arm/gic.h b/xen/include/asm-arm/gic.h
index 1164e0c7a6..6a0910e13e 100644
--- a/xen/include/asm-arm/gic.h
+++ b/xen/include/asm-arm/gic.h
@@ -244,6 +244,8 @@  extern void gic_route_irq_to_xen(struct irq_desc *desc, unsigned int priority);
 extern int gic_route_irq_to_guest(struct domain *, unsigned int virq,
                                   struct irq_desc *desc,
                                   unsigned int priority);
+int gic_route_irq_to_current_guest(struct irq_desc *desc,
+                                   unsigned int priority);
 
 /* Remove an IRQ passthrough to a guest */
 int gic_remove_irq_from_guest(struct domain *d, unsigned int virq,
diff --git a/xen/include/asm-arm/irq.h b/xen/include/asm-arm/irq.h
index 367fe6269c..c51265180b 100644
--- a/xen/include/asm-arm/irq.h
+++ b/xen/include/asm-arm/irq.h
@@ -77,6 +77,7 @@  void init_secondary_IRQ(void);
 
 int route_irq_to_guest(struct domain *d, unsigned int virq,
                        unsigned int irq, const char *devname);
+int route_hwppi_to_current_vcpu(unsigned int irq, const char *devname);
 int release_guest_irq(struct domain *d, unsigned int irq);
 
 void arch_move_irqs(struct vcpu *v);