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>
This commit is contained in:
parent
b33ac88b4c
commit
9da8f4e83a
1 changed files with 26 additions and 10 deletions
|
@ -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)
|
||||||
|
|
||||||
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;
|
||||||
|
|
Loading…
Add table
Reference in a new issue