diff options
Diffstat (limited to 'arch/x86/kvm/ioapic.c')
| -rw-r--r-- | arch/x86/kvm/ioapic.c | 36 | 
1 files changed, 33 insertions, 3 deletions
| diff --git a/arch/x86/kvm/ioapic.c b/arch/x86/kvm/ioapic.c index 042dee556125..995eb5054360 100644 --- a/arch/x86/kvm/ioapic.c +++ b/arch/x86/kvm/ioapic.c @@ -368,9 +368,39 @@ static void ioapic_write_indirect(struct kvm_ioapic *ioapic, u32 val)  		mask_after = e->fields.mask;  		if (mask_before != mask_after)  			kvm_fire_mask_notifiers(ioapic->kvm, KVM_IRQCHIP_IOAPIC, index, mask_after); -		if (e->fields.trig_mode == IOAPIC_LEVEL_TRIG -		    && ioapic->irr & (1 << index)) -			ioapic_service(ioapic, index, false); +		if (e->fields.trig_mode == IOAPIC_LEVEL_TRIG && +		    ioapic->irr & (1 << index) && !e->fields.mask && !e->fields.remote_irr) { +			/* +			 * Pending status in irr may be outdated: the IRQ line may have +			 * already been deasserted by a device while the IRQ was masked. +			 * This occurs, for instance, if the interrupt is handled in a +			 * Linux guest as a oneshot interrupt (IRQF_ONESHOT). In this +			 * case the guest acknowledges the interrupt to the device in +			 * its threaded irq handler, i.e. after the EOI but before +			 * unmasking, so at the time of unmasking the IRQ line is +			 * already down but our pending irr bit is still set. In such +			 * cases, injecting this pending interrupt to the guest is +			 * buggy: the guest will receive an extra unwanted interrupt. +			 * +			 * So we need to check here if the IRQ is actually still pending. +			 * As we are generally not able to probe the IRQ line status +			 * directly, we do it through irqfd resampler. Namely, we clear +			 * the pending status and notify the resampler that this interrupt +			 * is done, without actually injecting it into the guest. If the +			 * IRQ line is actually already deasserted, we are done. If it is +			 * still asserted, a new interrupt will be shortly triggered +			 * through irqfd and injected into the guest. +			 * +			 * If, however, it's not possible to resample (no irqfd resampler +			 * registered for this irq), then unconditionally inject this +			 * pending interrupt into the guest, so the guest will not miss +			 * an interrupt, although may get an extra unwanted interrupt. +			 */ +			if (kvm_notify_irqfd_resampler(ioapic->kvm, KVM_IRQCHIP_IOAPIC, index)) +				ioapic->irr &= ~(1 << index); +			else +				ioapic_service(ioapic, index, false); +		}  		if (e->fields.delivery_mode == APIC_DM_FIXED) {  			struct kvm_lapic_irq irq; | 
