Skip to content

Commit

Permalink
[PATCH] irqpoll
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Alan Cox authored and Linus Torvalds committed Jun 29, 2005
1 parent 21fe347 commit 200803d
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 6 deletions.
15 changes: 15 additions & 0 deletions Documentation/kernel-parameters.txt
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,10 @@ running once the system is up.
Format: {"of[f]" | "sk[ipmbr]"}
See comment in arch/i386/boot/edd.S

edd [EDD]
Format: {"of[f]" | "sk[ipmbr]"}
See comment in arch/i386/boot/edd.S

eicon= [HW,ISDN]
Format: <id>,<membase>,<irq>

Expand Down Expand Up @@ -622,6 +626,17 @@ running once the system is up.
ips= [HW,SCSI] Adaptec / IBM ServeRAID controller
See header of drivers/scsi/ips.c.

irqfixup [HW]
When an interrupt is not handled search all handlers
for it. Intended to get systems with badly broken
firmware running.

irqpoll [HW]
When an interrupt is not handled search all handlers
for it. Also check all handlers each timer
interrupt. Intended to get systems with badly broken
firmware running.

isapnp= [ISAPNP]
Format: <RDP>, <reset>, <pci_scan>, <verbosity>

Expand Down
2 changes: 1 addition & 1 deletion arch/ppc64/kernel/irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ void ppc_irq_dispatch_handler(struct pt_regs *regs, int irq)

spin_lock(&desc->lock);
if (!noirqdebug)
note_interrupt(irq, desc, action_ret);
note_interrupt(irq, desc, action_ret, regs);
if (likely(!(desc->status & IRQ_PENDING)))
break;
desc->status &= ~IRQ_PENDING;
Expand Down
5 changes: 3 additions & 2 deletions include/linux/irq.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,10 @@ extern int no_irq_affinity;
extern int noirqdebug_setup(char *str);

extern fastcall int handle_IRQ_event(unsigned int irq, struct pt_regs *regs,
struct irqaction *action);
struct irqaction *action);
extern fastcall unsigned int __do_IRQ(unsigned int irq, struct pt_regs *regs);
extern void note_interrupt(unsigned int irq, irq_desc_t *desc, int action_ret);
extern void note_interrupt(unsigned int irq, irq_desc_t *desc,
int action_ret, struct pt_regs *regs);
extern int can_request_irq(unsigned int irq, unsigned long irqflags);

extern void init_irq_proc(void);
Expand Down
2 changes: 1 addition & 1 deletion kernel/irq/handle.c
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ fastcall unsigned int __do_IRQ(unsigned int irq, struct pt_regs *regs)

spin_lock(&desc->lock);
if (!noirqdebug)
note_interrupt(irq, desc, action_ret);
note_interrupt(irq, desc, action_ret, regs);
if (likely(!(desc->status & IRQ_PENDING)))
break;
desc->status &= ~IRQ_PENDING;
Expand Down
113 changes: 111 additions & 2 deletions kernel/irq/spurious.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,83 @@
#include <linux/kallsyms.h>
#include <linux/interrupt.h>

static int irqfixup;

/*
* Recovery handler for misrouted interrupts.
*/

static int misrouted_irq(int irq, struct pt_regs *regs)
{
int i;
irq_desc_t *desc;
int ok = 0;
int work = 0; /* Did we do work for a real IRQ */

for(i = 1; i < NR_IRQS; i++) {
struct irqaction *action;

if (i == irq) /* Already tried */
continue;
desc = &irq_desc[i];
spin_lock(&desc->lock);
action = desc->action;
/* Already running on another processor */
if (desc->status & IRQ_INPROGRESS) {
/*
* Already running: If it is shared get the other
* CPU to go looking for our mystery interrupt too
*/
if (desc->action && (desc->action->flags & SA_SHIRQ))
desc->status |= IRQ_PENDING;
spin_unlock(&desc->lock);
continue;
}
/* Honour the normal IRQ locking */
desc->status |= IRQ_INPROGRESS;
spin_unlock(&desc->lock);
while (action) {
/* Only shared IRQ handlers are safe to call */
if (action->flags & SA_SHIRQ) {
if (action->handler(i, action->dev_id, regs) ==
IRQ_HANDLED)
ok = 1;
}
action = action->next;
}
local_irq_disable();
/* Now clean up the flags */
spin_lock(&desc->lock);
action = desc->action;

/*
* While we were looking for a fixup someone queued a real
* IRQ clashing with our walk
*/

while ((desc->status & IRQ_PENDING) && action) {
/*
* Perform real IRQ processing for the IRQ we deferred
*/
work = 1;
spin_unlock(&desc->lock);
handle_IRQ_event(i, regs, action);
spin_lock(&desc->lock);
desc->status &= ~IRQ_PENDING;
}
desc->status &= ~IRQ_INPROGRESS;
/*
* If we did actual work for the real IRQ line we must let the
* IRQ controller clean up too
*/
if(work)
desc->handler->end(i);
spin_unlock(&desc->lock);
}
/* So the caller can adjust the irq error counts */
return ok;
}

/*
* If 99,900 of the previous 100,000 interrupts have not been handled
* then assume that the IRQ is stuck in some manner. Drop a diagnostic
Expand All @@ -31,7 +108,8 @@ __report_bad_irq(unsigned int irq, irq_desc_t *desc, irqreturn_t action_ret)
printk(KERN_ERR "irq event %d: bogus return value %x\n",
irq, action_ret);
} else {
printk(KERN_ERR "irq %d: nobody cared!\n", irq);
printk(KERN_ERR "irq %d: nobody cared (try booting with "
"the \"irqpoll\" option)\n", irq);
}
dump_stack();
printk(KERN_ERR "handlers:\n");
Expand All @@ -55,14 +133,24 @@ static void report_bad_irq(unsigned int irq, irq_desc_t *desc, irqreturn_t actio
}
}

void note_interrupt(unsigned int irq, irq_desc_t *desc, irqreturn_t action_ret)
void note_interrupt(unsigned int irq, irq_desc_t *desc, irqreturn_t action_ret,
struct pt_regs *regs)
{
if (action_ret != IRQ_HANDLED) {
desc->irqs_unhandled++;
if (action_ret != IRQ_NONE)
report_bad_irq(irq, desc, action_ret);
}

if (unlikely(irqfixup)) {
/* Don't punish working computers */
if ((irqfixup == 2 && irq == 0) || action_ret == IRQ_NONE) {
int ok = misrouted_irq(irq, regs);
if (action_ret == IRQ_NONE)
desc->irqs_unhandled -= ok;
}
}

desc->irq_count++;
if (desc->irq_count < 100000)
return;
Expand Down Expand Up @@ -94,3 +182,24 @@ int __init noirqdebug_setup(char *str)

__setup("noirqdebug", noirqdebug_setup);

static int __init irqfixup_setup(char *str)
{
irqfixup = 1;
printk(KERN_WARNING "Misrouted IRQ fixup support enabled.\n");
printk(KERN_WARNING "This may impact system performance.\n");
return 1;
}

__setup("irqfixup", irqfixup_setup);

static int __init irqpoll_setup(char *str)
{
irqfixup = 2;
printk(KERN_WARNING "Misrouted IRQ fixup and polling support "
"enabled\n");
printk(KERN_WARNING "This may significantly impact system "
"performance\n");
return 1;
}

__setup("irqpoll", irqpoll_setup);

0 comments on commit 200803d

Please sign in to comment.