aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kvm/lapic.c
diff options
context:
space:
mode:
authorNadav Amit <namit@cs.technion.ac.il>2014-11-16 16:49:07 -0500
committerPaolo Bonzini <pbonzini@redhat.com>2014-11-17 06:16:20 -0500
commitf210f7572bedf3320599e8b2d8e8ec2d96270d0b (patch)
treec0ed2fb0c2a33ad0afeb55e321bdf41a75be67b8 /arch/x86/kvm/lapic.c
parenta3e339e1cec899908f516a4ebde64cac500b0c45 (diff)
KVM: x86: Fix lost interrupt on irr_pending race
apic_find_highest_irr assumes irr_pending is set if any vector in APIC_IRR is set. If this assumption is broken and apicv is disabled, the injection of interrupts may be deferred until another interrupt is delivered to the guest. Ultimately, if no other interrupt should be injected to that vCPU, the pending interrupt may be lost. commit 56cc2406d68c ("KVM: nVMX: fix "acknowledge interrupt on exit" when APICv is in use") changed the behavior of apic_clear_irr so irr_pending is cleared after setting APIC_IRR vector. After this commit, if apic_set_irr and apic_clear_irr run simultaneously, a race may occur, resulting in APIC_IRR vector set, and irr_pending cleared. In the following example, assume a single vector is set in IRR prior to calling apic_clear_irr: apic_set_irr apic_clear_irr ------------ -------------- apic->irr_pending = true; apic_clear_vector(...); vec = apic_search_irr(apic); // => vec == -1 apic_set_vector(...); apic->irr_pending = (vec != -1); // => apic->irr_pending == false Nonetheless, it appears the race might even occur prior to this commit: apic_set_irr apic_clear_irr ------------ -------------- apic->irr_pending = true; apic->irr_pending = false; apic_clear_vector(...); if (apic_search_irr(apic) != -1) apic->irr_pending = true; // => apic->irr_pending == false apic_set_vector(...); Fixing this issue by: 1. Restoring the previous behavior of apic_clear_irr: clear irr_pending, call apic_clear_vector, and then if APIC_IRR is non-zero, set irr_pending. 2. On apic_set_irr: first call apic_set_vector, then set irr_pending. Signed-off-by: Nadav Amit <namit@cs.technion.ac.il> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'arch/x86/kvm/lapic.c')
-rw-r--r--arch/x86/kvm/lapic.c18
1 files changed, 12 insertions, 6 deletions
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index 6e8ce5a1a05d..e0e5642dae41 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -341,8 +341,12 @@ EXPORT_SYMBOL_GPL(kvm_apic_update_irr);
341 341
342static inline void apic_set_irr(int vec, struct kvm_lapic *apic) 342static inline void apic_set_irr(int vec, struct kvm_lapic *apic)
343{ 343{
344 apic->irr_pending = true;
345 apic_set_vector(vec, apic->regs + APIC_IRR); 344 apic_set_vector(vec, apic->regs + APIC_IRR);
345 /*
346 * irr_pending must be true if any interrupt is pending; set it after
347 * APIC_IRR to avoid race with apic_clear_irr
348 */
349 apic->irr_pending = true;
346} 350}
347 351
348static inline int apic_search_irr(struct kvm_lapic *apic) 352static inline int apic_search_irr(struct kvm_lapic *apic)
@@ -374,13 +378,15 @@ static inline void apic_clear_irr(int vec, struct kvm_lapic *apic)
374 378
375 vcpu = apic->vcpu; 379 vcpu = apic->vcpu;
376 380
377 apic_clear_vector(vec, apic->regs + APIC_IRR); 381 if (unlikely(kvm_apic_vid_enabled(vcpu->kvm))) {
378 if (unlikely(kvm_apic_vid_enabled(vcpu->kvm)))
379 /* try to update RVI */ 382 /* try to update RVI */
383 apic_clear_vector(vec, apic->regs + APIC_IRR);
380 kvm_make_request(KVM_REQ_EVENT, vcpu); 384 kvm_make_request(KVM_REQ_EVENT, vcpu);
381 else { 385 } else {
382 vec = apic_search_irr(apic); 386 apic->irr_pending = false;
383 apic->irr_pending = (vec != -1); 387 apic_clear_vector(vec, apic->regs + APIC_IRR);
388 if (apic_search_irr(apic) != -1)
389 apic->irr_pending = true;
384 } 390 }
385} 391}
386 392