On Tue, 24 Nov 2015 16:43:59 +0100 Christoffer Dall <christoffer.dall@xxxxxxxxxx> wrote: > 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. > > Signed-off-by: Christoffer Dall <christoffer.dall@xxxxxxxxxx> > --- > include/kvm/arm_vgic.h | 2 +- > virt/kvm/arm/arch_timer.c | 28 +++++++++++++++++----------- > virt/kvm/arm/vgic.c | 37 +++++++++++++++++++++++++------------ > 3 files changed, 43 insertions(+), 24 deletions(-) > > diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h > index 9c747cb..d2f4147 100644 > --- a/include/kvm/arm_vgic.h > +++ b/include/kvm/arm_vgic.h > @@ -342,10 +342,10 @@ int kvm_vgic_inject_mapped_irq(struct kvm *kvm, int cpuid, > struct irq_phys_map *map, bool level); > 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_active_irq(struct kvm_vcpu *vcpu); > struct irq_phys_map *kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu, > int virt_irq, int irq); > 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 vgic_initialized(k) (!!((k)->arch.vgic.nr_cpus)) > diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c > index 21a0ab2..69bca18 100644 > --- a/virt/kvm/arm/arch_timer.c > +++ b/virt/kvm/arm/arch_timer.c > @@ -221,17 +221,23 @@ void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu) > kvm_timer_update_state(vcpu); > > /* > - * If we enter the guest with the virtual input level to the VGIC > - * asserted, then we have already told the VGIC what we need to, and > - * we don't need to exit from the guest until the guest deactivates > - * the already injected interrupt, so therefore we should set the > - * hardware active state to prevent unnecessary exits from the guest. > - * > - * Conversely, if the virtual input level is deasserted, then always > - * clear the hardware active state to ensure that hardware interrupts > - * from the timer triggers a guest exit. > - */ > - if (timer->irq.level) > + * If we enter the guest with the virtual input level to the VGIC > + * asserted, then we have already told the VGIC what we need to, and > + * we don't need to exit from the guest until the guest deactivates > + * the already injected interrupt, so therefore we should set the > + * hardware active state to prevent unnecessary exits from the guest. > + * > + * Also, if we enter the guest with the virtual timer interrupt active, > + * then it must be active on the physical distributor, because we set > + * 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 || kvm_vgic_map_is_active(vcpu, timer->map)) > phys_active = true; > else > phys_active = false; > diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c > index 5335383..9002f0d 100644 > --- a/virt/kvm/arm/vgic.c > +++ b/virt/kvm/arm/vgic.c > @@ -1096,6 +1096,30 @@ static void vgic_retire_lr(int lr_nr, struct kvm_vcpu *vcpu) > vgic_set_lr(vcpu, lr_nr, vlr); > } > > +static int dist_active_irq(struct kvm_vcpu *vcpu) bool? That'd be consistent with kvm_vgic_map_is_active. > +{ > + struct vgic_dist *dist = &vcpu->kvm->arch.vgic; > + > + if (!irqchip_in_kernel(vcpu->kvm)) > + return 0; > + I believe you can drop this test, as the only other use case for this function is on the flush path, which obviously mandates an in-kernel irqchip. > + 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 > * CPU interface (the classic case is a timer running while we're > @@ -1248,7 +1272,7 @@ static void __kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu) > * may have been serviced from another vcpu. In all cases, > * 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; > > /* SGIs */ > @@ -1479,17 +1503,6 @@ int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu) > 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) > { > struct kvm_vcpu *vcpu; Other than the above nits: Reviewed-by: Marc Zyngier <marc.zyngier@xxxxxxx> M. -- Jazz is not dead. It just smells funny. -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html