aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2006-11-14 12:52:12 -0500
committerLinus Torvalds <torvalds@woody.osdl.org>2006-11-15 12:04:32 -0500
commit45c99533252ef2297f37c5fdd672a3e0eb566870 (patch)
tree469e706b7a6be19004015e305bdaf4cba4a89a52
parent134a11f0c37c043d3ea557ea15b95b084e3cc2c8 (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.c4
-rw-r--r--arch/x86_64/kernel/io_apic.c4
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
798static void __init setup_IO_APIC_irqs(void) 800static void __init setup_IO_APIC_irqs(void)