Re: [PATCH v4 16/48] KVM: arm64: gic-v5: Request doorbells when VPEs enter WFI
Sascha Bischoff <[email protected]> Fri, 31 Jul 2026 10:20:00 +0000
| Newsgroups | dev.linux.lists.sashiko-reviews,dev.linux.lists.kvmarm,org.kernel.vger.kvm |
|---|---|
| Message-ID | <[email protected]> |
On Fri, 2026-07-24 at 11:41 +0000, [email protected] wrote: > Thank you for your contribution! Sashiko AI review found 2 potential > issue(s) to consider: > - [High] Stale `db_fired` state causes an infinite loop bypassing WFI > sleep, pegging the host CPU to 100%. > - [High] Incorrect GICv5 doorbell priority mask (DBPM) calculation > drops preempting interrupts and vNMIs. > -- > > --- Patch [16]: [PATCH v4 16/48] KVM: arm64: gic-v5: Request > doorbells when VPEs enter WFI --- > commit 4c69e1e71f915cfc6ba78559fe449983dee20dd6 > Author: Sascha Bischoff <[email protected]> > > KVM: arm64: gic-v5: Request doorbells when VPEs enter WFI > > This commit requests a GICv5 VPE doorbell when a vcpu enters WFI, > allowing > KVM to be notified when an SPI or LPI becomes pending. It also > calculates > a doorbell priority mask (DBPM) from the virtual priority state to > avoid > waking the guest for masked interrupts. > > > diff --git a/arch/arm64/kvm/vgic/vgic-v5.c > > b/arch/arm64/kvm/vgic/vgic-v5.c > > --- a/arch/arm64/kvm/vgic/vgic-v5.c > > +++ b/arch/arm64/kvm/vgic/vgic-v5.c > [ ... ] > > + /* > > + * Request a doorbell *unless* the priority is 0, > > indicating > > + * that no interrupt can wake the CPU up. > > + */ > > + if (priority_mask) { > > [Severity: High] > Does skipping the doorbell request when the priority is 0 break vNMI > wakeups? > > Architectural semantics dictate that vNMIs have superpriority and > must wake > a vCPU even when the running priority is 0. KVM does not expose FEAT_NMI to a guest, so this is not an issue. > > > + int db_irq = vgic_v5_vpe_db(vcpu); > > + struct irq_data *d = > > irq_get_irq_data(db_irq); > > + const struct cpumask *aff = > > irq_data_get_effective_affinity_mask(d); > > + int cpu = smp_processor_id(); > > + > > + dbpm = priority_mask - 1; > > [Severity: High] > Does subtracting 1 from the priority mask drop valid preempting > interrupts? No. We discussed this last time. This is correct for GICv5, but wouldn't be for GICv3. > > The hardware doorbell condition triggers when the pending priority is > less > than the threshold. If the highest active priority is 5, the vCPU > should > be woken by any interrupt with a priority less than 5, for example, > 4. > > If the priority mask is 5, this code sets the DBPM to 4. The hardware > condition then becomes priority < 4. An interrupt with priority 4 > will > not signal the doorbell, despite having a higher priority than the > active > interrupt, causing missed wakeups and guest hangs. > > > + cpu_if->vgic_contextr = > > FIELD_PREP(ICH_CONTEXTR_EL2_DB, 1) | > > + FIELD_PREP(ICH_CON > > TEXTR_EL2_DBPM, dbpm); > [ ... ] > > diff --git a/arch/arm64/kvm/vgic/vgic.c > > b/arch/arm64/kvm/vgic/vgic.c > > --- a/arch/arm64/kvm/vgic/vgic.c > > +++ b/arch/arm64/kvm/vgic/vgic.c > [ ... ] > > @@ -1227,8 +1227,12 @@ int kvm_vgic_vcpu_pending_irq(struct > > kvm_vcpu *vcpu) > > unsigned long flags; > > struct vgic_vmcr vmcr; > > > > - if (vgic_is_v5(vcpu->kvm)) > > + if (vgic_is_v5(vcpu->kvm)) { > > + if (READ_ONCE(vcpu- > > >arch.vgic_cpu.vgic_v5.gicv5_vpe.db_fired)) > > + return true; > > [Severity: High] > Can a stale db_fired state cause an infinite loop bypassing WFI > sleep? > > The db_fired flag is cleared only in __vgic_v5_make_non_resident(), > which > executes during schedule-out. > > If a doorbell fires, db_fired becomes true and the vCPU wakes up to > enter > the guest. When the vCPU subsequently re-enters WFI without a > schedule-out, > kvm_vcpu_block() evaluates kvm_vgic_vcpu_pending_irq(). Because > db_fired > remains true, this instantly returns without sleeping. > > This leaves the vCPU in an infinite loop returning from WFI and > pegging > the host CPU at 100% until a host timer tick forces preemption. Entering WFI (when it is trapped to KVM) results in a vgic_v5_put() which makes the VPE non-resident. At this point, it would be cleared. In the event that we sleep in the guest, then we indeed won't call vgic_v5_put() until we schedule out - but that is by design. We've already chosen to sleep in the guest context at this point, but will return to the host on a scheduler tick, at which point we take whatever action is appropriate, such as making the VPE non-resident. > > > + > > return vgic_v5_has_pending_ppi(vcpu); > > + } > > > > if (!vcpu->kvm->arch.vgic.enabled) > > return false; > Thanks, Sascha