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

[PATCH v3 29/39] xen/riscv: prepare new IMSIC VS-file



Implement the first steps of migrating a vCPU to a different guest
interrupt file: take an interrupt file on the vCPU's new pCPU with
vgein_assign() and bring it to a clean state by setting eidelivery and
eithreshold to 0 and zeroing all implemented interrupt-pending and
interrupt-enable bits (the eip and eie arrays).

An IMSIC VS-file is reachable only through hstatus.VGEIN of the hart it
belongs to, so the clearing is done by that hart, through
imsic_call_on_cpu(). The helper doing it temporarily retargets
hstatus.VGEIN and vsiselect, which belong to whatever vCPU is running
there, and restores both before returning.

vgein_assign() is only a stub for now. Software interrupt files aren't
supported, so a vCPU which can't be given a hardware one hits a BUG_ON(),
and the BUG_ON("unimplemented") at the end of imsic_migrate_vcpu() stays,
as the migration functionality isn't complete yet.

Signed-off-by: Oleksii Kurochko <oleksii.kurochko@xxxxxxxxx>
---
Changes in v3:
 - Split the switchcase comment so each part sits above the macros it
   describes.
 - Drop imsic_switchcase_ret() as it has no users in this patch.
 - Drop the unused mrif member from struct imsic_vsfile_data.
 - Leave the trailing semicolon of the switchcase macros to their users.
 - Use __VA_ARGS__ instead of the GNU ##__VA_ARGS__ extension and
   parenthesize ireg in the switchcase expansion pyramid.
 - Make imsic_call_on_cpu() take struct imsic_vsfile_data * instead of
   void *.
 - Explain in imsic_vsfile_local_clear() why hstatus and vsiselect are
   saved and restored: the file is reached by retargeting hstatus.VGEIN,
   imsic_vs_csr_write() alters vsiselect, and both belong to the vCPU
   running on the pCPU.
 - Introduce imsic_eix_write64() to write a whole 64-bit EIx group,
   documenting the EIx register layout on RV32 vs RV64 above it, which
   lets the #ifdef CONFIG_RISCV_32 in imsic_vsfile_local_clear() go.
 - Drop the unclear "Latch the pCPU" comment.
 - Drop the redundant parentheses around the arguments of
   imsic_vs_csr_write() as they are passed to csr_*() as whole expressions.
 - Add missing break after ASSERT_UNREACHABLE() in the default case of
   imsic_eix_write().
 - Compute the number of EIx groups once in imsic_parse_node() and keep it
   in imsic_cfg.nr_eix instead of recalculating it on each migration.
 - Drop the introduction of BITS_PER_TYPE() in xen/include/xen/config.h as
   plain 64 is used instead.
 - Minor style and wording fixes: "} while (0)" in imsic_vs_csr_write(),
   no "\n" in the BUG_ON() message of vgein_assign(), and the extra args of
   the switchcase expansion pyramid are no longer described as optional.
 - Rewrite the commit message: it described saving eidelivery/eithreshold
   of the old interrupt file, which isn't done here, and adding the
   BUG_ON("unimplemented") placeholder, which is already present.
 - Drop the new_vsfile_{hgei,cpu} locals: vsfile_data.hgei and v->processor
   serve the same purpose.
---
Changes in v2:
 - New patch.
---
---
 xen/arch/riscv/aia.c               |   9 ++
 xen/arch/riscv/imsic.c             | 159 +++++++++++++++++++++++++++++
 xen/arch/riscv/include/asm/aia.h   |   4 +
 xen/arch/riscv/include/asm/imsic.h |   7 ++
 4 files changed, 179 insertions(+)

diff --git a/xen/arch/riscv/aia.c b/xen/arch/riscv/aia.c
index e31c9c2d24b6..229ab4b8678e 100644
--- a/xen/arch/riscv/aia.c
+++ b/xen/arch/riscv/aia.c
@@ -1,8 +1,10 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 
+#include <xen/bug.h>
 #include <xen/errno.h>
 #include <xen/init.h>
 #include <xen/sections.h>
+#include <xen/sched.h>
 #include <xen/types.h>
 
 #include <asm/cpufeature.h>
@@ -21,3 +23,10 @@ void __init aia_init(void)
 
     _aia_usable = true;
 }
+
+unsigned int vgein_assign(struct vcpu *v)
+{
+    BUG_ON("unimplemented");
+
+    return 0;
+}
diff --git a/xen/arch/riscv/imsic.c b/xen/arch/riscv/imsic.c
index 2f0bb29ae639..f1e44dc56bb8 100644
--- a/xen/arch/riscv/imsic.c
+++ b/xen/arch/riscv/imsic.c
@@ -26,6 +26,7 @@
 #include <xen/spinlock.h>
 #include <xen/xvmalloc.h>
 
+#include <asm/aia.h>
 #include <asm/imsic.h>
 
 #define IMSIC_HART_SIZE(guest_bits) (BIT(guest_bits, U) * IMSIC_MMIO_PAGE_SZ)
