@@ -214,6 +214,9 @@ struct vgic_cpu {
u32 vgic_elrsr[2]; /* Saved only */
u32 vgic_apr;
u32 vgic_lr[64]; /* A15 has only 4... */
+
+ /* Number of level-triggered interrupt in progress */
+ atomic_t irq_active_count;
#endif
};
@@ -250,6 +253,8 @@ bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
struct kvm_exit_mmio *mmio);
#define irqchip_in_kernel(k) (!!((k)->arch.vgic.vctrl_base))
+#define vgic_active_irq(v) (atomic_read(&(v)->arch.vgic_cpu.irq_active_count) == 0)
+
#else
static inline int kvm_vgic_hyp_init(void)
{
@@ -286,6 +291,11 @@ static inline int irqchip_in_kernel(struct kvm *kvm)
{
return 0;
}
+
+static inline int vgic_active_irq(struct kvm_vcpu *vcpu)
+{
+ return 0;
+}
#endif
#endif
@@ -94,7 +94,15 @@ int kvm_arch_hardware_enable(void *garbage)
int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu)
{
- return kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE;
+ if (kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE) {
+ if (vgic_active_irq(vcpu) &&
+ cmpxchg(&vcpu->mode, EXITING_GUEST_MODE, IN_GUEST_MODE) == EXITING_GUEST_MODE)
+ return 0;
+
+ return 1;
+ }
+
+ return 0;
}
void kvm_arch_hardware_disable(void *garbage)
@@ -674,8 +674,10 @@ static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq)
kvm_debug("LR%d piggyback for IRQ%d %x\n", lr, irq, vgic_cpu->vgic_lr[lr]);
BUG_ON(!test_bit(lr, vgic_cpu->lr_used));
vgic_cpu->vgic_lr[lr] |= VGIC_LR_PENDING_BIT;
- if (is_level)
+ if (is_level) {
vgic_cpu->vgic_lr[lr] |= VGIC_LR_EOI;
+ atomic_inc(&vgic_cpu->irq_active_count);
+ }
return true;
}
@@ -687,8 +689,10 @@ static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq)
kvm_debug("LR%d allocated for IRQ%d %x\n", lr, irq, sgi_source_id);
vgic_cpu->vgic_lr[lr] = MK_LR_PEND(sgi_source_id, irq);
- if (is_level)
+ if (is_level) {
vgic_cpu->vgic_lr[lr] |= VGIC_LR_EOI;
+ atomic_inc(&vgic_cpu->irq_active_count);
+ }
vgic_cpu->vgic_irq_lr_map[irq] = lr;
clear_bit(lr, (unsigned long *)vgic_cpu->vgic_elrsr);
@@ -963,6 +967,8 @@ static irqreturn_t vgic_maintenance_handler(int irq, void *data)
vgic_bitmap_set_irq_val(&dist->irq_active,
vcpu->vcpu_id, irq, 0);
+ atomic_dec(&vgic_cpu->irq_active_count);
+ smp_mb();
vgic_cpu->vgic_lr[lr] &= ~VGIC_LR_EOI;
writel_relaxed(vgic_cpu->vgic_lr[lr],
dist->vctrl_base + GICH_LR0 + (lr << 2));