aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorKevin Pedretti <kevin.pedretti@gmail.com>2007-10-21 02:55:50 -0400
committerAvi Kivity <avi@qumranet.com>2007-10-22 06:03:29 -0400
commit9da8f4e83a824dabf3fb7ad0890549257ae614a0 (patch)
treec99a6c3ae8f07c9ae9fc49b5567a665359d7da24 /drivers
parentb33ac88b4c23330043acad930517282eb486db1d (diff)
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')
-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)
494 494
495static u32 apic_get_tmcct(struct kvm_lapic *apic) 495static u32 apic_get_tmcct(struct kvm_lapic *apic)
496{ 496{
497 u32 counter_passed; 497 u64 counter_passed;
498 ktime_t passed, now = apic->timer.dev.base->get_time(); 498 ktime_t passed, now;
499 u32 tmcct = apic_get_reg(apic, APIC_TMICT); 499 u32 tmcct;
500 500
501 ASSERT(apic != NULL); 501 ASSERT(apic != NULL);
502 502
503 now = apic->timer.dev.base->get_time();
504 tmcct = apic_get_reg(apic, APIC_TMICT);
505
506 /* if initial count is 0, current count should also be 0 */
507 if (tmcct == 0)
508 return 0;
509
503 if (unlikely(ktime_to_ns(now) <= 510 if (unlikely(ktime_to_ns(now) <=
504 ktime_to_ns(apic->timer.last_update))) { 511 ktime_to_ns(apic->timer.last_update))) {
505 /* Wrap around */ 512 /* Wrap around */
@@ -514,15 +521,24 @@ static u32 apic_get_tmcct(struct kvm_lapic *apic)
514 521
515 counter_passed = div64_64(ktime_to_ns(passed), 522 counter_passed = div64_64(ktime_to_ns(passed),
516 (APIC_BUS_CYCLE_NS * apic->timer.divide_count)); 523 (APIC_BUS_CYCLE_NS * apic->timer.divide_count));
517 tmcct -= counter_passed;
518 524
519 if (tmcct <= 0) { 525 if (counter_passed > tmcct) {
520 if (unlikely(!apic_lvtt_period(apic))) 526 if (unlikely(!apic_lvtt_period(apic))) {
527 /* one-shot timers stick at 0 until reset */
521 tmcct = 0; 528 tmcct = 0;
522 else 529 } else {
523 do { 530 /*
524 tmcct += apic_get_reg(apic, APIC_TMICT); 531 * periodic timers reset to APIC_TMICT when they
525 } while (tmcct <= 0); 532 * hit 0. The while loop simulates this happening N
533 * times. (counter_passed %= tmcct) would also work,
534 * but might be slower or not work on 32-bit??
535 */
536 while (counter_passed > tmcct)
537 counter_passed -= tmcct;
538 tmcct -= counter_passed;
539 }
540 } else {
541 tmcct -= counter_passed;
526 } 542 }
527 543
528 return tmcct; 544 return tmcct;