Re: [patch] 2.4.0, 2.4.0-ac12: APIC lock-ups

Maciej W. Rozycki (macro@ds2.pg.gda.pl)
Fri, 2 Feb 2001 13:08:30 +0100 (MET)


On Thu, 1 Feb 2001, Andrew Morton wrote:

> Your latest patch passes all my testing.
>
> 2.4.1+irq-whacker+netperf: APIC dies instantly
> 2.4.1+irq-whacker+netperf+patch: 8 million interrupts, then I got bored.

Linus, would you please apply the following patch for 2.4.2? The idea of
operation is described in the comment below.

Maciej

-- 
+  Maciej W. Rozycki, Technical University of Gdansk, Poland   +
+--------------------------------------------------------------+
+        e-mail: macro@ds2.pg.gda.pl, PGP key available        +

patch-2.4.0-io_apic-4 diff -up --recursive --new-file linux-2.4.0.macro/arch/i386/kernel/apic.c linux-2.4.0/arch/i386/kernel/apic.c --- linux-2.4.0.macro/arch/i386/kernel/apic.c Wed Dec 13 23:54:27 2000 +++ linux-2.4.0/arch/i386/kernel/apic.c Sun Jan 28 08:58:02 2001 @@ -270,7 +270,7 @@ void __init setup_local_APIC (void) * PCI Ne2000 networking cards and PII/PIII processors, dual * BX chipset. ] */ -#if 0 +#if 1 /* Enable focus processor (bit==0) */ value &= ~(1<<9); #else diff -up --recursive --new-file linux-2.4.0.macro/arch/i386/kernel/io_apic.c linux-2.4.0/arch/i386/kernel/io_apic.c --- linux-2.4.0.macro/arch/i386/kernel/io_apic.c Thu Oct 5 21:08:17 2000 +++ linux-2.4.0/arch/i386/kernel/io_apic.c Tue Jan 30 07:49:01 2001 @@ -122,8 +122,27 @@ static void add_pin_to_irq(unsigned int static void name##_IO_APIC_irq (unsigned int irq) \ __DO_ACTION(R, ACTION, FINAL) -DO_ACTION( __mask, 0, |= 0x00010000, io_apic_sync(entry->apic))/* mask = 1 */ -DO_ACTION( __unmask, 0, &= 0xfffeffff, ) /* mask = 0 */ +/* + * It appears there is an erratum which affects at least the 82093AA + * I/O APIC. If a level-triggered interrupt input is being masked in + * the redirection entry while the interrupt is send pending (its + * delivery status bit is set), the interrupt is erroneously + * delivered as edge-triggered but the IRR bit gets set nevertheless. + * As a result the I/O unit expects an EOI message but it will never + * arrive and further interrupts are blocked for the source. + * + * A workaround is to set the trigger mode to edge when masking + * a level-triggered interrupt and to revert the mode when unmasking. + * The idea is from Manfred Spraul. --macro + */ +DO_ACTION( __mask, 0, |= 0x00010000, + ) /* mask = 1 */ +DO_ACTION( __unmask, 0, &= 0xfffeffff, + io_apic_sync(entry->apic)) /* mask = 0 */ +DO_ACTION( __mask_level, 0, = (reg & 0xffff7fff) | 0x00010000, + io_apic_sync(entry->apic)) /* mask = 1, trigger = edge */ +DO_ACTION( __unmask_level, 0, = (reg & 0xfffeffff) | 0x00008000, + ) /* mask = 0, trigger = level */ static void mask_IO_APIC_irq (unsigned int irq) { @@ -143,6 +162,24 @@ static void unmask_IO_APIC_irq (unsigned spin_unlock_irqrestore(&ioapic_lock, flags); } +static void mask_level_IO_APIC_irq (unsigned int irq) +{ + unsigned long flags; + + spin_lock_irqsave(&ioapic_lock, flags); + __mask_level_IO_APIC_irq(irq); + spin_unlock_irqrestore(&ioapic_lock, flags); +} + +static void unmask_level_IO_APIC_irq (unsigned int irq) +{ + unsigned long flags; + + spin_lock_irqsave(&ioapic_lock, flags); + __unmask_level_IO_APIC_irq(irq); + spin_unlock_irqrestore(&ioapic_lock, flags); +} + void clear_IO_APIC_pin(unsigned int apic, unsigned int pin) { struct IO_APIC_route_entry entry; @@ -1181,14 +1218,18 @@ static void end_edge_ioapic_irq (unsigne */ static unsigned int startup_level_ioapic_irq (unsigned int irq) { - unmask_IO_APIC_irq(irq); + unmask_level_IO_APIC_irq(irq); return 0; /* don't check for pending */ } -#define shutdown_level_ioapic_irq mask_IO_APIC_irq -#define enable_level_ioapic_irq unmask_IO_APIC_irq -#define disable_level_ioapic_irq mask_IO_APIC_irq +#define shutdown_level_ioapic_irq mask_level_IO_APIC_irq +#define enable_level_ioapic_irq unmask_level_IO_APIC_irq +#define disable_level_ioapic_irq mask_level_IO_APIC_irq + +#define shutdown_level_82489dx_irq mask_IO_APIC_irq +#define enable_level_82489dx_irq unmask_IO_APIC_irq +#define disable_level_82489dx_irq mask_IO_APIC_irq static void end_level_ioapic_irq (unsigned int i) { @@ -1503,6 +1544,27 @@ static inline void check_timer(void) } /* + * We can't set the trigger mode to edge when masking a + * level-triggered interrupt in the 82489DX I/O APIC as + * no deassert message will be sent in this case and a + * local APIC may keep delivering the interrupt to a CPU. + * Hence we substitute generic versions for affected + * handlers. + */ + +static inline void setup_IO_APIC_irq_handlers(void) +{ + struct IO_APIC_reg_01 reg_01; + + *(int *)&reg_01 = io_apic_read(0, 1); + if (reg_01.version < 0x10) { + ioapic_level_irq_type.shutdown = shutdown_level_82489dx_irq; + ioapic_level_irq_type.enable = enable_level_82489dx_irq; + ioapic_level_irq_type.disable = disable_level_82489dx_irq; + } +} + +/* * * IRQ's that are handled by the old PIC in all cases: * - IRQ2 is the cascade IRQ, and cannot be a io-apic IRQ. @@ -1520,6 +1582,8 @@ static inline void check_timer(void) void __init setup_IO_APIC(void) { + setup_IO_APIC_irq_handlers(); + enable_IO_APIC(); io_apic_irqs = ~PIC_IRQS;

- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org Please read the FAQ at http://www.tux.org/lkml/