2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* 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>
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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, irq_desc_t *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!\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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-06-24 13:05:33 +08:00
|
|
|
static void report_bad_irq(unsigned int irq, irq_desc_t *desc, irqreturn_t action_ret)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
static int count = 100;
|
|
|
|
|
|
|
|
if (count > 0) {
|
|
|
|
count--;
|
|
|
|
__report_bad_irq(irq, desc, action_ret);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void note_interrupt(unsigned int irq, irq_desc_t *desc, irqreturn_t action_ret)
|
|
|
|
{
|
|
|
|
if (action_ret != IRQ_HANDLED) {
|
|
|
|
desc->irqs_unhandled++;
|
|
|
|
if (action_ret != IRQ_NONE)
|
|
|
|
report_bad_irq(irq, desc, action_ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
desc->irq_count++;
|
|
|
|
if (desc->irq_count < 100000)
|
|
|
|
return;
|
|
|
|
|
|
|
|
desc->irq_count = 0;
|
|
|
|
if (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->handler->disable(irq);
|
|
|
|
}
|
|
|
|
desc->irqs_unhandled = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int noirqdebug;
|
|
|
|
|
|
|
|
int __init noirqdebug_setup(char *str)
|
|
|
|
{
|
|
|
|
noirqdebug = 1;
|
|
|
|
printk(KERN_INFO "IRQ lockup detection disabled\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
__setup("noirqdebug", noirqdebug_setup);
|
|
|
|
|