aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Cox <alan@lxorguk.ukuu.org.uk>2005-06-28 23:45:18 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-06-29 00:20:35 -0400
commit200803dfe4ff772740d63db725ab2f1b185ccf92 (patch)
treef567852c984c947f792edb18fee273cfa363d374
parent21fe3471c3aaa5c489c5d3a4d705291eb7511248 (diff)
[PATCH] irqpoll
Anyone reporting a stuck IRQ should try these options. Its effectiveness varies we've found in the Fedora case. Quite a few systems with misdescribed IRQ routing just work when you use irqpoll. It also fixes up the VIA systems although thats now fixed with the VIA quirk (which we could just make default as its what Redmond OS does but Linus didn't like it historically). A small number of systems have jammed IRQ sources or misdescribes that cause an IRQ that we have no handler registered anywhere for. In those cases it doesn't help. Signed-off-by: Alan Cox <number6@the-village.bc.nu> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--Documentation/kernel-parameters.txt15
-rw-r--r--arch/ppc64/kernel/irq.c2
-rw-r--r--include/linux/irq.h5
-rw-r--r--kernel/irq/handle.c2
-rw-r--r--kernel/irq/spurious.c113
5 files changed, 131 insertions, 6 deletions
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 0f71251f12b9..67e99f144199 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -437,6 +437,10 @@ running once the system is up.
437 Format: {"of[f]" | "sk[ipmbr]"} 437 Format: {"of[f]" | "sk[ipmbr]"}
438 See comment in arch/i386/boot/edd.S 438 See comment in arch/i386/boot/edd.S
439 439
440 edd [EDD]
441 Format: {"of[f]" | "sk[ipmbr]"}
442 See comment in arch/i386/boot/edd.S
443
440 eicon= [HW,ISDN] 444 eicon= [HW,ISDN]
441 Format: <id>,<membase>,<irq> 445 Format: <id>,<membase>,<irq>
442 446
@@ -622,6 +626,17 @@ running once the system is up.
622 ips= [HW,SCSI] Adaptec / IBM ServeRAID controller 626 ips= [HW,SCSI] Adaptec / IBM ServeRAID controller
623 See header of drivers/scsi/ips.c. 627 See header of drivers/scsi/ips.c.
624 628
629 irqfixup [HW]
630 When an interrupt is not handled search all handlers
631 for it. Intended to get systems with badly broken
632 firmware running.
633
634 irqpoll [HW]
635 When an interrupt is not handled search all handlers
636 for it. Also check all handlers each timer
637 interrupt. Intended to get systems with badly broken
638 firmware running.
639
625 isapnp= [ISAPNP] 640 isapnp= [ISAPNP]
626 Format: <RDP>, <reset>, <pci_scan>, <verbosity> 641 Format: <RDP>, <reset>, <pci_scan>, <verbosity>
627 642
diff --git a/arch/ppc64/kernel/irq.c b/arch/ppc64/kernel/irq.c
index 3defc8c33adf..ffe300611f00 100644
--- a/arch/ppc64/kernel/irq.c
+++ b/arch/ppc64/kernel/irq.c
@@ -245,7 +245,7 @@ void ppc_irq_dispatch_handler(struct pt_regs *regs, int irq)
245 245
246 spin_lock(&desc->lock); 246 spin_lock(&desc->lock);
247 if (!noirqdebug) 247 if (!noirqdebug)
248 note_interrupt(irq, desc, action_ret); 248 note_interrupt(irq, desc, action_ret, regs);
249 if (likely(!(desc->status & IRQ_PENDING))) 249 if (likely(!(desc->status & IRQ_PENDING)))
250 break; 250 break;
251 desc->status &= ~IRQ_PENDING; 251 desc->status &= ~IRQ_PENDING;
diff --git a/include/linux/irq.h b/include/linux/irq.h
index 12277799c007..069d3b84d311 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -85,9 +85,10 @@ extern int no_irq_affinity;
85extern int noirqdebug_setup(char *str); 85extern int noirqdebug_setup(char *str);
86 86
87extern fastcall int handle_IRQ_event(unsigned int irq, struct pt_regs *regs, 87extern fastcall int handle_IRQ_event(unsigned int irq, struct pt_regs *regs,
88 struct irqaction *action); 88 struct irqaction *action);
89extern fastcall unsigned int __do_IRQ(unsigned int irq, struct pt_regs *regs); 89extern fastcall unsigned int __do_IRQ(unsigned int irq, struct pt_regs *regs);
90extern void note_interrupt(unsigned int irq, irq_desc_t *desc, int action_ret); 90extern void note_interrupt(unsigned int irq, irq_desc_t *desc,
91 int action_ret, struct pt_regs *regs);
91extern int can_request_irq(unsigned int irq, unsigned long irqflags); 92extern int can_request_irq(unsigned int irq, unsigned long irqflags);
92 93
93extern void init_irq_proc(void); 94extern void init_irq_proc(void);
diff --git a/kernel/irq/handle.c b/kernel/irq/handle.c
index 436c7d93c00a..c29f83c16497 100644
--- a/kernel/irq/handle.c
+++ b/kernel/irq/handle.c
@@ -172,7 +172,7 @@ fastcall unsigned int __do_IRQ(unsigned int irq, struct pt_regs *regs)
172 172
173 spin_lock(&desc->lock); 173 spin_lock(&desc->lock);
174 if (!noirqdebug) 174 if (!noirqdebug)
175 note_interrupt(irq, desc, action_ret); 175 note_interrupt(irq, desc, action_ret, regs);
176 if (likely(!(desc->status & IRQ_PENDING))) 176 if (likely(!(desc->status & IRQ_PENDING)))
177 break; 177 break;
178 desc->status &= ~IRQ_PENDING; 178 desc->status &= ~IRQ_PENDING;
diff --git a/kernel/irq/spurious.c b/kernel/irq/spurious.c
index ba039e827d58..7df9abd5ec86 100644
--- a/kernel/irq/spurious.c
+++ b/kernel/irq/spurious.c
@@ -11,6 +11,83 @@
11#include <linux/kallsyms.h> 11#include <linux/kallsyms.h>
12#include <linux/interrupt.h> 12#include <linux/interrupt.h>
13 13
14static int irqfixup;
15
16/*
17 * Recovery handler for misrouted interrupts.
18 */
19
20static int misrouted_irq(int irq, struct pt_regs *regs)
21{
22 int i;
23 irq_desc_t *desc;
24 int ok = 0;
25 int work = 0; /* Did we do work for a real IRQ */
26
27 for(i = 1; i < NR_IRQS; i++) {
28 struct irqaction *action;
29
30 if (i == irq) /* Already tried */
31 continue;
32 desc = &irq_desc[i];
33 spin_lock(&desc->lock);
34 action = desc->action;
35 /* Already running on another processor */
36 if (desc->status & IRQ_INPROGRESS) {
37 /*
38 * Already running: If it is shared get the other
39 * CPU to go looking for our mystery interrupt too
40 */
41 if (desc->action && (desc->action->flags & SA_SHIRQ))
42 desc->status |= IRQ_PENDING;
43 spin_unlock(&desc->lock);
44 continue;
45 }
46 /* Honour the normal IRQ locking */
47 desc->status |= IRQ_INPROGRESS;
48 spin_unlock(&desc->lock);
49 while (action) {
50 /* Only shared IRQ handlers are safe to call */
51 if (action->flags & SA_SHIRQ) {
52 if (action->handler(i, action->dev_id, regs) ==
53 IRQ_HANDLED)
54 ok = 1;
55 }
56 action = action->next;
57 }
58 local_irq_disable();
59 /* Now clean up the flags */
60 spin_lock(&desc->lock);
61 action = desc->action;
62
63 /*
64 * While we were looking for a fixup someone queued a real
65 * IRQ clashing with our walk
66 */
67
68 while ((desc->status & IRQ_PENDING) && action) {
69 /*
70 * Perform real IRQ processing for the IRQ we deferred
71 */
72 work = 1;
73 spin_unlock(&desc->lock);
74 handle_IRQ_event(i, regs, action);
75 spin_lock(&desc->lock);
76 desc->status &= ~IRQ_PENDING;
77 }
78 desc->status &= ~IRQ_INPROGRESS;
79 /*
80 * If we did actual work for the real IRQ line we must let the
81 * IRQ controller clean up too
82 */
83 if(work)
84 desc->handler->end(i);
85 spin_unlock(&desc->lock);
86 }
87 /* So the caller can adjust the irq error counts */
88 return ok;
89}
90
14/* 91/*
15 * If 99,900 of the previous 100,000 interrupts have not been handled 92 * If 99,900 of the previous 100,000 interrupts have not been handled
16 * then assume that the IRQ is stuck in some manner. Drop a diagnostic 93 * then assume that the IRQ is stuck in some manner. Drop a diagnostic
@@ -31,7 +108,8 @@ __report_bad_irq(unsigned int irq, irq_desc_t *desc, irqreturn_t action_ret)
31 printk(KERN_ERR "irq event %d: bogus return value %x\n", 108 printk(KERN_ERR "irq event %d: bogus return value %x\n",
32 irq, action_ret); 109 irq, action_ret);
33 } else { 110 } else {
34 printk(KERN_ERR "irq %d: nobody cared!\n", irq); 111 printk(KERN_ERR "irq %d: nobody cared (try booting with "
112 "the \"irqpoll\" option)\n", irq);
35 } 113 }
36 dump_stack(); 114 dump_stack();
37 printk(KERN_ERR "handlers:\n"); 115 printk(KERN_ERR "handlers:\n");
@@ -55,7 +133,8 @@ static void report_bad_irq(unsigned int irq, irq_desc_t *desc, irqreturn_t actio
55 } 133 }
56} 134}
57 135
58void note_interrupt(unsigned int irq, irq_desc_t *desc, irqreturn_t action_ret) 136void note_interrupt(unsigned int irq, irq_desc_t *desc, irqreturn_t action_ret,
137 struct pt_regs *regs)
59{ 138{
60 if (action_ret != IRQ_HANDLED) { 139 if (action_ret != IRQ_HANDLED) {
61 desc->irqs_unhandled++; 140 desc->irqs_unhandled++;
@@ -63,6 +142,15 @@ void note_interrupt(unsigned int irq, irq_desc_t *desc, irqreturn_t action_ret)
63 report_bad_irq(irq, desc, action_ret); 142 report_bad_irq(irq, desc, action_ret);
64 } 143 }
65 144
145 if (unlikely(irqfixup)) {
146 /* Don't punish working computers */
147 if ((irqfixup == 2 && irq == 0) || action_ret == IRQ_NONE) {
148 int ok = misrouted_irq(irq, regs);
149 if (action_ret == IRQ_NONE)
150 desc->irqs_unhandled -= ok;
151 }
152 }
153
66 desc->irq_count++; 154 desc->irq_count++;
67 if (desc->irq_count < 100000) 155 if (desc->irq_count < 100000)
68 return; 156 return;
@@ -94,3 +182,24 @@ int __init noirqdebug_setup(char *str)
94 182
95__setup("noirqdebug", noirqdebug_setup); 183__setup("noirqdebug", noirqdebug_setup);
96 184
185static int __init irqfixup_setup(char *str)
186{
187 irqfixup = 1;
188 printk(KERN_WARNING "Misrouted IRQ fixup support enabled.\n");
189 printk(KERN_WARNING "This may impact system performance.\n");
190 return 1;
191}
192
193__setup("irqfixup", irqfixup_setup);
194
195static int __init irqpoll_setup(char *str)
196{
197 irqfixup = 2;
198 printk(KERN_WARNING "Misrouted IRQ fixup and polling support "
199 "enabled\n");
200 printk(KERN_WARNING "This may significantly impact system "
201 "performance\n");
202 return 1;
203}
204
205__setup("irqpoll", irqpoll_setup);