diff options
author | Kevin Pedretti <kevin.pedretti@gmail.com> | 2007-10-21 02:55:50 -0400 |
---|---|---|
committer | Avi Kivity <avi@qumranet.com> | 2007-10-22 06:03:29 -0400 |
commit | 9da8f4e83a824dabf3fb7ad0890549257ae614a0 (patch) | |
tree | c99a6c3ae8f07c9ae9fc49b5567a665359d7da24 /drivers | |
parent | b33ac88b4c23330043acad930517282eb486db1d (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.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) | |||
494 | 494 | ||
495 | static u32 apic_get_tmcct(struct kvm_lapic *apic) | 495 | static 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; |