d85a60d85e
irqpoll is broken on some architectures that don't use the IRQ 0 for the timer interrupt like IA64. This patch adds a IRQF_IRQPOLL flag. Each architecture is handled in a separate pach. As I left the irq == 0 as condition, this should not break existing architectures that use timer_irq == 0 and that I did't address with that patch (because I don't know). This patch: This patch adds a IRQF_IRQPOLL flag that the interrupt registration code could use for the interrupt it wants to use for IRQ polling. Because this must not be the timer interrupt, an additional flag was added instead of re-using the IRQF_TIMER constant. Until all architectures will have an IRQF_IRQPOLL interrupt, irq == 0 will stay as alternative as it should not break anything. Also, note_interrupt() is called on CPU-specific interrupts to be used as interrupt source for IRQ polling. Signed-off-by: Bernhard Walle <bwalle@suse.de> Cc: Russell King <rmk@arm.linux.org.uk> Cc: Kyle McMartin <kyle@mcmartin.ca> Cc: Matthew Wilcox <willy@debian.org> Cc: Grant Grundler <grundler@google.com> Cc: Paul Mundt <lethal@linux-sh.org> Cc: "Luck, Tony" <tony.luck@intel.com> Cc: Ingo Molnar <mingo@elte.hu> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
213 lines
4.8 KiB
C
213 lines
4.8 KiB
C
/*
|
|
* linux/kernel/irq/spurious.c
|
|
*
|
|
* Copyright (C) 1992, 1998-2004 Linus Torvalds, Ingo Molnar
|
|
*
|
|
* This file contains spurious interrupt handling.
|
|
*/
|
|
|
|
#include <linux/irq.h>
|
|
#include <linux/module.h>
|
|
#include <linux/kallsyms.h>
|
|
#include <linux/interrupt.h>
|
|
|
|
static int irqfixup __read_mostly;
|
|
|
|
/*
|
|
* Recovery handler for misrouted interrupts.
|
|
*/
|
|
static int misrouted_irq(int irq)
|
|
{
|
|
int i;
|
|
int ok = 0;
|
|
int work = 0; /* Did we do work for a real IRQ */
|
|
|
|
for (i = 1; i < NR_IRQS; i++) {
|
|
struct irq_desc *desc = irq_desc + i;
|
|
struct irqaction *action;
|
|
|
|
if (i == irq) /* Already tried */
|
|
continue;
|
|
|
|
spin_lock(&desc->lock);
|
|
/* 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 & 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);
|
|
|
|
while (action) {
|
|
/* Only shared IRQ handlers are safe to call */
|
|
if (action->flags & IRQF_SHARED) {
|
|
if (action->handler(i, action->dev_id) ==
|
|
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, 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(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
|
|
* and try to turn the IRQ off.
|
|
*
|
|
* (The other 100-of-100,000 interrupts may have been a correctly
|
|
* functioning device sharing an IRQ with the failing one)
|
|
*
|
|
* Called under desc->lock
|
|
*/
|
|
|
|
static void
|
|
__report_bad_irq(unsigned int irq, struct irq_desc *desc,
|
|
irqreturn_t action_ret)
|
|
{
|
|
struct irqaction *action;
|
|
|
|
if (action_ret != IRQ_HANDLED && action_ret != IRQ_NONE) {
|
|
printk(KERN_ERR "irq event %d: bogus return value %x\n",
|
|
irq, action_ret);
|
|
} else {
|
|
printk(KERN_ERR "irq %d: nobody cared (try booting with "
|
|
"the \"irqpoll\" option)\n", irq);
|
|
}
|
|
dump_stack();
|
|
printk(KERN_ERR "handlers:\n");
|
|
|
|
action = desc->action;
|
|
while (action) {
|
|
printk(KERN_ERR "[<%p>]", action->handler);
|
|
print_symbol(" (%s)",
|
|
(unsigned long)action->handler);
|
|
printk("\n");
|
|
action = action->next;
|
|
}
|
|
}
|
|
|
|
static void
|
|
report_bad_irq(unsigned int irq, struct irq_desc *desc, irqreturn_t action_ret)
|
|
{
|
|
static int count = 100;
|
|
|
|
if (count > 0) {
|
|
count--;
|
|
__report_bad_irq(irq, desc, action_ret);
|
|
}
|
|
}
|
|
|
|
void note_interrupt(unsigned int irq, struct irq_desc *desc,
|
|
irqreturn_t action_ret)
|
|
{
|
|
if (unlikely(action_ret != IRQ_HANDLED)) {
|
|
desc->irqs_unhandled++;
|
|
if (unlikely(action_ret != IRQ_NONE))
|
|
report_bad_irq(irq, desc, action_ret);
|
|
}
|
|
|
|
if (unlikely(irqfixup)) {
|
|
/* Don't punish working computers */
|
|
if ((irqfixup == 2 && ((irq == 0) ||
|
|
(desc->action->flags & IRQF_IRQPOLL))) ||
|
|
action_ret == IRQ_NONE) {
|
|
int ok = misrouted_irq(irq);
|
|
if (action_ret == IRQ_NONE)
|
|
desc->irqs_unhandled -= ok;
|
|
}
|
|
}
|
|
|
|
desc->irq_count++;
|
|
if (likely(desc->irq_count < 100000))
|
|
return;
|
|
|
|
desc->irq_count = 0;
|
|
if (unlikely(desc->irqs_unhandled > 99900)) {
|
|
/*
|
|
* The interrupt is stuck
|
|
*/
|
|
__report_bad_irq(irq, desc, action_ret);
|
|
/*
|
|
* Now kill the IRQ
|
|
*/
|
|
printk(KERN_EMERG "Disabling IRQ #%d\n", irq);
|
|
desc->status |= IRQ_DISABLED;
|
|
desc->depth = 1;
|
|
desc->chip->disable(irq);
|
|
}
|
|
desc->irqs_unhandled = 0;
|
|
}
|
|
|
|
int noirqdebug __read_mostly;
|
|
|
|
int noirqdebug_setup(char *str)
|
|
{
|
|
noirqdebug = 1;
|
|
printk(KERN_INFO "IRQ lockup detection disabled\n");
|
|
|
|
return 1;
|
|
}
|
|
|
|
__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);
|