[PATCH] KVM: arm64: Skip interrupts in LRs during EOIcount replay
From: Valentine Burley
Date: Sat Mar 07 2026 - 07:02:49 EST
Commit 05984ba67eb6 ("KVM: arm64: Invert ap_list sorting to push active
interrupts out") allowed active interrupts to be evicted from LRs to
make room for pending ones.
When an evicted interrupt is deactivated by the guest, the GIC
increments EOIcount. KVM replays this by finding an active interrupt
in the ap_list to deactivate. However, the replay logic may pick an
interrupt that is currently residing in an LR, leading to a spurious
deactivation and leaving the actually finished interrupt active.
This results in interrupt storms and boot failures (seen in Cuttlefish
guests on Qualcomm SC7180).
Fix this by skipping interrupts that are currently assigned to an LR
during EOIcount replay.
This allows booting Android VMs in Cuttlefish again.
Fixes: 05984ba67eb6 ("KVM: arm64: Invert ap_list sorting to push active interrupts out")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Valentine Burley <valentine.burley@xxxxxxxxxxxxx>
---
arch/arm64/kvm/vgic/vgic-v2.c | 14 ++++++++++++++
arch/arm64/kvm/vgic/vgic-v3.c | 19 +++++++++++++++++++
2 files changed, 33 insertions(+)
diff --git a/arch/arm64/kvm/vgic/vgic-v2.c b/arch/arm64/kvm/vgic/vgic-v2.c
index 585491fbda80..821cf5bc30da 100644
--- a/arch/arm64/kvm/vgic/vgic-v2.c
+++ b/arch/arm64/kvm/vgic/vgic-v2.c
@@ -135,6 +135,20 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu)
irq->active))
continue;
+ bool was_in_lr = false;
+
+ for (int i = 0; i < cpuif->used_lrs; i++) {
+ u32 intid = cpuif->vgic_lr[i] & GICH_LR_VIRTUALID;
+
+ if (intid == irq->intid) {
+ was_in_lr = true;
+ break;
+ }
+ }
+
+ if (was_in_lr)
+ continue;
+
lr = vgic_v2_compute_lr(vcpu, irq) & ~GICH_LR_ACTIVE_BIT;
}
diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
index 1d6dd1b545bd..00d9bc39bffb 100644
--- a/arch/arm64/kvm/vgic/vgic-v3.c
+++ b/arch/arm64/kvm/vgic/vgic-v3.c
@@ -179,6 +179,25 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
irq->active))
continue;
+ bool was_in_lr = false;
+
+ for (int i = 0; i < cpuif->used_lrs; i++) {
+ u32 intid;
+
+ if (vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3)
+ intid = cpuif->vgic_lr[i] & ICH_LR_VIRTUAL_ID_MASK;
+ else
+ intid = cpuif->vgic_lr[i] & GICH_LR_VIRTUALID;
+
+ if (intid == irq->intid) {
+ was_in_lr = true;
+ break;
+ }
+ }
+
+ if (was_in_lr)
+ continue;
+
lr = vgic_v3_compute_lr(vcpu, irq) & ~ICH_LR_ACTIVE_BIT;
}
--
2.51.0