@@ -77,6 +78,72 @@ do {                            \
     csr_clear(CSR_SIREG, v);    \
 } while (0)
 
+#define imsic_vs_csr_write(c, v)    \
+do {                                \
+    csr_write(CSR_VSISELECT, c);    \
+    csr_write(CSR_VSIREG, v);       \
+} while (0)
+
+/*
+ * Leaf macros for the switchcase expansion pyramid below:
+ * imsic_switchcase_break(ireg, op, v) - emit "case ireg: op(ireg, v); break"
+ */
+#define imsic_switchcase_break(ireg, op, v) \
+    case ireg:                              \
+        op(ireg, v);                        \
+        break
+
+/*
+ * Generic switchcase expansion pyramid.
+ * F is the per-operation leaf macro, ireg is the base register index.
+ * Extra args (e.g. an operation and/or a value) are forwarded to F via
+ * __VA_ARGS__.
+ */
+#define imsic_switchcase_2(F, ireg, ...)             \
+    F((ireg) + 0, __VA_ARGS__);                      \
+    F((ireg) + 1, __VA_ARGS__)
+#define imsic_switchcase_4(F, ireg, ...)             \
+    imsic_switchcase_2(F, (ireg) + 0, __VA_ARGS__);  \
+    imsic_switchcase_2(F, (ireg) + 2, __VA_ARGS__)
+#define imsic_switchcase_8(F, ireg, ...)             \
+    imsic_switchcase_4(F, (ireg) + 0, __VA_ARGS__);  \
+    imsic_switchcase_4(F, (ireg) + 4, __VA_ARGS__)
+#define imsic_switchcase_16(F, ireg, ...)            \
+    imsic_switchcase_8(F, (ireg) + 0, __VA_ARGS__);  \
+    imsic_switchcase_8(F, (ireg) + 8, __VA_ARGS__)
+#define imsic_switchcase_32(F, ireg, ...)            \
+    imsic_switchcase_16(F, (ireg) + 0, __VA_ARGS__); \
+    imsic_switchcase_16(F, (ireg) + 16, __VA_ARGS__)
+#define imsic_switchcase_64(F, ireg, ...)            \
+    imsic_switchcase_32(F, (ireg) + 0, __VA_ARGS__); \
+    imsic_switchcase_32(F, (ireg) + 32, __VA_ARGS__)
+
+static void imsic_eix_write(unsigned int ireg, unsigned long val)
+{
+    switch ( ireg )
+    {
+    imsic_switchcase_64(imsic_switchcase_break, IMSIC_EIP0,
+                        imsic_vs_csr_write, val);
+    imsic_switchcase_64(imsic_switchcase_break, IMSIC_EIE0,
+                        imsic_vs_csr_write, val);
+    default:
+        ASSERT_UNREACHABLE();
+        break;
+    }
+}
+
+/*
+ * On RV64 a 64-bit EIx group is the even-numbered register alone, whereas on
+ * RV32 it is split into the even-numbered (low half) and the following
+ * odd-numbered (high half) register.
+ */
+static void imsic_eix_write64(unsigned int ireg, uint64_t val)
+{
+    imsic_eix_write(ireg, val);
+    if ( IS_ENABLED(CONFIG_RISCV_32) )
+        imsic_eix_write(ireg + 1, val >> 32);
+}
+
 unsigned int vcpu_guest_file_id(const struct vcpu *v)
 {
     return ACCESS_ONCE(v->arch.vimsic_state->guest_file_id);
@@ -318,6 +385,8 @@ static int __init imsic_parse_node(const struct 
dt_device_node *node,
         return -ENOENT;
     }
 
+    imsic_cfg.nr_eix = DIV_ROUND_UP(imsic_cfg.nr_ids + 1, 64);
+
     /* Compute base address */
     *nr_mmios = 0;
     rc = dt_device_get_address(node, *nr_mmios, &base_addr, NULL);
@@ -389,6 +458,79 @@ int cf_check vcpu_imsic_init(struct vcpu *v)
     return 0;
 }
 
