From: "Guo Ren (Alibaba DAMO Academy)" Previously, the number of Hypervisor Guest External Interrupt (HGEI) lines was stored in a single global variable `kvm_riscv_aia_nr_hgei` and assumed to be the same for all HARTs. This assumption does not hold on heterogeneous RISC-V SoCs where different cores may expose different HGEIE CSR widths. Introduce `nr_hgei` field into the per-CPU `struct aia_hgei_control` and probe the actual supported HGEI count for the current HART in `kvm_riscv_aia_enable()` using the standard RISC-V CSR probe technique: csr_write(CSR_HGEIE, -1UL); nr = fls_long(csr_read(CSR_HGEIE)); if (nr) nr--; All HGEI allocation, free and disable paths (`kvm_riscv_aia_free_hgei()`, `kvm_riscv_aia_disable()`, etc.) now use the per-CPU value instead of the global one. The global `kvm_riscv_aia_nr_hgei` now represents the minimum number of HGEI lines across HARTs and can be used to check whether HGEI support is available or not. This makes KVM AIA robust on big.LITTLE-style asymmetric platforms. Signed-off-by: Guo Ren (Alibaba DAMO Academy) Signed-off-by: Anup Patel --- arch/riscv/kvm/aia.c | 67 +++++++++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 23 deletions(-) diff --git a/arch/riscv/kvm/aia.c b/arch/riscv/kvm/aia.c index 5ec503288555..c289d8999aaf 100644 --- a/arch/riscv/kvm/aia.c +++ b/arch/riscv/kvm/aia.c @@ -23,6 +23,7 @@ struct aia_hgei_control { raw_spinlock_t lock; unsigned long free_bitmap; struct kvm_vcpu *owners[BITS_PER_LONG]; + unsigned int nr_hgei; }; static DEFINE_PER_CPU(struct aia_hgei_control, aia_hgei); static int hgei_parent_irq; @@ -452,7 +453,7 @@ void kvm_riscv_aia_free_hgei(int cpu, int hgei) raw_spin_lock_irqsave(&hgctrl->lock, flags); - if (hgei > 0 && hgei <= kvm_riscv_aia_nr_hgei) { + if (hgei > 0 && hgei <= hgctrl->nr_hgei) { if (!(hgctrl->free_bitmap & BIT(hgei))) { hgctrl->free_bitmap |= BIT(hgei); hgctrl->owners[hgei] = NULL; @@ -486,21 +487,8 @@ static irqreturn_t hgei_interrupt(int irq, void *dev_id) static int aia_hgei_init(void) { - int cpu, rc; + int rc; struct irq_domain *domain; - struct aia_hgei_control *hgctrl; - - /* Initialize per-CPU guest external interrupt line management */ - for_each_possible_cpu(cpu) { - hgctrl = per_cpu_ptr(&aia_hgei, cpu); - raw_spin_lock_init(&hgctrl->lock); - if (kvm_riscv_aia_nr_hgei) { - hgctrl->free_bitmap = - BIT(kvm_riscv_aia_nr_hgei + 1) - 1; - hgctrl->free_bitmap &= ~BIT(0); - } else - hgctrl->free_bitmap = 0; - } /* Skip SGEI interrupt setup for zero guest external interrupts */ if (!kvm_riscv_aia_nr_hgei) @@ -545,9 +533,48 @@ static void aia_hgei_exit(void) void kvm_riscv_aia_enable(void) { + const struct imsic_global_config *gc; + const struct imsic_local_config *lc; + struct aia_hgei_control *hgctrl; + if (!kvm_riscv_aia_available()) return; + gc = imsic_get_global_config(); + lc = (gc) ? this_cpu_ptr(gc->local) : NULL; + hgctrl = this_cpu_ptr(&aia_hgei); + + /* Figure-out number of bits in HGEIE */ + csr_write(CSR_HGEIE, -1UL); + hgctrl->nr_hgei = fls_long(csr_read(CSR_HGEIE)); + csr_write(CSR_HGEIE, 0); + if (hgctrl->nr_hgei) + hgctrl->nr_hgei--; + + /* + * Number of usable per-HART HGEI lines should be minimum of + * per-HART IMSIC guest files and number of bits in HGEIE. + */ + if (lc) + hgctrl->nr_hgei = min((ulong)hgctrl->nr_hgei, lc->nr_guest_files); + else + hgctrl->nr_hgei = 0; + + /* + * The global number of usable HGEI lines should be minimum + * across all HARTs. + */ + kvm_riscv_aia_nr_hgei = min(kvm_riscv_aia_nr_hgei, hgctrl->nr_hgei); + + if (hgctrl->nr_hgei) { + hgctrl->free_bitmap = BIT(hgctrl->nr_hgei + 1) - 1; + hgctrl->free_bitmap &= ~BIT(0); + } else { + hgctrl->free_bitmap = 0; + } + + raw_spin_lock_init(&hgctrl->lock); + csr_write(CSR_HVICTL, aia_hvictl_value(false)); csr_write(CSR_HVIPRIO1, 0x0); csr_write(CSR_HVIPRIO2, 0x0); @@ -588,7 +615,7 @@ void kvm_riscv_aia_disable(void) raw_spin_lock_irqsave(&hgctrl->lock, flags); - for (i = 0; i <= kvm_riscv_aia_nr_hgei; i++) { + for (i = 0; i <= hgctrl->nr_hgei; i++) { vcpu = hgctrl->owners[i]; if (!vcpu) continue; @@ -634,14 +661,8 @@ int kvm_riscv_aia_init(void) csr_write(CSR_HGEIE, 0); if (kvm_riscv_aia_nr_hgei) kvm_riscv_aia_nr_hgei--; - - /* - * Number of usable HGEI lines should be minimum of per-HART - * IMSIC guest files and number of bits in HGEIE - */ if (gc) - kvm_riscv_aia_nr_hgei = min((ulong)kvm_riscv_aia_nr_hgei, - gc->nr_guest_files); + kvm_riscv_aia_nr_hgei = min(kvm_riscv_aia_nr_hgei, gc->nr_guest_files); else kvm_riscv_aia_nr_hgei = 0; -- 2.43.0