Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9da8f4e8 authored by Kevin Pedretti's avatar Kevin Pedretti Committed by Avi Kivity
Browse files

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: default avatarKevin Pedretti <kevin.pedretti@gmail.com>
Signed-off-by: default avatarAvi Kivity <avi@qumranet.com>
parent b33ac88b
Loading
Loading
Loading
Loading
+26 −10
Original line number Diff line number Diff line
@@ -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;