+/*
+ * The imsic_vsfile_local_*() helpers have to run on the pCPU owning the
+ * interrupt file, so they are called through imsic_call_on_cpu(), which
+ * passes them a single pointer. Their arguments are bundled here.
+ */
+struct imsic_vsfile_data {
+    /* Guest interrupt file to work on, i.e. the hstatus.VGEIN selecting it. */
+    unsigned int hgei;
+    /* Number of 64-bit EIx groups to cover, see imsic_cfg.nr_eix. */
+    unsigned int nr_eix;
+};
+
+/*
+ * Execute func() on the pCPU which owns the IMSIC interrupt file func() is
+ * going to work with.
+ *
+ * An IMSIC VS-file is reachable only through hstatus.VGEIN of the hart the
+ * file belongs to, and a guest interrupt file index is meaningless on any
+ * other hart, so such work always has to be done by that very hart.
+ *
+ * The local case runs with IRQs disabled to provide func() with the same
+ * environment it is given when it is called from the function call IPI
+ * handler.
+ */
+static void imsic_call_on_cpu(unsigned int cpu, void (*func)(void *),
+                              struct imsic_vsfile_data *data)
+{
+    if ( cpu == smp_processor_id() )
+    {
+        unsigned long flags;
+
+        local_irq_save(flags);
+        func(data);
+        local_irq_restore(flags);
+    }
+    else
+        on_selected_cpus(cpumask_of(cpu), func, data, 1);
+}
+
+static void cf_check imsic_vsfile_local_clear(void *data)
+{
+    unsigned int i;
+    const struct imsic_vsfile_data *idata = data;
+    unsigned long new_hstatus, old_hstatus, old_vsiselect;
+
+    /* We can only zero-out if we have a IMSIC VS-file */
+    if ( !idata->hgei )
+        return;
+
+    /*
+     * The file is reached by retargeting hstatus.VGEIN, and
+     * imsic_vs_csr_write() alters vsiselect. Both belong to the vCPU running
+     * on this pCPU, so restore them before returning.
+     */
+    old_vsiselect = csr_read(CSR_VSISELECT);
+    old_hstatus = csr_read(CSR_HSTATUS);
+    new_hstatus = old_hstatus & ~HSTATUS_VGEIN;
+    new_hstatus |= MASK_INSR(idata->hgei, HSTATUS_VGEIN);
+    csr_write(CSR_HSTATUS, new_hstatus);
+
+    imsic_vs_csr_write(IMSIC_EIDELIVERY, 0);
+    imsic_vs_csr_write(IMSIC_EITHRESHOLD, 0);
+
+    for ( i = 0; i < idata->nr_eix; i++ )
+    {
+        imsic_eix_write64(IMSIC_EIP0 + i * 2, 0);
+        imsic_eix_write64(IMSIC_EIE0 + i * 2, 0);
+    }
+
+    csr_write(CSR_HSTATUS, old_hstatus);
+    csr_write(CSR_VSISELECT, old_vsiselect);
+}
+
 void cf_check vcpu_imsic_deinit(struct vcpu *v)
 {
     XVFREE(v->arch.vimsic_state);
@@ -693,6 +835,10 @@ int __init vimsic_make_domu_dt_node(struct kernel_info 
*kinfo,
 
 void imsic_migrate_vcpu(struct vcpu *v)
 {
+    struct imsic_vsfile_data vsfile_data = {
+        .nr_eix = imsic_cfg.nr_eix,
+    };
+
     /*
      * The scheduler can mark a freshly created vCPU's unit as migrated and
      * invoke this before the vCPU has ever run (see the migrated branch in
@@ -703,5 +849,18 @@ void imsic_migrate_vcpu(struct vcpu *v)
     if ( v->arch.last_cpu == CPU_NONE )
         return;
 
+    /*
+     * At this point, all interrupt producers are still using the old IMSIC
+     * VS-file.
+     */
+
+    vsfile_data.hgei = vgein_assign(v);
+
+    /* We don't support SW interrupt files at the moment. */
+    BUG_ON(!vsfile_data.hgei);
+
+    /* Zero-out new IMSIC VS-file */
+    imsic_call_on_cpu(v->processor, imsic_vsfile_local_clear, &vsfile_data);
+
     BUG_ON("unimplemented");
 }
diff --git a/xen/arch/riscv/include/asm/aia.h b/xen/arch/riscv/include/asm/aia.h
index aaa4bf91fc75..53a1efb042f8 100644
--- a/xen/arch/riscv/include/asm/aia.h
+++ b/xen/arch/riscv/include/asm/aia.h
@@ -3,8 +3,12 @@
 #ifndef RISCV_AIA_H
 #define RISCV_AIA_H
 
+struct vcpu;
+
 bool aia_usable(void);
 
 void aia_init(void);
 
+unsigned int vgein_assign(struct vcpu *v);
+
 #endif /* RISCV_AIA_H */
diff --git a/xen/arch/riscv/include/asm/imsic.h 
b/xen/arch/riscv/include/asm/imsic.h
index 22ce90e2e9b3..8130d125f5c3 100644
--- a/xen/arch/riscv/include/asm/imsic.h
+++ b/xen/arch/riscv/include/asm/imsic.h
@@ -55,6 +55,13 @@ struct imsic_config {
     /* Number off interrupt identities */
     unsigned int nr_ids;
 
+    /*
+     * Number of 64-bit EIx groups needed to cover all the interrupt
+     * identities, which are 0 (never valid, but it still occupies a bit) up
+     * to and including nr_ids.
+     */
+    unsigned int nr_eix;
+
     /* MSI */
     const struct imsic_msi *msi;
 
-- 
2.55.0




 


Rackspace

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