|
@@ -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
|
|
@@ -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");
|
|
@@ -55,7 +133,8 @@ 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++;
|
|
@@ -63,6 +142,15 @@ void note_interrupt(unsigned int irq, irq_desc_t *desc, irqreturn_t action_ret)
|
|
|
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;
|
|
@@ -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);
|