Merge tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm
[linux-2.6-microblaze.git] / arch / x86 / kvm / lapic.c
index bd13fdd..4924f83 100644 (file)
@@ -1454,7 +1454,7 @@ static void apic_timer_expired(struct kvm_lapic *apic)
        if (swait_active(q))
                swake_up_one(q);
 
-       if (apic_lvtt_tscdeadline(apic))
+       if (apic_lvtt_tscdeadline(apic) || ktimer->hv_timer_in_use)
                ktimer->expired_tscdeadline = ktimer->tscdeadline;
 }
 
@@ -1696,37 +1696,42 @@ static void cancel_hv_timer(struct kvm_lapic *apic)
 static bool start_hv_timer(struct kvm_lapic *apic)
 {
        struct kvm_timer *ktimer = &apic->lapic_timer;
-       int r;
+       struct kvm_vcpu *vcpu = apic->vcpu;
+       bool expired;
 
        WARN_ON(preemptible());
        if (!kvm_x86_ops->set_hv_timer)
                return false;
 
-       if (!apic_lvtt_period(apic) && atomic_read(&ktimer->pending))
-               return false;
-
        if (!ktimer->tscdeadline)
                return false;
 
-       r = kvm_x86_ops->set_hv_timer(apic->vcpu, ktimer->tscdeadline);
-       if (r < 0)
+       if (kvm_x86_ops->set_hv_timer(vcpu, ktimer->tscdeadline, &expired))
                return false;
 
        ktimer->hv_timer_in_use = true;
        hrtimer_cancel(&ktimer->timer);
 
        /*
-        * Also recheck ktimer->pending, in case the sw timer triggered in
-        * the window.  For periodic timer, leave the hv timer running for
-        * simplicity, and the deadline will be recomputed on the next vmexit.
+        * To simplify handling the periodic timer, leave the hv timer running
+        * even if the deadline timer has expired, i.e. rely on the resulting
+        * VM-Exit to recompute the periodic timer's target expiration.
         */
-       if (!apic_lvtt_period(apic) && (r || atomic_read(&ktimer->pending))) {
-               if (r)
+       if (!apic_lvtt_period(apic)) {
+               /*
+                * Cancel the hv timer if the sw timer fired while the hv timer
+                * was being programmed, or if the hv timer itself expired.
+                */
+               if (atomic_read(&ktimer->pending)) {
+                       cancel_hv_timer(apic);
+               } else if (expired) {
                        apic_timer_expired(apic);
-               return false;
+                       cancel_hv_timer(apic);
+               }
        }
 
-       trace_kvm_hv_timer_state(apic->vcpu->vcpu_id, true);
+       trace_kvm_hv_timer_state(vcpu->vcpu_id, ktimer->hv_timer_in_use);
+
        return true;
 }
 
@@ -1750,8 +1755,13 @@ static void start_sw_timer(struct kvm_lapic *apic)
 static void restart_apic_timer(struct kvm_lapic *apic)
 {
        preempt_disable();
+
+       if (!apic_lvtt_period(apic) && atomic_read(&apic->lapic_timer.pending))
+               goto out;
+
        if (!start_hv_timer(apic))
                start_sw_timer(apic);
+out:
        preempt_enable();
 }