summaryrefslogtreecommitdiffstats
path: root/drivers/kvm/lapic.c
diff options
context:
space:
mode:
authorKevin Pedretti2007-10-21 08:55:50 +0200
committerAvi Kivity2007-10-22 12:03:29 +0200
commit9da8f4e83a824dabf3fb7ad0890549257ae614a0 (patch)
treec99a6c3ae8f07c9ae9fc49b5567a665359d7da24 /drivers/kvm/lapic.c
parentKVM: Fix local apic timer divide by zero (diff)
downloadkernel-qcow2-linux-9da8f4e83a824dabf3fb7ad0890549257ae614a0.tar.gz
kernel-qcow2-linux-9da8f4e83a824dabf3fb7ad0890549257ae614a0.tar.xz
kernel-qcow2-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/lapic.c')
-rw-r--r--drivers/kvm/lapic.c36
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;