KVM: arm/arm64: arch_timer: Preserve physical dist. active state on LR.active
We were incorrectly removing the active state from the physical distributor on the timer interrupt when the timer output level was deasserted. We shouldn't be doing this without considering the virtual interrupt's active state, because the architecture requires that when an LR has the HW bit set and the pending or active bits set, then the physical interrupt must also have the corresponding bits set. This addresses an issue where we have been observing an inconsistency between the LR state and the physical distributor state where the LR state was active and the physical distributor was not active, which shouldn't happen. Reviewed-by: Marc Zyngier <marc.zyngier@arm.com> Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
This commit is contained in:
parent
7e16aa81f9
commit
0e3dfda91d
|
@ -342,10 +342,10 @@ int kvm_vgic_inject_mapped_irq(struct kvm *kvm, int cpuid,
|
||||||
struct irq_phys_map *map, bool level);
|
struct irq_phys_map *map, bool level);
|
||||||
void vgic_v3_dispatch_sgi(struct kvm_vcpu *vcpu, u64 reg);
|
void vgic_v3_dispatch_sgi(struct kvm_vcpu *vcpu, u64 reg);
|
||||||
int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu);
|
int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu);
|
||||||
int kvm_vgic_vcpu_active_irq(struct kvm_vcpu *vcpu);
|
|
||||||
struct irq_phys_map *kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu,
|
struct irq_phys_map *kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu,
|
||||||
int virt_irq, int irq);
|
int virt_irq, int irq);
|
||||||
int kvm_vgic_unmap_phys_irq(struct kvm_vcpu *vcpu, struct irq_phys_map *map);
|
int kvm_vgic_unmap_phys_irq(struct kvm_vcpu *vcpu, struct irq_phys_map *map);
|
||||||
|
bool kvm_vgic_map_is_active(struct kvm_vcpu *vcpu, struct irq_phys_map *map);
|
||||||
|
|
||||||
#define irqchip_in_kernel(k) (!!((k)->arch.vgic.in_kernel))
|
#define irqchip_in_kernel(k) (!!((k)->arch.vgic.in_kernel))
|
||||||
#define vgic_initialized(k) (!!((k)->arch.vgic.nr_cpus))
|
#define vgic_initialized(k) (!!((k)->arch.vgic.nr_cpus))
|
||||||
|
|
|
@ -227,11 +227,17 @@ void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu)
|
||||||
* the already injected interrupt, so therefore we should set the
|
* the already injected interrupt, so therefore we should set the
|
||||||
* hardware active state to prevent unnecessary exits from the guest.
|
* hardware active state to prevent unnecessary exits from the guest.
|
||||||
*
|
*
|
||||||
* Conversely, if the virtual input level is deasserted, then always
|
* Also, if we enter the guest with the virtual timer interrupt active,
|
||||||
* clear the hardware active state to ensure that hardware interrupts
|
* then it must be active on the physical distributor, because we set
|
||||||
* from the timer triggers a guest exit.
|
* the HW bit and the guest must be able to deactivate the virtual and
|
||||||
|
* physical interrupt at the same time.
|
||||||
|
*
|
||||||
|
* Conversely, if the virtual input level is deasserted and the virtual
|
||||||
|
* interrupt is not active, then always clear the hardware active state
|
||||||
|
* to ensure that hardware interrupts from the timer triggers a guest
|
||||||
|
* exit.
|
||||||
*/
|
*/
|
||||||
if (timer->irq.level)
|
if (timer->irq.level || kvm_vgic_map_is_active(vcpu, timer->map))
|
||||||
phys_active = true;
|
phys_active = true;
|
||||||
else
|
else
|
||||||
phys_active = false;
|
phys_active = false;
|
||||||
|
|
|
@ -1096,6 +1096,27 @@ static void vgic_retire_lr(int lr_nr, struct kvm_vcpu *vcpu)
|
||||||
vgic_set_lr(vcpu, lr_nr, vlr);
|
vgic_set_lr(vcpu, lr_nr, vlr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool dist_active_irq(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
|
||||||
|
|
||||||
|
return test_bit(vcpu->vcpu_id, dist->irq_active_on_cpu);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool kvm_vgic_map_is_active(struct kvm_vcpu *vcpu, struct irq_phys_map *map)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < vcpu->arch.vgic_cpu.nr_lr; i++) {
|
||||||
|
struct vgic_lr vlr = vgic_get_lr(vcpu, i);
|
||||||
|
|
||||||
|
if (vlr.irq == map->virt_irq && vlr.state & LR_STATE_ACTIVE)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dist_active_irq(vcpu);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* An interrupt may have been disabled after being made pending on the
|
* An interrupt may have been disabled after being made pending on the
|
||||||
* CPU interface (the classic case is a timer running while we're
|
* CPU interface (the classic case is a timer running while we're
|
||||||
|
@ -1248,7 +1269,7 @@ static void __kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu)
|
||||||
* may have been serviced from another vcpu. In all cases,
|
* may have been serviced from another vcpu. In all cases,
|
||||||
* move along.
|
* move along.
|
||||||
*/
|
*/
|
||||||
if (!kvm_vgic_vcpu_pending_irq(vcpu) && !kvm_vgic_vcpu_active_irq(vcpu))
|
if (!kvm_vgic_vcpu_pending_irq(vcpu) && !dist_active_irq(vcpu))
|
||||||
goto epilog;
|
goto epilog;
|
||||||
|
|
||||||
/* SGIs */
|
/* SGIs */
|
||||||
|
@ -1479,17 +1500,6 @@ int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu)
|
||||||
return test_bit(vcpu->vcpu_id, dist->irq_pending_on_cpu);
|
return test_bit(vcpu->vcpu_id, dist->irq_pending_on_cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
int kvm_vgic_vcpu_active_irq(struct kvm_vcpu *vcpu)
|
|
||||||
{
|
|
||||||
struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
|
|
||||||
|
|
||||||
if (!irqchip_in_kernel(vcpu->kvm))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return test_bit(vcpu->vcpu_id, dist->irq_active_on_cpu);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void vgic_kick_vcpus(struct kvm *kvm)
|
void vgic_kick_vcpus(struct kvm *kvm)
|
||||||
{
|
{
|
||||||
struct kvm_vcpu *vcpu;
|
struct kvm_vcpu *vcpu;
|
||||||
|
|
Loading…
Reference in New Issue