|
@@ -1456,6 +1456,30 @@ static void start_sw_period(struct kvm_lapic *apic)
|
|
|
HRTIMER_MODE_ABS_PINNED);
|
|
|
}
|
|
|
|
|
|
+static void update_target_expiration(struct kvm_lapic *apic, uint32_t old_divisor)
|
|
|
+{
|
|
|
+ ktime_t now, remaining;
|
|
|
+ u64 ns_remaining_old, ns_remaining_new;
|
|
|
+
|
|
|
+ apic->lapic_timer.period = (u64)kvm_lapic_get_reg(apic, APIC_TMICT)
|
|
|
+ * APIC_BUS_CYCLE_NS * apic->divide_count;
|
|
|
+ limit_periodic_timer_frequency(apic);
|
|
|
+
|
|
|
+ now = ktime_get();
|
|
|
+ remaining = ktime_sub(apic->lapic_timer.target_expiration, now);
|
|
|
+ if (ktime_to_ns(remaining) < 0)
|
|
|
+ remaining = 0;
|
|
|
+
|
|
|
+ ns_remaining_old = ktime_to_ns(remaining);
|
|
|
+ ns_remaining_new = mul_u64_u32_div(ns_remaining_old,
|
|
|
+ apic->divide_count, old_divisor);
|
|
|
+
|
|
|
+ apic->lapic_timer.tscdeadline +=
|
|
|
+ nsec_to_cycles(apic->vcpu, ns_remaining_new) -
|
|
|
+ nsec_to_cycles(apic->vcpu, ns_remaining_old);
|
|
|
+ apic->lapic_timer.target_expiration = ktime_add_ns(now, ns_remaining_new);
|
|
|
+}
|
|
|
+
|
|
|
static bool set_target_expiration(struct kvm_lapic *apic)
|
|
|
{
|
|
|
ktime_t now;
|
|
@@ -1748,13 +1772,21 @@ int kvm_lapic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)
|
|
|
start_apic_timer(apic);
|
|
|
break;
|
|
|
|
|
|
- case APIC_TDCR:
|
|
|
+ case APIC_TDCR: {
|
|
|
+ uint32_t old_divisor = apic->divide_count;
|
|
|
+
|
|
|
if (val & 4)
|
|
|
apic_debug("KVM_WRITE:TDCR %x\n", val);
|
|
|
kvm_lapic_set_reg(apic, APIC_TDCR, val);
|
|
|
update_divide_count(apic);
|
|
|
+ if (apic->divide_count != old_divisor &&
|
|
|
+ apic->lapic_timer.period) {
|
|
|
+ hrtimer_cancel(&apic->lapic_timer.timer);
|
|
|
+ update_target_expiration(apic, old_divisor);
|
|
|
+ restart_apic_timer(apic);
|
|
|
+ }
|
|
|
break;
|
|
|
-
|
|
|
+ }
|
|
|
case APIC_ESR:
|
|
|
if (apic_x2apic_mode(apic) && val != 0) {
|
|
|
apic_debug("KVM_WRITE:ESR not zero %x\n", val);
|