]> rtime.felk.cvut.cz Git - zynq/linux.git/blobdiff - arch/x86/kvm/lapic.c
KVM: lapic: mark LAPIC timer handler as irqsafe
[zynq/linux.git] / arch / x86 / kvm / lapic.c
index bd4e34de24c7a0860de0adf88d3b85fd94f8ddae..3feaefa5751e0b8d0c7762ee92f0704c5c3a6c5d 100644 (file)
@@ -833,8 +833,7 @@ int kvm_apic_compare_prio(struct kvm_vcpu *vcpu1, struct kvm_vcpu *vcpu2)
 
 static void kvm_ioapic_send_eoi(struct kvm_lapic *apic, int vector)
 {
-       if (!(kvm_apic_get_reg(apic, APIC_SPIV) & APIC_SPIV_DIRECTED_EOI) &&
-           kvm_ioapic_handles_vector(apic->vcpu->kvm, vector)) {
+       if (kvm_ioapic_handles_vector(apic->vcpu->kvm, vector)) {
                int trigger_mode;
                if (apic_test_vector(vector, apic->regs + APIC_TMR))
                        trigger_mode = IOAPIC_LEVEL_TRIG;
@@ -1065,6 +1064,17 @@ static void update_divide_count(struct kvm_lapic *apic)
                                   apic->divide_count);
 }
 
+static void apic_update_lvtt(struct kvm_lapic *apic)
+{
+       u32 timer_mode = kvm_apic_get_reg(apic, APIC_LVTT) &
+                       apic->lapic_timer.timer_mode_mask;
+
+       if (apic->lapic_timer.timer_mode != timer_mode) {
+               apic->lapic_timer.timer_mode = timer_mode;
+               hrtimer_cancel(&apic->lapic_timer.timer);
+       }
+}
+
 static void apic_timer_expired(struct kvm_lapic *apic)
 {
        struct kvm_vcpu *vcpu = apic->vcpu;
@@ -1131,8 +1141,36 @@ void wait_lapic_expire(struct kvm_vcpu *vcpu)
                __delay(tsc_deadline - guest_tsc);
 }
 
+static enum hrtimer_restart apic_timer_fn(struct hrtimer *data);
+
+static void __apic_timer_expired(struct hrtimer *data)
+{
+       int ret, i = 0;
+       enum hrtimer_restart r;
+       struct kvm_timer *ktimer = container_of(data, struct kvm_timer, timer);
+
+       r = apic_timer_fn(data);
+
+       if (r == HRTIMER_RESTART) {
+               do {
+                       ret = hrtimer_start_expires(data, HRTIMER_MODE_ABS);
+                       if (ret == -ETIME)
+                               hrtimer_add_expires_ns(&ktimer->timer,
+                                                      ktimer->period);
+                       i++;
+               } while (ret == -ETIME && i < 10);
+
+               if (ret == -ETIME) {
+                       printk_once(KERN_ERR "%s: failed to reprogram timer\n",
+                                   __func__);
+                       WARN_ON_ONCE(1);
+               }
+       }
+}
+
 static void start_apic_timer(struct kvm_lapic *apic)
 {
+       int ret;
        ktime_t now;
 
        atomic_set(&apic->lapic_timer.pending, 0);
@@ -1163,9 +1201,11 @@ static void start_apic_timer(struct kvm_lapic *apic)
                        }
                }
 
