Skip to content

Commit 6da5e53

Browse files
author
Marc Zyngier
committed
KVM: arm64: vgic: Pick EOIcount deactivations from AP-list tail
Valentine reports that their guests fail to boot correctly, losing interrupts, and indicates that the wrong interrupt gets deactivated. What happens here is that if the maintenance interrupt is slow enough to kick us out of the guest, extra interrupts can be activated from the LRs. We then exit and proceed to handle EOIcount deactivations, picking active interrupts from the AP list. But we start from the top of the list, potentially deactivating interrupts that were in the LRs, while EOIcount only denotes deactivation of interrupts that are not present in an LR. Solve this by tracking the last interrupt that made it in the LRs, and start the EOIcount deactivation walk *after* that interrupt. Since this only makes sense while the vcpu is loaded, stash this in the per-CPU host state. Huge thanks to Valentine for doing all the detective work and providing an initial patch. Fixes: 3cfd59f ("KVM: arm64: GICv3: Handle LR overflow when EOImode==0") Fixes: 281c6c0 ("KVM: arm64: GICv2: Handle LR overflow when EOImode==0") Reported-by: Valentine Burley <valentine.burley@collabora.com> Tested-by: Valentine Burley <valentine.burley@collabora.com> Signed-off-by: Marc Zyngier <maz@kernel.org> Link: https://lore.kernel.org/r/20260307115955.369455-1-valentine.burley@collabora.com Link: https://patch.msgid.link/20260307191151.3781182-1-maz@kernel.org Cc: stable@vger.kernel.org
1 parent 3599c71 commit 6da5e53

4 files changed

Lines changed: 17 additions & 8 deletions

File tree

arch/arm64/include/asm/kvm_host.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -784,6 +784,9 @@ struct kvm_host_data {
784784
/* Number of debug breakpoints/watchpoints for this CPU (minus 1) */
785785
unsigned int debug_brps;
786786
unsigned int debug_wrps;
787+
788+
/* Last vgic_irq part of the AP list recorded in an LR */
789+
struct vgic_irq *last_lr_irq;
787790
};
788791

789792
struct kvm_host_psci_config {

arch/arm64/kvm/vgic/vgic-v2.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,15 +115,15 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu)
115115
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
116116
struct vgic_v2_cpu_if *cpuif = &vgic_cpu->vgic_v2;
117117
u32 eoicount = FIELD_GET(GICH_HCR_EOICOUNT, cpuif->vgic_hcr);
118-
struct vgic_irq *irq;
118+
struct vgic_irq *irq = *host_data_ptr(last_lr_irq);
119119

120120
DEBUG_SPINLOCK_BUG_ON(!irqs_disabled());
121121

122122
for (int lr = 0; lr < vgic_cpu->vgic_v2.used_lrs; lr++)
123123
vgic_v2_fold_lr(vcpu, cpuif->vgic_lr[lr]);
124124

125125
/* See the GICv3 equivalent for the EOIcount handling rationale */
126-
list_for_each_entry(irq, &vgic_cpu->ap_list_head, ap_list) {
126+
list_for_each_entry_continue(irq, &vgic_cpu->ap_list_head, ap_list) {
127127
u32 lr;
128128

129129
if (!eoicount) {

arch/arm64/kvm/vgic/vgic-v3.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
148148
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
149149
struct vgic_v3_cpu_if *cpuif = &vgic_cpu->vgic_v3;
150150
u32 eoicount = FIELD_GET(ICH_HCR_EL2_EOIcount, cpuif->vgic_hcr);
151-
struct vgic_irq *irq;
151+
struct vgic_irq *irq = *host_data_ptr(last_lr_irq);
152152

153153
DEBUG_SPINLOCK_BUG_ON(!irqs_disabled());
154154

@@ -158,12 +158,12 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
158158
/*
159159
* EOIMode=0: use EOIcount to emulate deactivation. We are
160160
* guaranteed to deactivate in reverse order of the activation, so
161-
* just pick one active interrupt after the other in the ap_list,
162-
* and replay the deactivation as if the CPU was doing it. We also
163-
* rely on priority drop to have taken place, and the list to be
164-
* sorted by priority.
161+
* just pick one active interrupt after the other in the tail part
162+
* of the ap_list, past the LRs, and replay the deactivation as if
163+
* the CPU was doing it. We also rely on priority drop to have taken
164+
* place, and the list to be sorted by priority.
165165
*/
166-
list_for_each_entry(irq, &vgic_cpu->ap_list_head, ap_list) {
166+
list_for_each_entry_continue(irq, &vgic_cpu->ap_list_head, ap_list) {
167167
u64 lr;
168168

169169
/*

arch/arm64/kvm/vgic/vgic.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -814,6 +814,9 @@ static void vgic_prune_ap_list(struct kvm_vcpu *vcpu)
814814

815815
static inline void vgic_fold_lr_state(struct kvm_vcpu *vcpu)
816816
{
817+
if (!*host_data_ptr(last_lr_irq))
818+
return;
819+
817820
if (kvm_vgic_global_state.type == VGIC_V2)
818821
vgic_v2_fold_lr_state(vcpu);
819822
else
@@ -960,10 +963,13 @@ static void vgic_flush_lr_state(struct kvm_vcpu *vcpu)
960963
if (irqs_outside_lrs(&als))
961964
vgic_sort_ap_list(vcpu);
962965

966+
*host_data_ptr(last_lr_irq) = NULL;
967+
963968
list_for_each_entry(irq, &vgic_cpu->ap_list_head, ap_list) {
964969
scoped_guard(raw_spinlock, &irq->irq_lock) {
965970
if (likely(vgic_target_oracle(irq) == vcpu)) {
966971
vgic_populate_lr(vcpu, irq, count++);
972+
*host_data_ptr(last_lr_irq) = irq;
967973
}
968974
}
969975

0 commit comments

Comments
 (0)