[PATCH v2 35/39] xen/riscv: add basic VGEIN management for AIA guests
Oleksii Kurochko <[email protected]>
| Newsgroups | gmane.comp.emulators.xen.devel |
|---|---|
| Message-ID | <b84c2624e2f49e99a5f29439f4f01608eb1e5825.1787838835.git.oleksii.kurochko@gmail.com> |
It was decided to add support for IMSIC from the start instead of having APLIC operate in direct delivery mode, as it requires a trap-and-emulation approach, which is not optimal from a performance standpoint. AIA provides a hardware-accelerated mechanism for delivering external interrupts to domains via "guest interrupt files" located in IMSIC. A single physical hart can implement multiple such files (up to GEILEN), allowing several virtual harts to receive interrupts directly from hardware. Introduce per-CPU tracking of guest interrupt file identifiers (VGEIN) for systems implementing AIA specification. Each CPU maintains a bitmap describing which guest interrupt files are currently in use. Implement helpers to initialize the bitmap based on the number of available guest interrupt files (GEILEN), assign a VGEIN to a vCPU, and release it when no longer needed. Signed-off-by: Oleksii Kurochko <[email protected]> --- Also in the next patch there is other context to understand the usage of spinlock introduced here. --- Changes in v2: - make vgein_init() pCPU agnostic as it is working with CSR which could be read only on local pCPU itself. - Move introduction of vgein_ctrl->owners[] to separate patch. - Add ASSERT() and re-init vgein->bmp with 0. - Update the commit message (drop the last sentence as ->hstatus isn't filled anymore in in vgein_*() functions). - Introduce vgein_deinit(). --- --- xen/arch/riscv/aia.c | 141 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 137 insertions(+), 4 deletions(-) diff --git a/xen/arch/riscv/aia.c b/xen/arch/riscv/aia.c index be3901ec0cfa..1aca07c2f70f 100644 --- a/xen/arch/riscv/aia.c +++ b/xen/arch/riscv/aia.c @@ -1,13 +1,31 @@ /* SPDX-License-Identifier: GPL-2.0-only */ -#include <xen/bug.h> +#include <xen/bitops.h> +#include <xen/cpu.h> #include <xen/errno.h> #include <xen/init.h> #include <xen/sections.h> #include <xen/sched.h> +#include <xen/spinlock.h> #include <xen/types.h> +#include <asm/aia.h> #include <asm/cpufeature.h> +#include <asm/csr.h> +#include <asm/current.h> + +struct vgein_ctrl { + /* The least-significant bits are implemented first, apart from bit 0 */ + unsigned long bmp; + spinlock_t lock; + unsigned int geilen; +}; + +/* + * VGEIN control structure for each physical CPU to track which VS (guest) + * interrupt file IDs are in use. + */ +static DEFINE_PER_CPU(struct vgein_ctrl, vgein); static bool __ro_after_init _aia_usable; @@ -16,22 +34,137 @@ bool aia_usable(void) return _aia_usable; } +/* HGEIE is a per-hart CSR, so this has to run on the CPU being initialized. */ +static int vgein_init(void) +{ + struct vgein_ctrl *vgein = &this_cpu(vgein); + + spin_lock_init(&vgein->lock); + + csr_write(CSR_HGEIE, ~0UL); + vgein->geilen = flsl(csr_read(CSR_HGEIE) >> 1); + csr_write(CSR_HGEIE, 0); + + vgein->bmp = 0; + + if ( !vgein->geilen ) + return -EOPNOTSUPP; + + return 0; +} + +static void vgein_deinit(void) +{ + csr_write(CSR_HGEIE, 0); +} + +static int cf_check cpu_callback(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + unsigned int cpu = (unsigned long)hcpu; + int rc = 0; + + switch ( action ) + { + case CPU_STARTING: + rc = vgein_init(); + if ( rc ) + printk(XENLOG_ERR "AIA: failed to init vgein for CPU%u: %d\n", + cpu, rc); + break; + + case CPU_DYING: + vgein_deinit(); + break; + } + + return notifier_from_errno(rc); +} + +static struct notifier_block cpu_nfb = { + .notifier_call = cpu_callback, +}; + void __init aia_init(void) { + int rc; + if ( !riscv_isa_extension_available(NULL, RISCV_ISA_EXT_ssaia) ) + { + dprintk(XENLOG_WARNING, "SSAIA isn't present in riscv,isa\n"); return; + } + + if ( (rc = vgein_init()) ) + { + dprintk(XENLOG_ERR, "vgein_init() failed: %d\n", rc); + return; + } _aia_usable = true; + + register_cpu_notifier(&cpu_nfb); } unsigned int vgein_assign(struct vcpu *v) { - BUG_ON("unimplemented\n"); + unsigned int vgein_id; + struct vgein_ctrl *vgein = &per_cpu(vgein, v->processor); + unsigned long *bmp = &vgein->bmp; + unsigned long flags; - return 0; + if ( !vgein->geilen ) + return 0; + + spin_lock_irqsave(&vgein->lock, flags); + /* + * The vgein_id shouldn't be zero, as it will indicate that no guest + * external interrupt source is selected for VS-level external interrupts + * according to RISC-V privileged spec: + * Hypervisor Status Register (hstatus) in RISC-V privileged spec: + * + * The VGEIN (Virtual Guest External Interrupt Number) field selects + * a guest external interrupt source for VS-level external interrupts. + * VGEIN is a WLRL field that must be able to hold values between zero + * and the maximum guest external interrupt number (known as GEILEN), + * inclusive. + * When VGEIN=0, no guest external interrupt source is selected for + * VS-level external interrupts. + * + * So start to search from bit number 1. + */ + vgein_id = find_next_zero_bit(bmp, vgein->geilen + 1, 1); + + if ( vgein_id > vgein->geilen ) + vgein_id = 0; + else + __set_bit(vgein_id, bmp); + + spin_unlock_irqrestore(&vgein->lock, flags); + +#ifdef VGEIN_DEBUG + gprintk(XENLOG_DEBUG, "%s: %pv: vgein_id(%u), xen_cpu%u_bmp=%#lx\n", + __func__, v, vgein_id, v->processor, *bmp); +#endif + + return vgein_id; } void vgein_release(struct vcpu *v, unsigned int vgein_id, unsigned int cpu) { - BUG_ON("unimplemented\n"); + unsigned long flags; + struct vgein_ctrl *vgein = &per_cpu(vgein, cpu); + + if ( !vgein_id ) + return; + + spin_lock_irqsave(&vgein->lock, flags); + if ( !__test_and_clear_bit(vgein_id, &vgein->bmp) ) + ASSERT_UNREACHABLE(); + spin_unlock_irqrestore(&vgein->lock, flags); + +#ifdef VGEIN_DEBUG + gprintk(XENLOG_DEBUG, "%s: %pv: vgein_id(%u), xen_cpu%u_bmp=%#lx\n", + __func__, v, vgein_id, cpu, vgein->bmp); +#endif } -- 2.55.0