arm/arm64: KVM: Fix arch timer behavior for disabled interrupts
authorChristoffer Dall <christoffer.dall@linaro.org>
Fri, 16 Oct 2015 10:41:21 +0000 (12:41 +0200)
committerChristoffer Dall <christoffer.dall@linaro.org>
Tue, 20 Oct 2015 16:04:54 +0000 (18:04 +0200)
commitcff9211eb1a1f58ce7f5a2d596b617928fd4be0e
treebe0d29ce87e62ad6281e75646e0da3c7e01d3a7a
parent4a5d69b73948d0e03cd38d77dc11edb2e707165f
arm/arm64: KVM: Fix arch timer behavior for disabled interrupts

We have an interesting issue when the guest disables the timer interrupt
on the VGIC, which happens when turning VCPUs off using PSCI, for
example.

The problem is that because the guest disables the virtual interrupt at
the VGIC level, we never inject interrupts to the guest and therefore
never mark the interrupt as active on the physical distributor.  The
host also never takes the timer interrupt (we only use the timer device
to trigger a guest exit and everything else is done in software), so the
interrupt does not become active through normal means.

The result is that we keep entering the guest with a programmed timer
that will always fire as soon as we context switch the hardware timer
state and run the guest, preventing forward progress for the VCPU.

Since the active state on the physical distributor is really part of the
timer logic, it is the job of our virtual arch timer driver to manage
this state.

The timer->map->active boolean field indicates whether we have signalled
this interrupt to the vgic and if that interrupt is still pending or
active.  As long as that is the case, the hardware doesn't have to
generate physical interrupts and therefore we mark the interrupt as
active on the physical distributor.

We also have to restore the pending state of an interrupt that was
queued to an LR but was retired from the LR for some reason, while
remaining pending in the LR.

Cc: Marc Zyngier <marc.zyngier@arm.com>
Reported-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
virt/kvm/arm/arch_timer.c
virt/kvm/arm/vgic.c