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

[PATCH v4 08/42] xen/riscv: distinguish h/w and virtual vINTC sources



A vIRQ of a domain's vINTC is either a h/w source, one a physical IRQ is
routed to by route_irq_to_guest(), or a virtual source, whose line is
driven by the emulator of a device in Xen and whose state exists in the
vINTC only. used_irqs only tells that a vIRQ is taken, not which of the
two it is, whereas they have to be told apart: only a h/w source has a
physical IRQ to release when the domain goes away, and h/w registers to
keep in line with the guest's view.

Track the h/w sources in a new hw_irqs bitmap, the subset of used_irqs
a h/w IRQ is routed to. intc_route_irq_to_guest() sets the vIRQ's bit
there, so it now takes the domain and the vIRQ, and
domain_vintc_deinit() releases the h/w sources only.

An IRQ shared between devices is routed to the domain once per device,
so vintc_reserve_virq() legitimately fails with -EEXIST for all but the
first of them. map_irq_to_domain() now accepts -EEXIST only for a vIRQ
which is a h/w source already, so that a vIRQ reserved for a virtual
source can't be taken over by a h/w IRQ.

Signed-off-by: Oleksii Kurochko <oleksii.kurochko@xxxxxxxxx>
---
More context on why the two kinds of sources have to be told apart is
given by the patches relying on hw_irqs:
 - "xen/riscv: implement virtual APLIC MMIO emulation", the next one,
   which accesses the registers of a source in the h/w APLIC only for a
   h/w source;
 - "xen/riscv: introduce vaplic_reconfigure_target()", which retargets
   the h/w sources only;
 - the patches introducing virtual sources to vAPLIC:
   "xen/riscv: emulate vAPLIC sourcecfg[], setie[] and domaincfg.IE",
   "xen/riscv: emulate target[] of virtual vAPLIC sources",
   "xen/riscv: emulate the pending bits and MSIs of virtual vAPLIC
   sources" and "xen/riscv: drive the line of a virtual vAPLIC source".
---
Changes in v4:
 - New patch.
---
---
 xen/arch/riscv/device.c           | 17 ++++++++++++-----
 xen/arch/riscv/include/asm/intc.h |  5 ++++-
 xen/arch/riscv/intc.c             | 26 +++++++++++++++++++-------
 xen/arch/riscv/irq.c              |  2 +-
 4 files changed, 36 insertions(+), 14 deletions(-)

