Patch from James Harper <james.harper@bigpond.com>

I propose the attached patch to arch/i386/kernel/irq.c.  it corrects what i
see as a bug in interrupt handling.

currently if a driver requests SA_INTERRUPT in an interrupt handler, it is
only called with interrupts disabled if it is the first handler in the list.

my patch modifies setup_irq to put any interrupt with SA_INTERRUPT in the
front of the handler queue (eg before any handlers without the flag).

and also modifies handle_IRQ_event to only enable interrupts when it hits the
first handler with SA_INTERRUPT not set.

the patch is against 2.5.62 and greatly improves stability on my SMP system.




 arch/i386/kernel/irq.c |   51 ++++++++++++++++++++++++++++++++-----------------
 1 files changed, 34 insertions(+), 17 deletions(-)

diff -puN arch/i386/kernel/irq.c~irq-sharing-fix arch/i386/kernel/irq.c
--- 25/arch/i386/kernel/irq.c~irq-sharing-fix	2003-02-24 23:31:35.000000000 -0800
+++ 25-akpm/arch/i386/kernel/irq.c	2003-02-24 23:31:35.000000000 -0800
@@ -207,11 +207,13 @@ inline void synchronize_irq(unsigned int
 int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, struct irqaction * action)
 {
 	int status = 1;	/* Force the "do bottom halves" bit */
-
-	if (!(action->flags & SA_INTERRUPT))
-		local_irq_enable();
+	int enabled = SA_INTERRUPT;
 
 	do {
+		if (~action->flags & enabled) {
+			local_irq_enable();
+			enabled = 0;
+		}
 		status |= action->flags;
 		action->handler(irq, action->dev_id, regs);
 		action = action->next;
@@ -770,24 +772,39 @@ int setup_irq(unsigned int irq, struct i
 	 * The following block of code has to be executed atomically
 	 */
 	spin_lock_irqsave(&desc->lock,flags);
-	p = &desc->action;
-	if ((old = *p) != NULL) {
-		/* Can't share interrupts unless both agree to */
-		if (!(old->flags & new->flags & SA_SHIRQ)) {
-			spin_unlock_irqrestore(&desc->lock,flags);
-			return -EBUSY;
+	if (new->flags & SA_INTERRUPT) {
+		p = &desc->action;
+		old = *p;
+		/* Add SA_INTERRUPT interrupts to the front of the queue
+		 * 'cos it's faster than adding them in the middle */
+		if (old) {
+			if (!(old->flags & new->flags & SA_SHIRQ)) {
+				spin_unlock_irqrestore(&desc->lock,flags);
+				return -EBUSY;
+			}
+			new->next = old;
+			shared = 1;
 		}
+		*p = new;
+	} else {
+		p = &desc->action;
+		if ((old = *p) != NULL) {
+			/* Can't share interrupts unless both agree to */
+			if (!(old->flags & new->flags & SA_SHIRQ)) {
+				spin_unlock_irqrestore(&desc->lock,flags);
+				return -EBUSY;
+			}
 
-		/* add new interrupt at end of irq queue */
-		do {
-			p = &old->next;
-			old = *p;
-		} while (old);
-		shared = 1;
+			/* add new interrupt at end of irq queue */
+			do {
+				p = &old->next;
+				old = *p;
+			} while (old);
+			shared = 1;
+		}
+		*p = new;
 	}
 
-	*p = new;
-
 	if (!shared) {
 		desc->depth = 0;
 		desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT | IRQ_WAITING | IRQ_INPROGRESS);

_