提交 f84dbb91 编写于 作者: E Eric W. Biederman 提交者: Ingo Molnar

genirq: enable polling for disabled screaming irqs

When we disable a screaming irq we never see it again.  If the irq
line is shared or if the driver half works this is a real pain.  So
periodically poll the handlers for screaming interrupts.

I use a timer instead of the classic irq poll technique of working off
the timer interrupt because when we use the local apic timers
note_interrupt is never called (bug?).  Further on a system with
dynamic ticks the timer interrupt might not even fire unless there is
a timer telling it it needs to.

I forced this case on my test system with an e1000 nic and my ssh
session remained responsive despite the interrupt handler only being
called every 10th of a second.
Signed-off-by: NEric W. Biederman <ebiederm@xmission.com>
Signed-off-by: NIngo Molnar <mingo@elte.hu>
上级 5b664cb2
...@@ -12,83 +12,117 @@ ...@@ -12,83 +12,117 @@
#include <linux/kallsyms.h> #include <linux/kallsyms.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/timer.h>
static int irqfixup __read_mostly; static int irqfixup __read_mostly;
#define POLL_SPURIOUS_IRQ_INTERVAL (HZ/10)
static void poll_spurious_irqs(unsigned long dummy);
static DEFINE_TIMER(poll_spurious_irq_timer, poll_spurious_irqs, 0, 0);
/* /*
* Recovery handler for misrouted interrupts. * Recovery handler for misrouted interrupts.
*/ */
static int misrouted_irq(int irq) static int try_one_irq(int irq, struct irq_desc *desc)
{ {
int i; struct irqaction *action;
int ok = 0; int ok = 0;
int work = 0; /* Did we do work for a real IRQ */ int work = 0; /* Did we do work for a real IRQ */
for (i = 1; i < NR_IRQS; i++) { spin_lock(&desc->lock);
struct irq_desc *desc = irq_desc + i; /* Already running on another processor */
struct irqaction *action; if (desc->status & IRQ_INPROGRESS) {
/*
if (i == irq) /* Already tried */ * Already running: If it is shared get the other
continue; * CPU to go looking for our mystery interrupt too
*/
spin_lock(&desc->lock); if (desc->action && (desc->action->flags & IRQF_SHARED))
/* Already running on another processor */ desc->status |= IRQ_PENDING;
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 & IRQF_SHARED))
desc->status |= IRQ_PENDING;
spin_unlock(&desc->lock);
continue;
}
/* Honour the normal IRQ locking */
desc->status |= IRQ_INPROGRESS;
action = desc->action;
spin_unlock(&desc->lock); spin_unlock(&desc->lock);
return ok;
}
/* Honour the normal IRQ locking */
desc->status |= IRQ_INPROGRESS;
action = desc->action;
spin_unlock(&desc->lock);
while (action) { while (action) {
/* Only shared IRQ handlers are safe to call */ /* Only shared IRQ handlers are safe to call */
if (action->flags & IRQF_SHARED) { if (action->flags & IRQF_SHARED) {
if (action->handler(i, action->dev_id) == if (action->handler(irq, action->dev_id) ==
IRQ_HANDLED) IRQ_HANDLED)
ok = 1; ok = 1;
}
action = action->next;
} }
local_irq_disable(); action = action->next;
/* Now clean up the flags */ }
spin_lock(&desc->lock); local_irq_disable();
action = desc->action; /* 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) {
/* /*
* While we were looking for a fixup someone queued a real * Perform real IRQ processing for the IRQ we deferred
* 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, 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->chip && desc->chip->end) work = 1;
desc->chip->end(i);
spin_unlock(&desc->lock); spin_unlock(&desc->lock);
handle_IRQ_event(irq, 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->chip && desc->chip->end)
desc->chip->end(irq);
spin_unlock(&desc->lock);
return ok;
}
static int misrouted_irq(int irq)
{
int i;
int ok = 0;
for (i = 1; i < NR_IRQS; i++) {
struct irq_desc *desc = irq_desc + i;
if (i == irq) /* Already tried */
continue;
if (try_one_irq(i, desc))
ok = 1;
} }
/* So the caller can adjust the irq error counts */ /* So the caller can adjust the irq error counts */
return ok; return ok;
} }
static void poll_spurious_irqs(unsigned long dummy)
{
int i;
for (i = 1; i < NR_IRQS; i++) {
struct irq_desc *desc = irq_desc + i;
unsigned int status;
/* Racy but it doesn't matter */
status = desc->status;
barrier();
if (!(status & IRQ_SPURIOUS_DISABLED))
continue;
try_one_irq(i, desc);
}
mod_timer(&poll_spurious_irq_timer, jiffies + POLL_SPURIOUS_IRQ_INTERVAL);
}
/* /*
* If 99,900 of the previous 100,000 interrupts have not been handled * 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 * then assume that the IRQ is stuck in some manner. Drop a diagnostic
...@@ -212,6 +246,8 @@ void note_interrupt(unsigned int irq, struct irq_desc *desc, ...@@ -212,6 +246,8 @@ void note_interrupt(unsigned int irq, struct irq_desc *desc,
desc->status |= IRQ_DISABLED | IRQ_SPURIOUS_DISABLED; desc->status |= IRQ_DISABLED | IRQ_SPURIOUS_DISABLED;
desc->depth++; desc->depth++;
desc->chip->disable(irq); desc->chip->disable(irq);
mod_timer(&poll_spurious_irq_timer, jiffies + POLL_SPURIOUS_IRQ_INTERVAL);
} }
desc->irqs_unhandled = 0; desc->irqs_unhandled = 0;
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册