diff options
Diffstat (limited to 'kernel/irq/spurious.c')
| -rw-r--r-- | kernel/irq/spurious.c | 113 | 
1 files changed, 111 insertions, 2 deletions
diff --git a/kernel/irq/spurious.c b/kernel/irq/spurious.c index ba039e827d58..7df9abd5ec86 100644 --- a/kernel/irq/spurious.c +++ b/kernel/irq/spurious.c @@ -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);  | 
