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

[PATCH v4 36/42] xen/riscv: send MSIs to a vCPU's guest interrupt file



vAPLIC has to send some MSIs itself, i.e. make an interrupt identity
pending in a vCPU's guest interrupt file, as an MSI addressed to that
file would.

Unlike the VS-level CSRs, which reach only the interrupt file of the
pCPU they are accessed on, a write to the SETIPNUM_LE register of an
interrupt file works from any pCPU. So map the interrupt files of each
hart in imsic_init(), and add vcpu_imsic_inject(), which writes an
identity to that register of the h/w guest interrupt file a vCPU owns.

vcpu_imsic_inject() holds the read side of vsfile_lock across the write,
so that a migration, which publishes the new file under the write side
and drains the old one only afterwards, can't drain the file between
the choice of the file and the write. A fence keeps the write ahead of
the release of the read side, whose own barrier orders only memory
accesses. Even so, the AIA spec lets the write take effect in the old
file only after the drain has read it.

A vCPU has no h/w guest interrupt file until it runs for the first time,
and s/w guest interrupt files aren't supported yet, so an injection into
such a vCPU fails with -EOPNOTSUPP.

Signed-off-by: Oleksii Kurochko <oleksii.kurochko@xxxxxxxxx>
---
vcpu_imsic_inject() gets its users in "xen/riscv: emulate the vAPLIC
genmsi register", for the MSIs a guest asks for through genmsi, and in
"xen/riscv: emulate the pending bits and MSIs of virtual vAPLIC
sources", for the MSIs of virtual vAPLIC sources.

Regarding the FENCE(o,w) after writel() in the injection function:
wouldn't it be better to just update the __io_aw() definition with
FENCE(o,w), so that such cases can never be missed? Would that lead to
an extra performance drop? Is writel() expected to be used only with
MMIO addresses?

Also, I checked the Linux implementation: __io_aw() indirectly *can*
lead to RISCV_FENCE(o, w) through MMIOWB, which is selected
unconditionally for RISC-V. Interestingly, with __io_aw() defined as:
  #define __io_aw()     mmiowb_set_pending()
RISCV_FENCE(o, w) is applied for a spinlock when it's unlocked, but not
for rwlocks, so in Linux FENCE(o,w) is still needed after writel() when
an rwlock is used.

---
Changes in v4:
 - New patch.
---
---
 xen/arch/riscv/imsic.c             | 67 +++++++++++++++++++++++++++++-
 xen/arch/riscv/include/asm/imsic.h |  8 ++++
 2 files changed, 74 insertions(+), 1 deletion(-)

diff --git a/xen/arch/riscv/imsic.c b/xen/arch/riscv/imsic.c
index 49b2d8cbb004..e2b3bfbd6619 100644
--- a/xen/arch/riscv/imsic.c
+++ b/xen/arch/riscv/imsic.c
@@ -24,11 +24,13 @@
 #include <xen/sched.h>
 #include <xen/smp.h>
 #include <xen/spinlock.h>
+#include <xen/vmap.h>
 #include <xen/xvmalloc.h>
 
 #include <asm/aia.h>
 #include <asm/aplic.h>
 #include <asm/imsic.h>
+#include <asm/io.h>
 #include <asm/p2m.h>
 #include <asm/vaplic.h>
 
@@ -181,10 +183,16 @@ static void imsic_update_state(struct vcpu *v, unsigned 
int guest_file_id)
     unsigned long flags;
     struct vimsic_state *vimsic_state = v->arch.vimsic_state;
     unsigned int cpu = guest_file_id ? v->processor : CPU_NONE;
+    void __iomem *vsfile_va = NULL;
+
+    if ( guest_file_id )
+        vsfile_va = imsic_cfg.msi[cpu].files_va +
+                    guest_file_id * IMSIC_MMIO_PAGE_SZ;
 
     write_lock_irqsave(&vimsic_state->vsfile_lock, flags);
     vimsic_state->guest_file_id = guest_file_id;
     vimsic_state->vsfile_cpu = cpu;
+    vimsic_state->vsfile_va = vsfile_va;
     write_unlock_irqrestore(&vimsic_state->vsfile_lock, flags);
 }
 
@@ -721,6 +729,45 @@ static void cf_check imsic_aplic_sync(void *unused)
         cpu_relax();
 }
 
