[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>

authored by Alan Cox and committed by Linus Torvalds 200803df 21fe3471

+131 -6
+15
Documentation/kernel-parameters.txt
··· 437 Format: {"of[f]" | "sk[ipmbr]"} 438 See comment in arch/i386/boot/edd.S 439 440 eicon= [HW,ISDN] 441 Format: <id>,<membase>,<irq> 442 ··· 625 626 ips= [HW,SCSI] Adaptec / IBM ServeRAID controller 627 See header of drivers/scsi/ips.c. 628 629 isapnp= [ISAPNP] 630 Format: <RDP>, <reset>, <pci_scan>, <verbosity>
··· 437 Format: {"of[f]" | "sk[ipmbr]"} 438 See comment in arch/i386/boot/edd.S 439 440 + edd [EDD] 441 + Format: {"of[f]" | "sk[ipmbr]"} 442 + See comment in arch/i386/boot/edd.S 443 + 444 eicon= [HW,ISDN] 445 Format: <id>,<membase>,<irq> 446 ··· 621 622 ips= [HW,SCSI] Adaptec / IBM ServeRAID controller 623 See header of drivers/scsi/ips.c. 624 + 625 + irqfixup [HW] 626 + When an interrupt is not handled search all handlers 627 + for it. Intended to get systems with badly broken 628 + firmware running. 629 + 630 + irqpoll [HW] 631 + When an interrupt is not handled search all handlers 632 + for it. Also check all handlers each timer 633 + interrupt. Intended to get systems with badly broken 634 + firmware running. 635 636 isapnp= [ISAPNP] 637 Format: <RDP>, <reset>, <pci_scan>, <verbosity>
+1 -1
arch/ppc64/kernel/irq.c
··· 245 246 spin_lock(&desc->lock); 247 if (!noirqdebug) 248 - note_interrupt(irq, desc, action_ret); 249 if (likely(!(desc->status & IRQ_PENDING))) 250 break; 251 desc->status &= ~IRQ_PENDING;
··· 245 246 spin_lock(&desc->lock); 247 if (!noirqdebug) 248 + note_interrupt(irq, desc, action_ret, regs); 249 if (likely(!(desc->status & IRQ_PENDING))) 250 break; 251 desc->status &= ~IRQ_PENDING;
+3 -2
include/linux/irq.h
··· 85 extern int noirqdebug_setup(char *str); 86 87 extern fastcall int handle_IRQ_event(unsigned int irq, struct pt_regs *regs, 88 - struct irqaction *action); 89 extern fastcall unsigned int __do_IRQ(unsigned int irq, struct pt_regs *regs); 90 - extern void note_interrupt(unsigned int irq, irq_desc_t *desc, int action_ret); 91 extern int can_request_irq(unsigned int irq, unsigned long irqflags); 92 93 extern void init_irq_proc(void);
··· 85 extern int noirqdebug_setup(char *str); 86 87 extern fastcall int handle_IRQ_event(unsigned int irq, struct pt_regs *regs, 88 + struct irqaction *action); 89 extern fastcall unsigned int __do_IRQ(unsigned int irq, struct pt_regs *regs); 90 + extern void note_interrupt(unsigned int irq, irq_desc_t *desc, 91 + int action_ret, struct pt_regs *regs); 92 extern int can_request_irq(unsigned int irq, unsigned long irqflags); 93 94 extern void init_irq_proc(void);
+1 -1
kernel/irq/handle.c
··· 172 173 spin_lock(&desc->lock); 174 if (!noirqdebug) 175 - note_interrupt(irq, desc, action_ret); 176 if (likely(!(desc->status & IRQ_PENDING))) 177 break; 178 desc->status &= ~IRQ_PENDING;
··· 172 173 spin_lock(&desc->lock); 174 if (!noirqdebug) 175 + note_interrupt(irq, desc, action_ret, regs); 176 if (likely(!(desc->status & IRQ_PENDING))) 177 break; 178 desc->status &= ~IRQ_PENDING;
+111 -2
kernel/irq/spurious.c
··· 11 #include <linux/kallsyms.h> 12 #include <linux/interrupt.h> 13 14 /* 15 * 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 ··· 108 printk(KERN_ERR "irq event %d: bogus return value %x\n", 109 irq, action_ret); 110 } else { 111 - printk(KERN_ERR "irq %d: nobody cared!\n", irq); 112 } 113 dump_stack(); 114 printk(KERN_ERR "handlers:\n"); ··· 133 } 134 } 135 136 - void note_interrupt(unsigned int irq, irq_desc_t *desc, irqreturn_t action_ret) 137 { 138 if (action_ret != IRQ_HANDLED) { 139 desc->irqs_unhandled++; 140 if (action_ret != IRQ_NONE) 141 report_bad_irq(irq, desc, action_ret); 142 } 143 144 desc->irq_count++; ··· 182 183 __setup("noirqdebug", noirqdebug_setup); 184
··· 11 #include <linux/kallsyms.h> 12 #include <linux/interrupt.h> 13 14 + static int irqfixup; 15 + 16 + /* 17 + * Recovery handler for misrouted interrupts. 18 + */ 19 + 20 + static 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 + 91 /* 92 * If 99,900 of the previous 100,000 interrupts have not been handled 93 * then assume that the IRQ is stuck in some manner. Drop a diagnostic ··· 31 printk(KERN_ERR "irq event %d: bogus return value %x\n", 32 irq, action_ret); 33 } else { 34 + printk(KERN_ERR "irq %d: nobody cared (try booting with " 35 + "the \"irqpoll\" option)\n", irq); 36 } 37 dump_stack(); 38 printk(KERN_ERR "handlers:\n"); ··· 55 } 56 } 57 58 + void note_interrupt(unsigned int irq, irq_desc_t *desc, irqreturn_t action_ret, 59 + struct pt_regs *regs) 60 { 61 if (action_ret != IRQ_HANDLED) { 62 desc->irqs_unhandled++; 63 if (action_ret != IRQ_NONE) 64 report_bad_irq(irq, desc, action_ret); 65 + } 66 + 67 + if (unlikely(irqfixup)) { 68 + /* Don't punish working computers */ 69 + if ((irqfixup == 2 && irq == 0) || action_ret == IRQ_NONE) { 70 + int ok = misrouted_irq(irq, regs); 71 + if (action_ret == IRQ_NONE) 72 + desc->irqs_unhandled -= ok; 73 + } 74 } 75 76 desc->irq_count++; ··· 94 95 __setup("noirqdebug", noirqdebug_setup); 96 97 + static int __init irqfixup_setup(char *str) 98 + { 99 + irqfixup = 1; 100 + printk(KERN_WARNING "Misrouted IRQ fixup support enabled.\n"); 101 + printk(KERN_WARNING "This may impact system performance.\n"); 102 + return 1; 103 + } 104 + 105 + __setup("irqfixup", irqfixup_setup); 106 + 107 + static int __init irqpoll_setup(char *str) 108 + { 109 + irqfixup = 2; 110 + printk(KERN_WARNING "Misrouted IRQ fixup and polling support " 111 + "enabled\n"); 112 + printk(KERN_WARNING "This may significantly impact system " 113 + "performance\n"); 114 + return 1; 115 + } 116 + 117 + __setup("irqpoll", irqpoll_setup);