Commit 7afc4ddb authored by Christoffer Dall's avatar Christoffer Dall Committed by Marc Zyngier

KVM: arm/arm64: Fix potential loss of ptimer interrupts

kvm_timer_update_state() is called when changing the phys timer
configuration registers, either via vcpu reset, as a result of a trap
from the guest, or when userspace programs the registers.

phys_timer_emulate() is in turn called by kvm_timer_update_state() to
either cancel an existing software timer, or program a new software
timer, to emulate the behavior of a real phys timer, based on the change
in configuration registers.

Unfortunately, the interaction between these two functions left a small
race; if the conceptual emulated phys timer should actually fire, but
the soft timer hasn't executed its callback yet, we cancel the timer in
phys_timer_emulate without injecting an irq.  This only happens if the
check in kvm_timer_update_state is called before the timer should fire,
which is relatively unlikely, but possible.

The solution is to update the state of the phys timer after calling
phys_timer_emulate, which will pick up the pending timer state and
update the interrupt value.

Note that this leaves the opportunity of raising the interrupt twice,
once in the just-programmed soft timer, and once in
kvm_timer_update_state.  Since this always happens synchronously with
the VCPU execution, there is no harm in this, and the guest ever only
sees a single timer interrupt.

Cc: Stable <stable@vger.kernel.org> # 4.15+
Signed-off-by: default avatarChristoffer Dall <christoffer.dall@arm.com>
Signed-off-by: default avatarMarc Zyngier <marc.zyngier@arm.com>
parent 6b8b9a48
...@@ -295,9 +295,9 @@ static void phys_timer_emulate(struct kvm_vcpu *vcpu) ...@@ -295,9 +295,9 @@ static void phys_timer_emulate(struct kvm_vcpu *vcpu)
struct arch_timer_context *ptimer = vcpu_ptimer(vcpu); struct arch_timer_context *ptimer = vcpu_ptimer(vcpu);
/* /*
* If the timer can fire now we have just raised the IRQ line and we * If the timer can fire now, we don't need to have a soft timer
* don't need to have a soft timer scheduled for the future. If the * scheduled for the future. If the timer cannot fire at all,
* timer cannot fire at all, then we also don't need a soft timer. * then we also don't need a soft timer.
*/ */
if (kvm_timer_should_fire(ptimer) || !kvm_timer_irq_can_fire(ptimer)) { if (kvm_timer_should_fire(ptimer) || !kvm_timer_irq_can_fire(ptimer)) {
soft_timer_cancel(&timer->phys_timer, NULL); soft_timer_cancel(&timer->phys_timer, NULL);
...@@ -332,10 +332,10 @@ static void kvm_timer_update_state(struct kvm_vcpu *vcpu) ...@@ -332,10 +332,10 @@ static void kvm_timer_update_state(struct kvm_vcpu *vcpu)
level = kvm_timer_should_fire(vtimer); level = kvm_timer_should_fire(vtimer);
kvm_timer_update_irq(vcpu, level, vtimer); kvm_timer_update_irq(vcpu, level, vtimer);
phys_timer_emulate(vcpu);
if (kvm_timer_should_fire(ptimer) != ptimer->irq.level) if (kvm_timer_should_fire(ptimer) != ptimer->irq.level)
kvm_timer_update_irq(vcpu, !ptimer->irq.level, ptimer); kvm_timer_update_irq(vcpu, !ptimer->irq.level, ptimer);
phys_timer_emulate(vcpu);
} }
static void vtimer_save_state(struct kvm_vcpu *vcpu) static void vtimer_save_state(struct kvm_vcpu *vcpu)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment