aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kvm
diff options
context:
space:
mode:
authorPaolo Bonzini <pbonzini@redhat.com>2014-05-14 11:40:58 -0400
committerPaolo Bonzini <pbonzini@redhat.com>2014-05-27 04:21:09 -0400
commitfc57ac2c9ca8109ea97fcc594f4be436944230cc (patch)
treea5147edd2bd218899d250860b5a3df199e3967a4 /arch/x86/kvm
parent1f854112553a1d65363ab27d4ee3dfb4b27075fb (diff)
KVM: lapic: sync highest ISR to hardware apic on EOI
When Hyper-V enlightenments are in effect, Windows prefers to issue an Hyper-V MSR write to issue an EOI rather than an x2apic MSR write. The Hyper-V MSR write is not handled by the processor, and besides being slower, this also causes bugs with APIC virtualization. The reason is that on EOI the processor will modify the highest in-service interrupt (SVI) field of the VMCS, as explained in section 29.1.4 of the SDM; every other step in EOI virtualization is already done by apic_send_eoi or on VM entry, but this one is missing. We need to do the same, and be careful not to muck with the isr_count and highest_isr_cache fields that are unused when virtual interrupt delivery is enabled. Cc: stable@vger.kernel.org Reviewed-by: Yang Zhang <yang.z.zhang@intel.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'arch/x86/kvm')
-rw-r--r--arch/x86/kvm/lapic.c62
1 files changed, 43 insertions, 19 deletions
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index 9736529ade08..006911858174 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -360,6 +360,8 @@ static inline void apic_clear_irr(int vec, struct kvm_lapic *apic)
360 360
361static inline void apic_set_isr(int vec, struct kvm_lapic *apic) 361static inline void apic_set_isr(int vec, struct kvm_lapic *apic)
362{ 362{
363 /* Note that we never get here with APIC virtualization enabled. */
364
363 if (!__apic_test_and_set_vector(vec, apic->regs + APIC_ISR)) 365 if (!__apic_test_and_set_vector(vec, apic->regs + APIC_ISR))
364 ++apic->isr_count; 366 ++apic->isr_count;
365 BUG_ON(apic->isr_count > MAX_APIC_VECTOR); 367 BUG_ON(apic->isr_count > MAX_APIC_VECTOR);
@@ -371,12 +373,48 @@ static inline void apic_set_isr(int vec, struct kvm_lapic *apic)
371 apic->highest_isr_cache = vec; 373 apic->highest_isr_cache = vec;
372} 374}
373 375
376static inline int apic_find_highest_isr(struct kvm_lapic *apic)
377{
378 int result;
379
380 /*
381 * Note that isr_count is always 1, and highest_isr_cache
382 * is always -1, with APIC virtualization enabled.
383 */
384 if (!apic->isr_count)
385 return -1;
386 if (likely(apic->highest_isr_cache != -1))
387 return apic->highest_isr_cache;
388
389 result = find_highest_vector(apic->regs + APIC_ISR);
390 ASSERT(result == -1 || result >= 16);
391
392 return result;
393}
394
374static inline void apic_clear_isr(int vec, struct kvm_lapic *apic) 395static inline void apic_clear_isr(int vec, struct kvm_lapic *apic)
375{ 396{
376 if (__apic_test_and_clear_vector(vec, apic->regs + APIC_ISR)) 397 struct kvm_vcpu *vcpu;
398 if (!__apic_test_and_clear_vector(vec, apic->regs + APIC_ISR))
399 return;
400
401 vcpu = apic->vcpu;
402
403 /*
404 * We do get here for APIC virtualization enabled if the guest
405 * uses the Hyper-V APIC enlightenment. In this case we may need
406 * to trigger a new interrupt delivery by writing the SVI field;
407 * on the other hand isr_count and highest_isr_cache are unused
408 * and must be left alone.
409 */
410 if (unlikely(kvm_apic_vid_enabled(vcpu->kvm)))
411 kvm_x86_ops->hwapic_isr_update(vcpu->kvm,
412 apic_find_highest_isr(apic));
413 else {
377 --apic->isr_count; 414 --apic->isr_count;
378 BUG_ON(apic->isr_count < 0); 415 BUG_ON(apic->isr_count < 0);
379 apic->highest_isr_cache = -1; 416 apic->highest_isr_cache = -1;
417 }
380} 418}
381 419
382int kvm_lapic_find_highest_irr(struct kvm_vcpu *vcpu) 420int kvm_lapic_find_highest_irr(struct kvm_vcpu *vcpu)
@@ -456,22 +494,6 @@ static void pv_eoi_clr_pending(struct kvm_vcpu *vcpu)
456 __clear_bit(KVM_APIC_PV_EOI_PENDING, &vcpu->arch.apic_attention); 494 __clear_bit(KVM_APIC_PV_EOI_PENDING, &vcpu->arch.apic_attention);
457} 495}
458 496
459static inline int apic_find_highest_isr(struct kvm_lapic *apic)
460{
461 int result;
462
463 /* Note that isr_count is always 1 with vid enabled */
464 if (!apic->isr_count)
465 return -1;
466 if (likely(apic->highest_isr_cache != -1))
467 return apic->highest_isr_cache;
468
469 result = find_highest_vector(apic->regs + APIC_ISR);
470 ASSERT(result == -1 || result >= 16);
471
472 return result;
473}
474
475void kvm_apic_update_tmr(struct kvm_vcpu *vcpu, u32 *tmr) 497void kvm_apic_update_tmr(struct kvm_vcpu *vcpu, u32 *tmr)
476{ 498{
477 struct kvm_lapic *apic = vcpu->arch.apic; 499 struct kvm_lapic *apic = vcpu->arch.apic;
@@ -1605,6 +1627,8 @@ int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu)
1605 int vector = kvm_apic_has_interrupt(vcpu); 1627 int vector = kvm_apic_has_interrupt(vcpu);
1606 struct kvm_lapic *apic = vcpu->arch.apic; 1628 struct kvm_lapic *apic = vcpu->arch.apic;
1607 1629
1630 /* Note that we never get here with APIC virtualization enabled. */
1631
1608 if (vector == -1) 1632 if (vector == -1)
1609 return -1; 1633 return -1;
1610 1634