Skip to content

Commit 87e4951

Browse files
weizijiesean-jc
authored andcommitted
KVM: x86: Rescan I/O APIC routes after EOI interception for old routing
Rescan I/O APIC routes for a vCPU after handling an intercepted I/O APIC EOI for an IRQ that is not targeting said vCPU, i.e. after handling what's effectively a stale EOI VM-Exit. If a level-triggered IRQ is in-flight when IRQ routing changes, e.g. because the guest changes routing from its IRQ handler, then KVM intercepts EOIs on both the new and old target vCPUs, so that the in-flight IRQ can be de-asserted when it's EOI'd. However, only the EOI for the in-flight IRQ needs to be intercepted, as IRQs on the same vector with the new routing are coincidental, i.e. occur only if the guest is reusing the vector for multiple interrupt sources. If the I/O APIC routes aren't rescanned, KVM will unnecessarily intercept EOIs for the vector and negative impact the vCPU's interrupt performance. Note, both commit db2bdcb ("KVM: x86: fix edge EOI and IOAPIC reconfig race") and commit 0fc5a36 ("KVM: x86: ioapic: Fix level-triggered EOI and IOAPIC reconfigure race") mentioned this issue, but it was considered a "rare" occurrence thus was not addressed. However in real environments, this issue can happen even in a well-behaved guest. Cc: Kai Huang <[email protected]> Co-developed-by: xuyun <[email protected]> Signed-off-by: xuyun <[email protected]> Signed-off-by: weizijie <[email protected]> [sean: massage changelog and comments, use int/-1, reset at scan] Reviewed-by: Kai Huang <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Sean Christopherson <[email protected]>
1 parent c2207bb commit 87e4951

File tree

4 files changed

+24
-2
lines changed

4 files changed

+24
-2
lines changed

arch/x86/include/asm/kvm_host.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,6 +1034,7 @@ struct kvm_vcpu_arch {
10341034

10351035
int pending_ioapic_eoi;
10361036
int pending_external_vector;
1037+
int highest_stale_pending_ioapic_eoi;
10371038

10381039
/* be preempted when it's in kernel-mode(cpl=0) */
10391040
bool preempted_in_kernel;

arch/x86/kvm/irq_comm.c

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -412,9 +412,21 @@ void kvm_scan_ioapic_irq(struct kvm_vcpu *vcpu, u32 dest_id, u16 dest_mode,
412412
* level-triggered IRQ. The EOI needs to be intercepted and forwarded
413413
* to I/O APIC emulation so that the IRQ can be de-asserted.
414414
*/
415-
if (kvm_apic_match_dest(vcpu, NULL, APIC_DEST_NOSHORT, dest_id, dest_mode) ||
416-
kvm_apic_pending_eoi(vcpu, vector))
415+
if (kvm_apic_match_dest(vcpu, NULL, APIC_DEST_NOSHORT, dest_id, dest_mode)) {
417416
__set_bit(vector, ioapic_handled_vectors);
417+
} else if (kvm_apic_pending_eoi(vcpu, vector)) {
418+
__set_bit(vector, ioapic_handled_vectors);
419+
420+
/*
421+
* Track the highest pending EOI for which the vCPU is NOT the
422+
* target in the new routing. Only the EOI for the IRQ that is
423+
* in-flight (for the old routing) needs to be intercepted, any
424+
* future IRQs that arrive on this vCPU will be coincidental to
425+
* the level-triggered routing and don't need to be intercepted.
426+
*/
427+
if ((int)vector > vcpu->arch.highest_stale_pending_ioapic_eoi)
428+
vcpu->arch.highest_stale_pending_ioapic_eoi = vector;
429+
}
418430
}
419431

420432
void kvm_scan_ioapic_routes(struct kvm_vcpu *vcpu,

arch/x86/kvm/lapic.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1459,6 +1459,14 @@ static void kvm_ioapic_send_eoi(struct kvm_lapic *apic, int vector)
14591459
if (!kvm_ioapic_handles_vector(apic, vector))
14601460
return;
14611461

1462+
/*
1463+
* If the intercepted EOI is for an IRQ that was pending from previous
1464+
* routing, then re-scan the I/O APIC routes as EOIs for the IRQ likely
1465+
* no longer need to be intercepted.
1466+
*/
1467+
if (apic->vcpu->arch.highest_stale_pending_ioapic_eoi == vector)
1468+
kvm_make_request(KVM_REQ_SCAN_IOAPIC, apic->vcpu);
1469+
14621470
/* Request a KVM exit to inform the userspace IOAPIC. */
14631471
if (irqchip_split(apic->vcpu->kvm)) {
14641472
apic->vcpu->arch.pending_ioapic_eoi = vector;

arch/x86/kvm/x86.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10692,6 +10692,7 @@ static void vcpu_scan_ioapic(struct kvm_vcpu *vcpu)
1069210692
return;
1069310693

1069410694
bitmap_zero(vcpu->arch.ioapic_handled_vectors, 256);
10695+
vcpu->arch.highest_stale_pending_ioapic_eoi = -1;
1069510696

1069610697
kvm_x86_call(sync_pir_to_irr)(vcpu);
1069710698

0 commit comments

Comments
 (0)