+/*
+ * Make interrupt identity @iid pending in @v's IMSIC interrupt file, as an MSI
+ * addressed to that file would.
+ *
+ * An identity an IMSIC would ignore is ignored here too, and reported as
+ * success. -EOPNOTSUPP means that @v has no h/w guest interrupt file (a vCPU
+ * is given one only when it runs for the first time), and s/w guest interrupt
+ * files aren't supported yet.
+ */
+int vcpu_imsic_inject(const struct vcpu *v, unsigned int iid)
+{
+    struct vimsic_state *imsic = v->arch.vimsic_state;
+    unsigned long flags;
+    int rc = 0;
+
+    /* Identity 0 and identities the file doesn't implement are ignored. */
+    if ( !iid || iid > guest_num_msis )
+        return 0;
+
+    read_lock_irqsave(&imsic->vsfile_lock, flags);
+
+    if ( imsic->vsfile_cpu != CPU_NONE )
+    {
+        writel(iid, imsic->vsfile_va + IMSIC_MMIO_SETIPNUM_LE);
+
+        /*
+         * Keep the write ahead of the release of the read side, whose barrier
+         * orders only memory accesses.
+         */
+        RISCV_FENCE(o, w);
+    }
+    else
+        rc = -EOPNOTSUPP;
+
+    read_unlock_irqrestore(&imsic->vsfile_lock, flags);
+
+    return rc;
+}
+
 static void imsic_vsfile_local_clear(const struct imsic_vsfile_data *idata)
 {
     unsigned int i;
@@ -959,6 +1006,18 @@ int __init imsic_init(const struct dt_device_node *node)
         msi[cpu].base_addr = mmios[index].base_addr;
         msi[cpu].offset = reloff;
 
+        msi[cpu].files_va =
+            ioremap(msi[cpu].base_addr + msi[cpu].offset,
+                    IMSIC_HART_SIZE(imsic_cfg.guest_index_bits));
+        if ( !msi[cpu].files_va )
+        {
+            printk(XENLOG_ERR
+                   "%s: failed to map the interrupt files of cpu%u\n",
+                   node->name, cpu);
+            rc = -ENOMEM;
+            goto imsic_init_err;
+        }
+
         nr_handlers++;
     }
 
@@ -980,7 +1039,13 @@ int __init imsic_init(const struct dt_device_node *node)
 
  imsic_init_err:
     xvfree(mmios);
-    xvfree(msi);
+    if ( msi )
+    {
+        for ( unsigned int i = 0; i < nr_cpu_ids; i++ )
+            if ( msi[i].files_va )
+                iounmap(msi[i].files_va);
+        xvfree(msi);
+    }
 
     return rc;
 }
diff --git a/xen/arch/riscv/include/asm/imsic.h 
b/xen/arch/riscv/include/asm/imsic.h
index 488ce652f0b6..8245ec6eb695 100644
--- a/xen/arch/riscv/include/asm/imsic.h
+++ b/xen/arch/riscv/include/asm/imsic.h
@@ -19,6 +19,8 @@
 #define IMSIC_MMIO_PAGE_SHIFT   12
 #define IMSIC_MMIO_PAGE_SZ      (1UL << IMSIC_MMIO_PAGE_SHIFT)
 
+#define IMSIC_MMIO_SETIPNUM_LE  0x00
+
 #define IMSIC_MIN_ID            63
 #define IMSIC_MAX_ID            2047
 
@@ -34,6 +36,8 @@
 struct imsic_msi {
     paddr_t base_addr;
     unsigned long offset;
+    /* Xen's mapping of this hart's interrupt files, to send MSIs to them. */
+    void __iomem *files_va;
 };
 
 struct imsic_config {
@@ -105,6 +109,8 @@ struct vimsic_state {
      * h/w IMSIC VS-file -> vsfile_cpu < NR_CPUS
      */
     unsigned int vsfile_cpu;
+    /* Xen's mapping of the h/w IMSIC VS-file, NULL while there is none. */
+    void __iomem *vsfile_va;
 };
 
 struct dt_device_node;
@@ -135,4 +141,6 @@ void imsic_vsfile_attach(struct vcpu *v);
 
 void cf_check imsic_migrate_vcpu(struct vcpu *v);
 
+int vcpu_imsic_inject(const struct vcpu *v, unsigned int iid);
+
 #endif /* RISCV_IMSIC_H */
-- 
2.56.1




 


Rackspace

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