From 8092cb71322ca488deeb7c750ff8022ffcc2f9a6 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 11 Nov 2014 13:14:14 +0100 Subject: [PATCH] apic: fix loss of IPI due to masked ExtINT 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 Tested-by: Richard Bilson Signed-off-by: Paolo Bonzini --- hw/intc/apic.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/intc/apic.c b/hw/intc/apic.c index 0653409..6ec5861 100644 --- a/hw/intc/apic.c +++ b/hw/intc/apic.c @@ -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); } } -- 2.7.4