@@ -188,7 +188,7 @@ void apic_deliver_pic_intr(DeviceState *dev, int level)
apic_reset_bit(s->irr, lvt & 0xff);
/* fall through */
case APIC_DM_EXTINT:
- cpu_reset_interrupt(CPU(s->cpu), CPU_INTERRUPT_HARD);
+ apic_update_irq(s);
break;
}
}
@@ -376,6 +376,8 @@ static void apic_update_irq(APICCommonState *s)
cpu_interrupt(cpu, CPU_INTERRUPT_POLL);
} else if (apic_irq_pending(s) > 0) {
cpu_interrupt(cpu, CPU_INTERRUPT_HARD);
+ } else if (!apic_accept_pic_intr(&s->busdev.qdev) || !pic_get_output(isa_pic)) {
+ cpu_reset_interrupt(cpu, CPU_INTERRUPT_HARD);
}
}
This patch fixes an obscure failure of the QNX kernel on QEMU x86 SMP. In QNX, all hardware interrupts come via the PIC, and are delivered by the cpu 0 LAPIC in ExtINT mode, while IPIs are delivered by the LAPIC in fixed mode. This bug happens as follows: - cpu 0 masks a particular PIC interrupt - IPI sent to cpu 0 (CPU_INTERRUPT_HARD is set) - before the IPI is accepted, the masked interrupt line is asserted by the device Since the interrupt is masked, apic_deliver_pic_intr will clear CPU_INTERRUPT_HARD. The IPI will still be set in the APIC irr, but since CPU_INTERRUPT_HARD is not set the cpu will not notice. Depending on the scenario this can cause a system hang, i.e. if cpu 0 is expected to unmask the interrupt. In order to fix this, do a full check of the APIC before an EXTINT is acknowledged. This can result in clearing CPU_INTERRUPT_HARD, but can also result in delivering the lost IPI. Reported-by: Richard Bilson <rbilson@qnx.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- hw/intc/apic.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)