diff options
Diffstat (limited to 'arch/x86/kvm/lapic.c')
-rw-r--r-- | arch/x86/kvm/lapic.c | 91 |
1 files changed, 70 insertions, 21 deletions
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index 36c90d631096..943acbf00c69 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c | |||
@@ -1301,14 +1301,42 @@ static void update_divide_count(struct kvm_lapic *apic) | |||
1301 | apic->divide_count); | 1301 | apic->divide_count); |
1302 | } | 1302 | } |
1303 | 1303 | ||
1304 | static void limit_periodic_timer_frequency(struct kvm_lapic *apic) | ||
1305 | { | ||
1306 | /* | ||
1307 | * Do not allow the guest to program periodic timers with small | ||
1308 | * interval, since the hrtimers are not throttled by the host | ||
1309 | * scheduler. | ||
1310 | */ | ||
1311 | if (apic_lvtt_period(apic) && apic->lapic_timer.period) { | ||
1312 | s64 min_period = min_timer_period_us * 1000LL; | ||
1313 | |||
1314 | if (apic->lapic_timer.period < min_period) { | ||
1315 | pr_info_ratelimited( | ||
1316 | "kvm: vcpu %i: requested %lld ns " | ||
1317 | "lapic timer period limited to %lld ns\n", | ||
1318 | apic->vcpu->vcpu_id, | ||
1319 | apic->lapic_timer.period, min_period); | ||
1320 | apic->lapic_timer.period = min_period; | ||
1321 | } | ||
1322 | } | ||
1323 | } | ||
1324 | |||
1304 | static void apic_update_lvtt(struct kvm_lapic *apic) | 1325 | static void apic_update_lvtt(struct kvm_lapic *apic) |
1305 | { | 1326 | { |
1306 | u32 timer_mode = kvm_lapic_get_reg(apic, APIC_LVTT) & | 1327 | u32 timer_mode = kvm_lapic_get_reg(apic, APIC_LVTT) & |
1307 | apic->lapic_timer.timer_mode_mask; | 1328 | apic->lapic_timer.timer_mode_mask; |
1308 | 1329 | ||
1309 | if (apic->lapic_timer.timer_mode != timer_mode) { | 1330 | if (apic->lapic_timer.timer_mode != timer_mode) { |
1331 | if (apic_lvtt_tscdeadline(apic) != (timer_mode == | ||
1332 | APIC_LVT_TIMER_TSCDEADLINE)) { | ||
1333 | hrtimer_cancel(&apic->lapic_timer.timer); | ||
1334 | kvm_lapic_set_reg(apic, APIC_TMICT, 0); | ||
1335 | apic->lapic_timer.period = 0; | ||
1336 | apic->lapic_timer.tscdeadline = 0; | ||
1337 | } | ||
1310 | apic->lapic_timer.timer_mode = timer_mode; | 1338 | apic->lapic_timer.timer_mode = timer_mode; |
1311 | hrtimer_cancel(&apic->lapic_timer.timer); | 1339 | limit_periodic_timer_frequency(apic); |
1312 | } | 1340 | } |
1313 | } | 1341 | } |
1314 | 1342 | ||
@@ -1430,6 +1458,30 @@ static void start_sw_period(struct kvm_lapic *apic) | |||
1430 | HRTIMER_MODE_ABS_PINNED); | 1458 | HRTIMER_MODE_ABS_PINNED); |
1431 | } | 1459 | } |
1432 | 1460 | ||
1461 | static void update_target_expiration(struct kvm_lapic *apic, uint32_t old_divisor) | ||
1462 | { | ||
1463 | ktime_t now, remaining; | ||
1464 | u64 ns_remaining_old, ns_remaining_new; | ||
1465 | |||
1466 | apic->lapic_timer.period = (u64)kvm_lapic_get_reg(apic, APIC_TMICT) | ||
1467 | * APIC_BUS_CYCLE_NS * apic->divide_count; | ||
1468 | limit_periodic_timer_frequency(apic); | ||
1469 | |||
1470 | now = ktime_get(); | ||
1471 | remaining = ktime_sub(apic->lapic_timer.target_expiration, now); | ||
1472 | if (ktime_to_ns(remaining) < 0) | ||
1473 | remaining = 0; | ||
1474 | |||
1475 | ns_remaining_old = ktime_to_ns(remaining); | ||
1476 | ns_remaining_new = mul_u64_u32_div(ns_remaining_old, | ||
1477 | apic->divide_count, old_divisor); | ||
1478 | |||
1479 | apic->lapic_timer.tscdeadline += | ||
1480 | nsec_to_cycles(apic->vcpu, ns_remaining_new) - | ||
1481 | nsec_to_cycles(apic->vcpu, ns_remaining_old); | ||
1482 | apic->lapic_timer.target_expiration = ktime_add_ns(now, ns_remaining_new); | ||
1483 | } | ||
1484 | |||
1433 | static bool set_target_expiration(struct kvm_lapic *apic) | 1485 | static bool set_target_expiration(struct kvm_lapic *apic) |
1434 | { | 1486 | { |
1435 | ktime_t now; | 1487 | ktime_t now; |
@@ -1439,27 +1491,13 @@ static bool set_target_expiration(struct kvm_lapic *apic) | |||
1439 | apic->lapic_timer.period = (u64)kvm_lapic_get_reg(apic, APIC_TMICT) | 1491 | apic->lapic_timer.period = (u64)kvm_lapic_get_reg(apic, APIC_TMICT) |
1440 | * APIC_BUS_CYCLE_NS * apic->divide_count; | 1492 | * APIC_BUS_CYCLE_NS * apic->divide_count; |
1441 | 1493 | ||
1442 | if (!apic->lapic_timer.period) | 1494 | if (!apic->lapic_timer.period) { |
1495 | apic->lapic_timer.tscdeadline = 0; | ||
1443 | return false; | 1496 | return false; |
1444 | |||
1445 | /* | ||
1446 | * Do not allow the guest to program periodic timers with small | ||
1447 | * interval, since the hrtimers are not throttled by the host | ||
1448 | * scheduler. | ||
1449 | */ | ||
1450 | if (apic_lvtt_period(apic)) { | ||
1451 | s64 min_period = min_timer_period_us * 1000LL; | ||
1452 | |||
1453 | if (apic->lapic_timer.period < min_period) { | ||
1454 | pr_info_ratelimited( | ||
1455 | "kvm: vcpu %i: requested %lld ns " | ||
1456 | "lapic timer period limited to %lld ns\n", | ||
1457 | apic->vcpu->vcpu_id, | ||
1458 | apic->lapic_timer.period, min_period); | ||
1459 | apic->lapic_timer.period = min_period; | ||
1460 | } | ||
1461 | } | 1497 | } |
1462 | 1498 | ||
1499 | limit_periodic_timer_frequency(apic); | ||
1500 | |||
1463 | apic_debug("%s: bus cycle is %" PRId64 "ns, now 0x%016" | 1501 | apic_debug("%s: bus cycle is %" PRId64 "ns, now 0x%016" |
1464 | PRIx64 ", " | 1502 | PRIx64 ", " |
1465 | "timer initial count 0x%x, period %lldns, " | 1503 | "timer initial count 0x%x, period %lldns, " |
@@ -1515,6 +1553,9 @@ static bool start_hv_timer(struct kvm_lapic *apic) | |||
1515 | if (!apic_lvtt_period(apic) && atomic_read(&ktimer->pending)) | 1553 | if (!apic_lvtt_period(apic) && atomic_read(&ktimer->pending)) |
1516 | return false; | 1554 | return false; |
1517 | 1555 | ||
1556 | if (!ktimer->tscdeadline) | ||
1557 | return false; | ||
1558 | |||
1518 | r = kvm_x86_ops->set_hv_timer(apic->vcpu, ktimer->tscdeadline); | 1559 | r = kvm_x86_ops->set_hv_timer(apic->vcpu, ktimer->tscdeadline); |
1519 | if (r < 0) | 1560 | if (r < 0) |
1520 | return false; | 1561 | return false; |
@@ -1738,13 +1779,21 @@ int kvm_lapic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val) | |||
1738 | start_apic_timer(apic); | 1779 | start_apic_timer(apic); |
1739 | break; | 1780 | break; |
1740 | 1781 | ||
1741 | case APIC_TDCR: | 1782 | case APIC_TDCR: { |
1783 | uint32_t old_divisor = apic->divide_count; | ||
1784 | |||
1742 | if (val & 4) | 1785 | if (val & 4) |
1743 | apic_debug("KVM_WRITE:TDCR %x\n", val); | 1786 | apic_debug("KVM_WRITE:TDCR %x\n", val); |
1744 | kvm_lapic_set_reg(apic, APIC_TDCR, val); | 1787 | kvm_lapic_set_reg(apic, APIC_TDCR, val); |
1745 | update_divide_count(apic); | 1788 | update_divide_count(apic); |
1789 | if (apic->divide_count != old_divisor && | ||
1790 | apic->lapic_timer.period) { | ||
1791 | hrtimer_cancel(&apic->lapic_timer.timer); | ||
1792 | update_target_expiration(apic, old_divisor); | ||
1793 | restart_apic_timer(apic); | ||
1794 | } | ||
1746 | break; | 1795 | break; |
1747 | 1796 | } | |
1748 | case APIC_ESR: | 1797 | case APIC_ESR: |
1749 | if (apic_x2apic_mode(apic) && val != 0) { | 1798 | if (apic_x2apic_mode(apic) && val != 0) { |
1750 | apic_debug("KVM_WRITE:ESR not zero %x\n", val); | 1799 | apic_debug("KVM_WRITE:ESR not zero %x\n", val); |