[PATCH v2 39/39] xen/riscv: introduce IMSIC h/w interrupt file attaching to vcpu
Oleksii Kurochko <[email protected]>
| Newsgroups | gmane.comp.emulators.xen.devel |
|---|---|
| Message-ID | <4df9cf63943d0f371c8a25c6c9e84adbd3083a61.1787838835.git.oleksii.kurochko@gmail.com> |
Introduce imsic_vsfile_attach() to initialize the AIA-related state needed for a vCPU to have a working guest interrupt file. A guest (VS) interrupt file must be mapped to one of a pCPU's hardware interrupt files (if they exist), so the pCPU a vCPU will actually run on needs to be known first. arch_vcpu_create() is therefore not a suitable place to call vcpu_aia_init(), since the pCPU assigned to a vCPU can still change before it is first scheduled. To avoid reassigning the VS interrupt file id and remapping it to a different pCPU's hardware interrupt file, imsic_vsfile_attach() is called from a later point in the scheduling path (e.g. continue_new_vcpu()). Since it will end up being called from a non-__init context, it is not itself marked __init. Introduce imsic_update_state() to update a vCPU's guest IMSIC state (the guest interrupt file id and the pCPU whose hardware interrupt file it is mapped to) as a single consistent unit. This state can be read concurrently, e.g. by a future helper that checks whether a vCPU has a pending IMSIC interrupt, though no such consumer exists yet at this stage, so it is protected by a lock. Signed-off-by: Oleksii Kurochko <[email protected]> --- Changes in v2: - Update vcpu_aia_init() to catch sw interrupt file and update some debug messages in it. - Add vgein_release() if IMSIC h/w mapping failed. - imsic_update_state(): store v->processor rather than cpuid_to_hartid(), as ->vsfile_cpu is consumed as a Xen CPU id (aplic_hart_field(), cpumask_of()) and its NR_CPUS sentinel lives in that numbering space. - Drop parantethis aroud guest_file_id ? ... in imsic_update_state(). - Rename vcpu_aia_init to imsic_vsfile_attach() and move the code to imsic.c. --- --- xen/arch/riscv/domain.c | 2 + xen/arch/riscv/imsic.c | 146 +++++++++++++++++++++++------ xen/arch/riscv/include/asm/imsic.h | 2 + 3 files changed, 121 insertions(+), 29 deletions(-) diff --git a/xen/arch/riscv/domain.c b/xen/arch/riscv/domain.c index 0782148b7207..15b6bfffa97d 100644 --- a/xen/arch/riscv/domain.c +++ b/xen/arch/riscv/domain.c @@ -155,6 +155,8 @@ static void continue_new_vcpu(struct vcpu *prev) reset_stack_and_jump(idle_loop); else { + imsic_vsfile_attach(current); + /* * During a context switch to a new vCPU, interrupts must be disabled * to guarantee that the vCPU's CSR state can be safely restored into diff --git a/xen/arch/riscv/imsic.c b/xen/arch/riscv/imsic.c index 374a21ace15f..ad638d748517 100644 --- a/xen/arch/riscv/imsic.c +++ b/xen/arch/riscv/imsic.c @@ -212,7 +212,14 @@ unsigned int vcpu_guest_file_id(const struct vcpu *v) void imsic_update_state(struct vcpu *v, unsigned int guest_file_id) { - BUG_ON("unimplemented\n"); + unsigned long flags; + struct vimsic_state *vimsic_state = v->arch.vimsic_state; + unsigned int cpu = guest_file_id ? v->processor : NR_CPUS; + + write_lock_irqsave(&vimsic_state->vsfile_lock, flags); + vimsic_state->guest_file_id = guest_file_id; + vimsic_state->vsfile_cpu = cpu; + write_unlock_irqrestore(&vimsic_state->vsfile_lock, flags); } void __init imsic_ids_local_delivery(bool enable) @@ -640,6 +647,16 @@ struct imsic_vsfile_data { struct imsic_mrif *mrif; }; +/* + * Number of 64-bit EIx groups needed to cover all the interrupt identities an + * IMSIC interrupt file provides, which are 0 (never valid, but it still + * occupies a bit) up to and including imsic_cfg.nr_ids. + */ +static unsigned int imsic_nr_eix(void) +{ + return DIV_ROUND_UP(imsic_cfg.nr_ids + 1, BITS_PER_TYPE(uint64_t)); +} + /* * Execute func() on the pCPU which owns the IMSIC interrupt file func() is * going to work with. @@ -1138,20 +1155,90 @@ static void cf_check imsic_vsfile_local_update(void *data) csr_write(CSR_VSISELECT, old_vsiselect); } +/* + * Point the vCPU's HSTATUS.VGEIN at the guest interrupt file it has been + * given. It is applied to the hart when the vCPU's context is restored. + */ +static void vcpu_set_vgein(struct vcpu *v, unsigned int vsfile_id) +{ + unsigned long hstatus = vcpu_guest_cpu_user_regs(v)->hstatus; + + hstatus &= ~HSTATUS_VGEIN; + hstatus |= MASK_INSR(vsfile_id, HSTATUS_VGEIN); + + vcpu_guest_cpu_user_regs(v)->hstatus = hstatus; +} + +/* + * Take a h/w guest interrupt file of 'cpu' for the vCPU: zero the file out, + * map it into the domain's G-stage at the vCPU's virtual IMSIC page and + * record the new location in the per-vCPU IMSIC state. + * + * HSTATUS.VGEIN is deliberately left alone: the vCPU may be pointed at the + * file only when the file already holds the vCPU's interrupt state, which in + * the case of imsic_migrate_vcpu() happens only after the old file has been + * moved to the new one. Thereby it is up to the caller to call + * vcpu_set_vgein() at the right moment. + * + * Returns the id of the taken interrupt file, or 0 if none could be taken, in + * which case the domain is crashed. + */ +static unsigned int imsic_vsfile_acquire(struct vcpu *v, unsigned int cpu) +{ + struct imsic_vsfile_data vsfile_data = { .nr_eix = imsic_nr_eix() }; + unsigned int vsfile_id; + int rc; + + vsfile_id = vgein_assign(v); + if ( !vsfile_id ) + { + /* + * vgein_assign() returns 0 when no free h/w guest interrupt file is + * available. s/w guest interrupt files aren't supported yet, so such + * a vCPU can't be run. + */ + domain_crash(v->domain, + "%pv: no free h/w guest interrupt file on CPU%u\n", + v, cpu); + return 0; + } + + vsfile_data.hgei = vsfile_id; + + /* The file could still hold the state of its previous owner */ + imsic_call_on_cpu(cpu, imsic_vsfile_local_clear, &vsfile_data); + + rc = imsic_map_guest_file(v, vsfile_id); + if ( rc ) + { + vgein_release(v, vsfile_id, cpu); + + /* Can't continue w/o correctly mapped IMSIC interrupt file */ + domain_crash(v->domain, + "%pv: failed to map h/w guest interrupt file %u: %d\n", + v, vsfile_id, rc); + return 0; + } + + imsic_update_state(v, vsfile_id); + + return vsfile_id; +} + void imsic_migrate_vcpu(struct vcpu *v) { - unsigned int new_vsfile_hgei; + unsigned int new_vsfile_id; unsigned int new_vsfile_cpu; - unsigned int nr_hw_eix = DIV_ROUND_UP(imsic_cfg.nr_ids + 1, - BITS_PER_TYPE(uint64_t)); - struct imsic_vsfile_data vsfile_data = { - .nr_eix = nr_hw_eix, - }; + unsigned int nr_hw_eix = imsic_nr_eix(); struct vimsic_state *imsic_state = v->arch.vimsic_state; unsigned long flags; unsigned int old_vsfile_id; unsigned int old_vsfile_cpu; struct imsic_mrif tmrif = { }; + struct imsic_vsfile_data vsfile_data = { + .nr_eix = nr_hw_eix, + .mrif = &tmrif, + }; /* * The scheduler can mark a freshly created vCPU's unit as migrated and @@ -1189,25 +1276,10 @@ void imsic_migrate_vcpu(struct vcpu *v) */ new_vsfile_cpu = v->processor; - new_vsfile_hgei = vgein_assign(v); - - /* We don't support SW interrupt files at the moment. */ - BUG_ON(!new_vsfile_hgei); - - vsfile_data.hgei = new_vsfile_hgei; - - /* Zero-out new IMSIC VS-file */ - imsic_call_on_cpu(new_vsfile_cpu, imsic_vsfile_local_clear, &vsfile_data); - - /* Update G-stage mapping for the new IMSIC VS-file */ - if ( imsic_map_guest_file(v, new_vsfile_hgei) ) - { - domain_crash(v->domain, "Migration to hw interrupt file failed\n"); - + /* Zero-out, map and start to use the new IMSIC VS-file */ + new_vsfile_id = imsic_vsfile_acquire(v, new_vsfile_cpu); + if ( !new_vsfile_id ) return; - } - - imsic_update_state(v, new_vsfile_hgei); /* * TODO: Modify the relevant translation tables at all IOMMUs so that MSIs @@ -1245,7 +1317,7 @@ void imsic_migrate_vcpu(struct vcpu *v) vgein_release(v, old_vsfile_id, old_vsfile_cpu); /* Restore register state in the new IMSIC VS-file */ - vsfile_data.mrif = &tmrif; + vsfile_data.hgei = new_vsfile_id; imsic_call_on_cpu(new_vsfile_cpu, imsic_vsfile_local_update, &vsfile_data); /* @@ -1262,7 +1334,23 @@ void imsic_migrate_vcpu(struct vcpu *v) imsic_call_on_cpu(new_vsfile_cpu, imsic_local_hgeie_set, &vsfile_data); /* Set VCPU HSTATUS.VGEIN to new IMSIC VS-file */ - vcpu_guest_cpu_user_regs(v)->hstatus &= ~HSTATUS_VGEIN; - vcpu_guest_cpu_user_regs(v)->hstatus |= - MASK_INSR(new_vsfile_hgei, HSTATUS_VGEIN); + vcpu_set_vgein(v, new_vsfile_id); +} + +void imsic_vsfile_attach(struct vcpu *v) +{ + unsigned int new_vsfile_id; + + if ( !aia_usable() ) + return; + + new_vsfile_id = imsic_vsfile_acquire(v, v->processor); + if ( !new_vsfile_id ) + return; + + /* + * The vCPU has never run yet, so the just zeroed out file is all the + * interrupt state it has and HSTATUS.VGEIN can be pointed at it at once. + */ + vcpu_set_vgein(v, new_vsfile_id); } diff --git a/xen/arch/riscv/include/asm/imsic.h b/xen/arch/riscv/include/asm/imsic.h index 6395b539c52d..f0edf0bff5d9 100644 --- a/xen/arch/riscv/include/asm/imsic.h +++ b/xen/arch/riscv/include/asm/imsic.h @@ -125,4 +125,6 @@ int imsic_map_guest_file(struct vcpu *v, unsigned int vsfile_id); void imsic_migrate_vcpu(struct vcpu *v); +void imsic_vsfile_attach(struct vcpu *v); + #endif /* ASM_RISCV_IMSIC_H */ -- 2.55.0