Re: [PATCH v5 2/2] RISC-V: KVM: AIA: Make HGEI number management fully per-CPU
Guo Ren <[email protected]>
| Newsgroups | org.infradead.lists.kvm-riscv,org.infradead.lists.linux-riscv,org.kernel.vger.kvm,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <CAJF2gTST9S5ojmvqvGZ7AekTKyN5JQJQZbW=Ruyg40ZRZmBtNQ@mail.gmail.com> |
On Mon, May 25, 2026 at 5:49 PM Anup Patel <[email protected]> wrote: > > From: "Guo Ren (Alibaba DAMO Academy)" <[email protected]> > > 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) <[email protected]> > Signed-off-by: Anup Patel <[email protected]> > --- > arch/riscv/include/asm/kvm_aia.h | 2 +- > arch/riscv/kvm/aia.c | 88 +++++++++++++++++++------------- > arch/riscv/kvm/aia_device.c | 4 +- > arch/riscv/kvm/main.c | 8 +-- > 4 files changed, 60 insertions(+), 42 deletions(-) > > diff --git a/arch/riscv/include/asm/kvm_aia.h b/arch/riscv/include/asm/kvm_aia.h > index b04ecdd1a860..c67ec5ac0a14 100644 > --- a/arch/riscv/include/asm/kvm_aia.h > +++ b/arch/riscv/include/asm/kvm_aia.h > @@ -79,7 +79,7 @@ struct kvm_vcpu_aia { > > #define irqchip_in_kernel(k) ((k)->arch.aia.in_kernel) > > -extern unsigned int kvm_riscv_aia_nr_hgei; > +extern atomic_t kvm_riscv_aia_nr_hgei; > extern unsigned int kvm_riscv_aia_max_ids; > DECLARE_STATIC_KEY_FALSE(kvm_riscv_aia_available); > #define kvm_riscv_aia_available() \ > diff --git a/arch/riscv/kvm/aia.c b/arch/riscv/kvm/aia.c > index 5ec503288555..bafb009c5ce5 100644 > --- a/arch/riscv/kvm/aia.c > +++ b/arch/riscv/kvm/aia.c > @@ -21,13 +21,15 @@ > > struct aia_hgei_control { > raw_spinlock_t lock; > + bool free_bitmap_initialized; > 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; > > -unsigned int kvm_riscv_aia_nr_hgei; > +atomic_t kvm_riscv_aia_nr_hgei; > unsigned int kvm_riscv_aia_max_ids; > DEFINE_STATIC_KEY_FALSE(kvm_riscv_aia_available); > > @@ -452,7 +454,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,26 +488,18 @@ static irqreturn_t hgei_interrupt(int irq, void *dev_id) > > static int aia_hgei_init(void) > { > - int cpu, rc; > - struct irq_domain *domain; > struct aia_hgei_control *hgctrl; > + struct irq_domain *domain; > + int cpu, rc; > > /* 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; > + hgctrl->free_bitmap_initialized = false; > + hgctrl->free_bitmap = 0; > } > > - /* Skip SGEI interrupt setup for zero guest external interrupts */ > - if (!kvm_riscv_aia_nr_hgei) > - goto skip_sgei_interrupt; > - > /* Find INTC irq domain */ > domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(), > DOMAIN_BUS_ANY); > @@ -529,25 +523,60 @@ static int aia_hgei_init(void) > return rc; > } > > -skip_sgei_interrupt: > return 0; > } > > static void aia_hgei_exit(void) > { > - /* Do nothing for zero guest external interrupts */ > - if (!kvm_riscv_aia_nr_hgei) > - return; > - > /* Free per-CPU SGEI interrupt */ > free_percpu_irq(hgei_parent_irq, &aia_hgei); > } > > void kvm_riscv_aia_enable(void) > { > + const struct imsic_global_config *gc; > + const struct imsic_local_config *lc; > + struct aia_hgei_control *hgctrl; > + unsigned long flags; > + int aia_nr_hgei; > + > 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); Great! You correct that. > + else > + hgctrl->nr_hgei = 0; > + > + /* Update the number of IMSIC guest files across all HARTs */ > + aia_nr_hgei = atomic_read(&kvm_riscv_aia_nr_hgei); > + do { > + if (aia_nr_hgei <= hgctrl->nr_hgei) > + break; > + } while (!atomic_try_cmpxchg(&kvm_riscv_aia_nr_hgei, &aia_nr_hgei, hgctrl->nr_hgei)); > + > + raw_spin_lock_irqsave(&hgctrl->lock, flags); > + if (!hgctrl->free_bitmap_initialized) { > + hgctrl->free_bitmap = (hgctrl->nr_hgei) ? GENMASK_ULL(hgctrl->nr_hgei, 1) : 0; > + hgctrl->free_bitmap_initialized = true; > + } > + raw_spin_unlock_irqrestore(&hgctrl->lock, flags); > + > csr_write(CSR_HVICTL, aia_hvictl_value(false)); > csr_write(CSR_HVIPRIO1, 0x0); > csr_write(CSR_HVIPRIO2, 0x0); > @@ -588,7 +617,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; > @@ -628,26 +657,15 @@ int kvm_riscv_aia_init(void) > return -ENODEV; > gc = imsic_get_global_config(); > > - /* Figure-out number of bits in HGEIE */ > - csr_write(CSR_HGEIE, -1UL); > - kvm_riscv_aia_nr_hgei = fls_long(csr_read(CSR_HGEIE)); > - 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 > - */ > + /* Set initial value of IMSIC guest files across all HARTs */ > if (gc) > - kvm_riscv_aia_nr_hgei = min((ulong)kvm_riscv_aia_nr_hgei, > - gc->nr_guest_files); > + atomic_set(&kvm_riscv_aia_nr_hgei, gc->nr_guest_files); > else > - kvm_riscv_aia_nr_hgei = 0; > + atomic_set(&kvm_riscv_aia_nr_hgei, 0); > > /* Find number of guest MSI IDs */ > kvm_riscv_aia_max_ids = IMSIC_MAX_ID; > - if (gc && kvm_riscv_aia_nr_hgei) > + if (gc) > kvm_riscv_aia_max_ids = gc->nr_guest_ids + 1; > > /* Initialize guest external interrupt line management */ > diff --git a/arch/riscv/kvm/aia_device.c b/arch/riscv/kvm/aia_device.c > index 3d1e81e2a36b..be83c2d5fc30 100644 > --- a/arch/riscv/kvm/aia_device.c > +++ b/arch/riscv/kvm/aia_device.c > @@ -71,7 +71,7 @@ static int aia_config(struct kvm *kvm, unsigned long type, > * external interrupts (i.e. non-zero > * VS-level IMSIC pages). > */ > - if (!kvm_riscv_aia_nr_hgei) > + if (!atomic_read(&kvm_riscv_aia_nr_hgei)) > return -EINVAL; > break; > default: > @@ -628,7 +628,7 @@ void kvm_riscv_aia_init_vm(struct kvm *kvm) > */ > > /* Initialize default values in AIA global context */ > - aia->mode = (kvm_riscv_aia_nr_hgei) ? > + aia->mode = (atomic_read(&kvm_riscv_aia_nr_hgei)) ? > KVM_DEV_RISCV_AIA_MODE_AUTO : KVM_DEV_RISCV_AIA_MODE_EMUL; > aia->nr_ids = kvm_riscv_aia_max_ids - 1; > aia->nr_sources = 0; > diff --git a/arch/riscv/kvm/main.c b/arch/riscv/kvm/main.c > index cb8a65273c1f..0924c75100a2 100644 > --- a/arch/riscv/kvm/main.c > +++ b/arch/riscv/kvm/main.c > @@ -168,10 +168,6 @@ static int __init riscv_kvm_init(void) > > kvm_info("VMID %ld bits available\n", kvm_riscv_gstage_vmid_bits()); > > - if (kvm_riscv_aia_available()) > - kvm_info("AIA available with %d guest external interrupts\n", > - kvm_riscv_aia_nr_hgei); > - > kvm_riscv_setup_vendor_features(); > > kvm_register_perf_callbacks(); > @@ -182,6 +178,10 @@ static int __init riscv_kvm_init(void) > return rc; > } > > + if (kvm_riscv_aia_available()) > + kvm_info("AIA available with %d guest external interrupts\n", > + atomic_read(&kvm_riscv_aia_nr_hgei)); > + > return 0; > } > module_init(riscv_kvm_init); > -- > 2.43.0 > -- Best Regards Guo Ren -- kvm-riscv mailing list [email protected] http://lists.infradead.org/mailman/listinfo/kvm-riscv