Messages in this thread | | | Date | Thu, 02 Sep 2004 16:38:24 -0400 | From | Nathan Bryant <> | Subject | Re: PATCH: prototype "handle broken IRQ routing" patch |
| |
Alan Cox wrote: > This works a treat on several boxes that otherwise simply won't run Linux > in any useful form. The theory is simple - if an IRQ is delivered to the wrong > place then someone somewhere in our IRQ handler lists knows what to do with it.
Wow, this is a crime against humanity! Good job.
There is one problem, it doesn't increment irqs_unhandled when nobody handled anything. I'm sure these broken boxes are still capable of having screaming interrupts too ;)
> > Its if(1) for now, obviously its a boot option and if(my_computer_sucks) in > a final version. > > > diff -u --new-file --recursive --exclude-from /usr/src/exclude linux-2.6.8.1/arch/i386/kernel/irq.c linux-2.6.8.1.ac/arch/i386/kernel/irq.c > --- linux-2.6.8.1/arch/i386/kernel/irq.c 2004-08-14 11:54:48.000000000 +0100 > +++ linux-2.6.8.1.ac/arch/i386/kernel/irq.c 2004-09-03 09:15:17.000000000 +0100 > @@ -280,6 +280,38 @@ > __setup("noirqdebug", noirqdebug_setup); > > /* > + * Recovery handler for misrouted interrupts > + */ > + > +static int misrouted_irq(int irq, irq_desc_t *desc, struct pt_regs *regs) > +{ > + int i; > + for(i = 1; i < NR_IRQS; i++) > + { > + struct irqaction *action; > + if(i == irq) /* Already tried */ > + continue; > + spin_lock(&irq_desc[i].lock); > + if(irq_desc[i].status & IRQ_INPROGRESS) > + { > + spin_unlock(&irq_desc[i].lock); > + continue; > + } > + action = irq_desc[i].action; > + while(action) > + { > + if(action->flags & SA_SHIRQ) > + { > + if(action->handler(irq, action->dev_id, regs) == IRQ_HANDLED) > + desc->irqs_unhandled = 0; > + } > + action = action->next; > + } > + spin_unlock(&irq_desc[i].lock); > + } > +} > + > +/* > * 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. > @@ -289,13 +321,68 @@ > * > * Called under desc->lock > */ > -static void note_interrupt(int irq, irq_desc_t *desc, irqreturn_t action_ret) > +static void note_interrupt(int irq, irq_desc_t *desc, irqreturn_t action_ret, struct pt_regs *regs) > { > if (action_ret != IRQ_HANDLED) { > desc->irqs_unhandled++; > if (action_ret != IRQ_NONE) > report_bad_irq(irq, desc, action_ret); > } > + if(1) > + { > +#ifdef CONFIG_4KSTACKS > + irqreturn_t action_ret; > + u32 *isp; > + union irq_ctx * curctx; > + union irq_ctx * irqctx; > + > + curctx = (union irq_ctx *) current_thread_info(); > + irqctx = hardirq_ctx[smp_processor_id()]; > + > + spin_unlock(&desc->lock); > + > + /* > + * this is where we switch to the IRQ stack. However, if we are already using > + * the IRQ stack (because we interrupted a hardirq handler) we can't do that > + * and just have to keep using the current stack (which is the irq stack already > + * after all) > + */ > + > + if (curctx == irqctx) > + misrouted_irq(irq, desc, regs); > + else { > + /* build the stack frame on the IRQ stack */ > + isp = (u32*) ((char*)irqctx + sizeof(*irqctx)); > + irqctx->tinfo.task = curctx->tinfo.task; > + irqctx->tinfo.previous_esp = current_stack_pointer(); > + > + *--isp = (u32) regs; > + *--isp = (u32) desc; > + *--isp = (u32) irq; > + > + asm volatile( > + " xchgl %%ebx,%%esp \n" > + " call misrouted_irq \n" > + " xchgl %%ebx,%%esp \n" > + : "=a"(action_ret) > + : "b"(isp) > + : "memory", "cc", "edx", "ecx" > + ); > + > + > + } > + spin_lock(&desc->lock); > + if (curctx != irqctx) > + irqctx->tinfo.task = NULL; > +#else > + irqreturn_t action_ret; > + spin_unlock(&desc->lock); > + > + misrouted_irq(irq, desc, regs); > + > + spin_lock(&desc->lock); > +#endif > + } > > desc->irq_count++; > if (desc->irq_count < 100000) > @@ -487,7 +574,7 @@ > * useful for irq hardware that does not mask cleanly in an > * SMP environment. > */ > -#ifdef CONFIG_4KSTACKS > + #ifdef CONFIG_4KSTACKS > > for (;;) { > irqreturn_t action_ret; > @@ -532,7 +619,7 @@ > } > spin_lock(&desc->lock); > if (!noirqdebug) > - note_interrupt(irq, desc, action_ret); > + note_interrupt(irq, desc, action_ret, ®s); > if (curctx != irqctx) > irqctx->tinfo.task = NULL; > if (likely(!(desc->status & IRQ_PENDING))) > @@ -551,7 +638,7 @@ > > spin_lock(&desc->lock); > if (!noirqdebug) > - note_interrupt(irq, desc, action_ret); > + note_interrupt(irq, desc, action_ret, ®s); > if (likely(!(desc->status & IRQ_PENDING))) > break; > desc->status &= ~IRQ_PENDING; > - > To unsubscribe from this list: send the line "unsubscribe linux-kernel" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html > Please read the FAQ at http://www.tux.org/lkml/ > - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
| |