Commit 9da8f4e8 authored by Kevin Pedretti's avatar Kevin Pedretti Committed by Avi Kivity

KVM: Improve local apic timer wraparound handling

Better handle wrap-around cases when reading the APIC CCR
(current count register).  Also, if ICR is 0, CCR should also
be 0... previously reading CCR before setting ICR would result
in a large kinda-random number.
Signed-off-by: default avatarKevin Pedretti <kevin.pedretti@gmail.com>
Signed-off-by: default avatarAvi Kivity <avi@qumranet.com>
parent b33ac88b
...@@ -494,12 +494,19 @@ static void apic_send_ipi(struct kvm_lapic *apic) ...@@ -494,12 +494,19 @@ static void apic_send_ipi(struct kvm_lapic *apic)
static u32 apic_get_tmcct(struct kvm_lapic *apic) static u32 apic_get_tmcct(struct kvm_lapic *apic)
{ {
u32 counter_passed; u64 counter_passed;
ktime_t passed, now = apic->timer.dev.base->get_time(); ktime_t passed, now;
u32 tmcct = apic_get_reg(apic, APIC_TMICT); u32 tmcct;
ASSERT(apic != NULL); ASSERT(apic != NULL);
now = apic->timer.dev.base->get_time();
tmcct = apic_get_reg(apic, APIC_TMICT);
/* if initial count is 0, current count should also be 0 */
if (tmcct == 0)
return 0;
if (unlikely(ktime_to_ns(now) <= if (unlikely(ktime_to_ns(now) <=
ktime_to_ns(apic->timer.last_update))) { ktime_to_ns(apic->timer.last_update))) {
/* Wrap around */ /* Wrap around */
...@@ -514,15 +521,24 @@ static u32 apic_get_tmcct(struct kvm_lapic *apic) ...@@ -514,15 +521,24 @@ static u32 apic_get_tmcct(struct kvm_lapic *apic)
counter_passed = div64_64(ktime_to_ns(passed), counter_passed = div64_64(ktime_to_ns(passed),
(APIC_BUS_CYCLE_NS * apic->timer.divide_count)); (APIC_BUS_CYCLE_NS * apic->timer.divide_count));
tmcct -= counter_passed;
if (tmcct <= 0) { if (counter_passed > tmcct) {
if (unlikely(!apic_lvtt_period(apic))) if (unlikely(!apic_lvtt_period(apic))) {
/* one-shot timers stick at 0 until reset */
tmcct = 0; tmcct = 0;
else } else {
do { /*
tmcct += apic_get_reg(apic, APIC_TMICT); * periodic timers reset to APIC_TMICT when they
} while (tmcct <= 0); * hit 0. The while loop simulates this happening N
* times. (counter_passed %= tmcct) would also work,
* but might be slower or not work on 32-bit??
*/
while (counter_passed > tmcct)
counter_passed -= tmcct;
tmcct -= counter_passed;
}
} else {
tmcct -= counter_passed;
} }
return tmcct; return tmcct;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment