diff options
author | Kevin Pedretti <kevin.pedretti@gmail.com> | 2007-10-21 08:55:50 +0200 |
---|---|---|
committer | Avi Kivity <avi@qumranet.com> | 2007-10-22 12:03:29 +0200 |
commit | 9da8f4e83a824dabf3fb7ad0890549257ae614a0 (patch) | |
tree | c99a6c3ae8f07c9ae9fc49b5567a665359d7da24 /drivers/kvm | |
parent | KVM: Fix local apic timer divide by zero (diff) | |
download | linux-9da8f4e83a824dabf3fb7ad0890549257ae614a0.tar.xz linux-9da8f4e83a824dabf3fb7ad0890549257ae614a0.zip |
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: Kevin Pedretti <kevin.pedretti@gmail.com>
Signed-off-by: Avi Kivity <avi@qumranet.com>
Diffstat (limited to 'drivers/kvm')
-rw-r--r-- | drivers/kvm/lapic.c | 36 |
1 files changed, 26 insertions, 10 deletions
diff --git a/drivers/kvm/lapic.c b/drivers/kvm/lapic.c index 443730e689e3..238fcad3cece 100644 --- a/drivers/kvm/lapic.c +++ b/drivers/kvm/lapic.c @@ -494,12 +494,19 @@ static void apic_send_ipi(struct kvm_lapic *apic) static u32 apic_get_tmcct(struct kvm_lapic *apic) { - u32 counter_passed; - ktime_t passed, now = apic->timer.dev.base->get_time(); - u32 tmcct = apic_get_reg(apic, APIC_TMICT); + u64 counter_passed; + ktime_t passed, now; + u32 tmcct; 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) <= ktime_to_ns(apic->timer.last_update))) { /* Wrap around */ @@ -514,15 +521,24 @@ static u32 apic_get_tmcct(struct kvm_lapic *apic) counter_passed = div64_64(ktime_to_ns(passed), (APIC_BUS_CYCLE_NS * apic->timer.divide_count)); - tmcct -= counter_passed; - if (tmcct <= 0) { - if (unlikely(!apic_lvtt_period(apic))) + if (counter_passed > tmcct) { + if (unlikely(!apic_lvtt_period(apic))) { + /* one-shot timers stick at 0 until reset */ tmcct = 0; - else - do { - tmcct += apic_get_reg(apic, APIC_TMICT); - } while (tmcct <= 0); + } else { + /* + * periodic timers reset to APIC_TMICT when they + * 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; |