-               hrtimer_start(&apic->lapic_timer.timer,
+               ret = hrtimer_start(&apic->lapic_timer.timer,
                              ktime_add_ns(now, apic->lapic_timer.period),
                              HRTIMER_MODE_ABS);
+               if (ret == -ETIME)
+                       __apic_timer_expired(&apic->lapic_timer.timer);
 
                apic_debug("%s: bus cycle is %" PRId64 "ns, now 0x%016"
                           PRIx64 ", "
@@ -1197,8 +1237,10 @@ static void start_apic_timer(struct kvm_lapic *apic)
                        do_div(ns, this_tsc_khz);
                        expire = ktime_add_ns(now, ns);
                        expire = ktime_sub_ns(expire, lapic_timer_advance_ns);
-                       hrtimer_start(&apic->lapic_timer.timer,
+                       ret = hrtimer_start(&apic->lapic_timer.timer,
                                      expire, HRTIMER_MODE_ABS);
+                       if (ret == -ETIME)
+                               __apic_timer_expired(&apic->lapic_timer.timer);
                } else
                        apic_timer_expired(apic);
 
@@ -1214,10 +1256,10 @@ static void apic_manage_nmi_watchdog(struct kvm_lapic *apic, u32 lvt0_val)
                if (!nmi_wd_enabled) {
                        apic_debug("Receive NMI setting on APIC_LVT0 "
                                   "for cpu %d\n", apic->vcpu->vcpu_id);
-                       apic->vcpu->kvm->arch.vapics_in_nmi_mode++;
+                       atomic_inc(&apic->vcpu->kvm->arch.vapics_in_nmi_mode);
                }
        } else if (nmi_wd_enabled)
-               apic->vcpu->kvm->arch.vapics_in_nmi_mode--;
+               atomic_dec(&apic->vcpu->kvm->arch.vapics_in_nmi_mode);
 }
 
 static int apic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)
@@ -1273,6 +1315,7 @@ static int apic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)
                                apic_set_reg(apic, APIC_LVTT + 0x10 * i,
                                             lvt_val | APIC_LVT_MASKED);
                        }
+                       apic_update_lvtt(apic);
                        atomic_set(&apic->lapic_timer.pending, 0);
 
                }
@@ -1305,20 +1348,13 @@ static int apic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)
 
                break;
 
-       case APIC_LVTT: {
-               u32 timer_mode = val & apic->lapic_timer.timer_mode_mask;
-
-               if (apic->lapic_timer.timer_mode != timer_mode) {
-                       apic->lapic_timer.timer_mode = timer_mode;
-                       hrtimer_cancel(&apic->lapic_timer.timer);
-               }
-
+       case APIC_LVTT:
                if (!kvm_apic_sw_enabled(apic))
                        val |= APIC_LVT_MASKED;
                val &= (apic_lvt_mask[0] | apic->lapic_timer.timer_mode_mask);
                apic_set_reg(apic, APIC_LVTT, val);
+               apic_update_lvtt(apic);
                break;
-       }
 
        case APIC_TMICT:
                if (apic_lvtt_tscdeadline(apic))
@@ -1553,7 +1589,7 @@ void kvm_lapic_reset(struct kvm_vcpu *vcpu)
 
        for (i = 0; i < APIC_LVT_NUM; i++)
                apic_set_reg(apic, APIC_LVTT + 0x10 * i, APIC_LVT_MASKED);
-       apic->lapic_timer.timer_mode = 0;
+       apic_update_lvtt(apic);
        apic_set_reg(apic, APIC_LVT0,
                     SET_APIC_DELIVERY_MODE(0, APIC_MODE_EXTINT));
 
@@ -1679,6 +1715,7 @@ int kvm_create_lapic(struct kvm_vcpu *vcpu)
        hrtimer_init(&apic->lapic_timer.timer, CLOCK_MONOTONIC,
                     HRTIMER_MODE_ABS);
        apic->lapic_timer.timer.function = apic_timer_fn;
+       apic->lapic_timer.timer.irqsafe = 1;
 
        /*
         * APIC is created enabled. This will prevent kvm_lapic_set_base from
@@ -1779,6 +1816,8 @@ void kvm_apic_post_state_restore(struct kvm_vcpu *vcpu,
 
        apic_update_ppr(apic);
        hrtimer_cancel(&apic->lapic_timer.timer);
+       apic_update_lvtt(apic);
+       apic_manage_nmi_watchdog(apic, kvm_apic_get_reg(apic, APIC_LVT0));
        update_divide_count(apic);
        start_apic_timer(apic);
        apic->irr_pending = true;
@@ -1804,7 +1843,8 @@ void __kvm_migrate_apic_timer(struct kvm_vcpu *vcpu)
 
        timer = &vcpu->arch.apic->lapic_timer.timer;
        if (hrtimer_cancel(timer))
-               hrtimer_start_expires(timer, HRTIMER_MODE_ABS);
+               if (hrtimer_start_expires(timer, HRTIMER_MODE_ABS) == -ETIME)
+                       __apic_timer_expired(timer);
 }
 
 /*