KVM: lapic: sync highest ISR to hardware apic on EOI
[profile/ivi/kernel-x86-ivi.git] / arch / x86 / kvm / lapic.c
index 9736529..0069118 100644 (file)
@@ -360,6 +360,8 @@ static inline void apic_clear_irr(int vec, struct kvm_lapic *apic)
 
 static inline void apic_set_isr(int vec, struct kvm_lapic *apic)
 {
+       /* Note that we never get here with APIC virtualization enabled.  */
+
        if (!__apic_test_and_set_vector(vec, apic->regs + APIC_ISR))
                ++apic->isr_count;
        BUG_ON(apic->isr_count > MAX_APIC_VECTOR);
@@ -371,12 +373,48 @@ static inline void apic_set_isr(int vec, struct kvm_lapic *apic)
        apic->highest_isr_cache = vec;
 }
 
+static inline int apic_find_highest_isr(struct kvm_lapic *apic)
+{
+       int result;
+
+       /*
+        * Note that isr_count is always 1, and highest_isr_cache
+        * is always -1, with APIC virtualization enabled.
+        */
+       if (!apic->isr_count)
+               return -1;
+       if (likely(apic->highest_isr_cache != -1))
+               return apic->highest_isr_cache;
+
+       result = find_highest_vector(apic->regs + APIC_ISR);
+       ASSERT(result == -1 || result >= 16);
+
+       return result;
+}
+
 static inline void apic_clear_isr(int vec, struct kvm_lapic *apic)
 {
-       if (__apic_test_and_clear_vector(vec, apic->regs + APIC_ISR))
+       struct kvm_vcpu *vcpu;
+       if (!__apic_test_and_clear_vector(vec, apic->regs + APIC_ISR))
+               return;
+
+       vcpu = apic->vcpu;
+
+       /*
+        * We do get here for APIC virtualization enabled if the guest
+        * uses the Hyper-V APIC enlightenment.  In this case we may need
+        * to trigger a new interrupt delivery by writing the SVI field;
+        * on the other hand isr_count and highest_isr_cache are unused
+        * and must be left alone.
+        */
+       if (unlikely(kvm_apic_vid_enabled(vcpu->kvm)))
+               kvm_x86_ops->hwapic_isr_update(vcpu->kvm,
+                                              apic_find_highest_isr(apic));
+       else {
                --apic->isr_count;
-       BUG_ON(apic->isr_count < 0);
-       apic->highest_isr_cache = -1;
+               BUG_ON(apic->isr_count < 0);
+               apic->highest_isr_cache = -1;
+       }
 }
 
 int kvm_lapic_find_highest_irr(struct kvm_vcpu *vcpu)
@@ -456,22 +494,6 @@ static void pv_eoi_clr_pending(struct kvm_vcpu *vcpu)
        __clear_bit(KVM_APIC_PV_EOI_PENDING, &vcpu->arch.apic_attention);
 }
 
-static inline int apic_find_highest_isr(struct kvm_lapic *apic)
-{
-       int result;
-
-       /* Note that isr_count is always 1 with vid enabled */
-       if (!apic->isr_count)
-               return -1;
-       if (likely(apic->highest_isr_cache != -1))
-               return apic->highest_isr_cache;
-
-       result = find_highest_vector(apic->regs + APIC_ISR);
-       ASSERT(result == -1 || result >= 16);
-
-       return result;
-}
-
 void kvm_apic_update_tmr(struct kvm_vcpu *vcpu, u32 *tmr)
 {
        struct kvm_lapic *apic = vcpu->arch.apic;
@@ -1605,6 +1627,8 @@ int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu)
        int vector = kvm_apic_has_interrupt(vcpu);
        struct kvm_lapic *apic = vcpu->arch.apic;
 
+       /* Note that we never get here with APIC virtualization enabled.  */
+
        if (vector == -1)
                return -1;