diff --git a/xen/arch/riscv/device.c b/xen/arch/riscv/device.c
index fc41c075c772..4c1e18b83c3a 100644
--- a/xen/arch/riscv/device.c
+++ b/xen/arch/riscv/device.c
@@ -25,14 +25,21 @@ int __overlay_init map_irq_to_domain(struct domain *d, 
unsigned int irq,
     {
         /*
          * -EEXIST merely means that the IRQ has already been reserved, which
-         * legitimately happens when the IRQ is shared between devices. Any
-         * other failure has to be fatal: the IRQ would otherwise be routed to
-         * the domain without domain_vintc_deinit() ever releasing it again.
+         * legitimately happens when the IRQ is shared between devices: the
+         * vIRQ is then a h/w source already, route_irq_to_guest() having
+         * connected it to the IRQ for the first of them. A vIRQ reserved for
+         * a virtual source, whose line an emulator in Xen drives, mustn't be
+         * taken over by a h/w IRQ, though. Any other failure has to be fatal
+         * too: the IRQ would otherwise be routed to the domain without
+         * domain_vintc_deinit() ever releasing it again.
          */
         res = vintc_reserve_virq(d, irq);
-        if ( res && (res != -EEXIST) )
+        if ( res == -EEXIST && test_bit(irq, d->arch.vintc->hw_irqs) )
+            res = 0;
+        if ( res )
         {
-            printk(XENLOG_ERR "Unable to reserve vIRQ %u for %pd\n", irq, d);
+            printk(XENLOG_ERR "Unable to reserve vIRQ %u for %pd: %d\n",
+                   irq, d, res);
             return res;
         }
 
diff --git a/xen/arch/riscv/include/asm/intc.h 
b/xen/arch/riscv/include/asm/intc.h
index 909317335769..7a31c6a298a0 100644
--- a/xen/arch/riscv/include/asm/intc.h
+++ b/xen/arch/riscv/include/asm/intc.h
@@ -69,6 +69,8 @@ struct vintc_ops {
 struct vintc {
     unsigned int nr_virqs;
     unsigned long *used_irqs;
+    /* Sources of used_irqs a h/w IRQ is routed to; the rest are virtual. */
+    unsigned long *hw_irqs;
     /* Callbacks invoked during domain construction only. */
     const struct vintc_init_ops *init_ops;
     /* Runtime callbacks used for the lifetime of the guest. */
@@ -82,7 +84,8 @@ void register_intc_ops(const struct intc_hw_init_ops 
*init_ops);
 void intc_init(void);
 
 void intc_route_irq_to_xen(struct irq_desc *desc, unsigned int priority);
-int intc_route_irq_to_guest(struct irq_desc *desc, unsigned int priority);
+int intc_route_irq_to_guest(const struct domain *d, unsigned int virq,
+                            struct irq_desc *desc, unsigned int priority);
 
 void intc_handle_external_irqs(struct cpu_user_regs *regs);
 
diff --git a/xen/arch/riscv/intc.c b/xen/arch/riscv/intc.c
index 810d126e263d..ac9c798ec36b 100644
--- a/xen/arch/riscv/intc.c
+++ b/xen/arch/riscv/intc.c
@@ -82,19 +82,25 @@ void intc_route_irq_to_xen(struct irq_desc *desc, unsigned 
int priority)
     intc_set_irq_priority(desc, priority);
 }
 
-int intc_route_irq_to_guest(struct irq_desc *desc,
-                            unsigned int priority)
+int intc_route_irq_to_guest(const struct domain *d, unsigned int virq,
+                            struct irq_desc *desc, unsigned int priority)
 {
     ASSERT(spin_is_locked(&desc->lock));
 
     ASSERT(intc_hw_ops->guest_irq_type);
 
+    /* The caller has reserved @virq, see vintc_reserve_virq(). */
+    ASSERT(virq < d->arch.vintc->nr_virqs);
+
     desc->handler = intc_hw_ops->guest_irq_type;
     desc->status |= IRQ_GUEST;
 
     intc_set_irq_type(desc, desc->arch.type);
     intc_set_irq_priority(desc, priority);
 
+    /* Connect @virq to the h/w IRQ: it isn't a virtual source. */
+    set_bit(virq, d->arch.vintc->hw_irqs);
+
     return 0;
 }
 
@@ -126,7 +132,10 @@ int domain_vintc_init(struct domain *d)
         d->arch.vintc->used_irqs =
             xvzalloc_array(unsigned long,
                            BITS_TO_LONGS(d->arch.vintc->nr_virqs));
-        if ( !d->arch.vintc->used_irqs )
+        d->arch.vintc->hw_irqs =
+            xvzalloc_array(unsigned long,
+                           BITS_TO_LONGS(d->arch.vintc->nr_virqs));
+        if ( !d->arch.vintc->used_irqs || !d->arch.vintc->hw_irqs )
             ret = -ENOMEM;
     }
 
@@ -140,17 +149,19 @@ void domain_vintc_deinit(struct domain *d)
     if ( !d->arch.vintc )
         return;
 
-    if ( d->arch.vintc->used_irqs )
+    if ( d->arch.vintc->hw_irqs )
     {
         unsigned int virq;
 
+        /* A virtual source has no h/w IRQ routed to the domain to release. */
         for ( virq = 0; virq < d->arch.vintc->nr_virqs; virq++ )
-            if ( test_bit(virq, d->arch.vintc->used_irqs) )
+            if ( test_bit(virq, d->arch.vintc->hw_irqs) )
                 release_guest_irq(d, virq);
-
-        XVFREE(d->arch.vintc->used_irqs);
     }
 
+    XVFREE(d->arch.vintc->used_irqs);
+    XVFREE(d->arch.vintc->hw_irqs);
+
     switch ( variant )
     {
     case INTC_APLIC:
@@ -169,6 +180,7 @@ void domain_vintc_deinit(struct domain *d)
  * Returns 0 on success, -EEXIST if @virq has already been reserved, which
  * legitimately happens when an IRQ is shared between devices, and -ERANGE if
  * @virq is outside the range of the interrupt sources the vINTC provides.
+ * A virtual source can't be shared, so for one -EEXIST is a failure.
  */
 int __overlay_init vintc_reserve_virq(const struct domain *d,
                                       unsigned int virq)
diff --git a/xen/arch/riscv/irq.c b/xen/arch/riscv/irq.c
index 9fc5a1bb703c..1b07b811b1fd 100644
--- a/xen/arch/riscv/irq.c
+++ b/xen/arch/riscv/irq.c
@@ -463,7 +463,7 @@ int route_irq_to_guest(struct domain *d, unsigned int virq,
     if ( retval )
         goto out;
 
-    retval = intc_route_irq_to_guest(desc, IRQ_NO_PRIORITY);
+    retval = intc_route_irq_to_guest(d, virq, desc, IRQ_NO_PRIORITY);
     if ( retval )
     {
         struct irqaction *action = irq_detach_action(desc, info);
-- 
2.56.1




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.