diff options
author | Eric W. Biederman <ebiederm@xmission.com> | 2006-11-14 12:52:12 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.osdl.org> | 2006-11-15 12:04:32 -0500 |
commit | 45c99533252ef2297f37c5fdd672a3e0eb566870 (patch) | |
tree | 469e706b7a6be19004015e305bdaf4cba4a89a52 | |
parent | 134a11f0c37c043d3ea557ea15b95b084e3cc2c8 (diff) |
[PATCH] Use delayed disable mode of ioapic edge triggered interrupts
Komuro reports that ISA interrupts do not work after a disable_irq(),
causing some PCMCIA drivers to not work, with messages like
eth0: Asix AX88190: io 0x300, irq 3, hw_addr xx:xx:xx:xx:xx:xx
eth0: found link beat
eth0: autonegotiation complete: 100baseT-FD selected
eth0: interrupt(s) dropped!
eth0: interrupt(s) dropped!
eth0: interrupt(s) dropped!
...
Linus Torvalds <torvalds@osdl.org> said:
"Now, edge-triggered interrupts are a _lot_ harder to mask, because the
Intel APIC is an unbelievable piece of sh*t, and has the edge-detect logic
_before_ the mask logic, so if a edge happens _while_ the device is
masked, you'll never ever see the edge ever again (unmasking will not
cause a new edge, so you simply lost the interrupt).
So when you "mask" an edge-triggered IRQ, you can't really mask it at all,
because if you did that, you'd lose it forever if the IRQ comes in while
you masked it. Instead, we're supposed to leave it active, and set a flag,
and IF the IRQ comes in, we just remember it, and mask it at that point
instead, and then on unmasking, we have to replay it by sending a
self-IPI."
This trivial patch solves the problem.
Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
Cc: Ingo Molnar <mingo@redhat.com>
Acked-by: Komuro <komurojun-mbn@nifty.com>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | arch/i386/kernel/io_apic.c | 4 | ||||
-rw-r--r-- | arch/x86_64/kernel/io_apic.c | 4 |
2 files changed, 6 insertions, 2 deletions
diff --git a/arch/i386/kernel/io_apic.c b/arch/i386/kernel/io_apic.c index ad84bc2802a6..3b7a63e0ed1a 100644 --- a/arch/i386/kernel/io_apic.c +++ b/arch/i386/kernel/io_apic.c | |||
@@ -1287,9 +1287,11 @@ static void ioapic_register_intr(int irq, int vector, unsigned long trigger) | |||
1287 | trigger == IOAPIC_LEVEL) | 1287 | trigger == IOAPIC_LEVEL) |
1288 | set_irq_chip_and_handler_name(irq, &ioapic_chip, | 1288 | set_irq_chip_and_handler_name(irq, &ioapic_chip, |
1289 | handle_fasteoi_irq, "fasteoi"); | 1289 | handle_fasteoi_irq, "fasteoi"); |
1290 | else | 1290 | else { |
1291 | irq_desc[irq].status |= IRQ_DELAYED_DISABLE; | ||
1291 | set_irq_chip_and_handler_name(irq, &ioapic_chip, | 1292 | set_irq_chip_and_handler_name(irq, &ioapic_chip, |
1292 | handle_edge_irq, "edge"); | 1293 | handle_edge_irq, "edge"); |
1294 | } | ||
1293 | set_intr_gate(vector, interrupt[irq]); | 1295 | set_intr_gate(vector, interrupt[irq]); |
1294 | } | 1296 | } |
1295 | 1297 | ||
diff --git a/arch/x86_64/kernel/io_apic.c b/arch/x86_64/kernel/io_apic.c index 41bfc49301ad..14654e682411 100644 --- a/arch/x86_64/kernel/io_apic.c +++ b/arch/x86_64/kernel/io_apic.c | |||
@@ -790,9 +790,11 @@ static void ioapic_register_intr(int irq, int vector, unsigned long trigger) | |||
790 | trigger == IOAPIC_LEVEL) | 790 | trigger == IOAPIC_LEVEL) |
791 | set_irq_chip_and_handler_name(irq, &ioapic_chip, | 791 | set_irq_chip_and_handler_name(irq, &ioapic_chip, |
792 | handle_fasteoi_irq, "fasteoi"); | 792 | handle_fasteoi_irq, "fasteoi"); |
793 | else | 793 | else { |
794 | irq_desc[irq].status |= IRQ_DELAYED_DISABLE; | ||
794 | set_irq_chip_and_handler_name(irq, &ioapic_chip, | 795 | set_irq_chip_and_handler_name(irq, &ioapic_chip, |
795 | handle_edge_irq, "edge"); | 796 | handle_edge_irq, "edge"); |
797 | } | ||
796 | } | 798 | } |
797 | 799 | ||
798 | static void __init setup_IO_APIC_irqs(void) | 800 | static void __init setup_IO_APIC_irqs(void) |