[parisc-linux-cvs] [ DIFF ] shorten IRQ code path

Grant Grundler grundler@puffin.external.hp.com
Tue, 12 Jun 2001 10:45:49 -0600


Hey all,
Short code path between entry.S and call to device driver ISR.
Basically, call CPU specific IRQ mask handler (do_cpu_irq_mask())
allows me to remove several function calls and branches from the
code path. Cleaned up prumpf's cruft in the process.

PARISC_IRQ_CR16_COUNTS ifdef in irq.c needs two fields added
to include/linux/interrupt.h:struct irqaction before it can be
enabled. I've done that locally but need to discuss adding
stuff to generic header file with upstream (Ingo Molnar).
This might get removed/changed in the future but I'm too lazy
to "unmerge" at the moment. :^)

Richard reminded that I forgot to update the extraversion.
If that's critical, then I can do it - but I'm not fixing
any bugs - introducing some at worst.

grant


Index: arch/parisc/kernel/entry.S
===================================================================
RCS file: /home/cvs/parisc/linux/arch/parisc/kernel/entry.S,v
retrieving revision 1.84
retrieving revision 1.85
diff -u -p -r1.84 -r1.85
--- entry.S	2001/05/30 13:25:15	1.84
+++ entry.S	2001/06/12 16:09:52	1.85
@@ -101,7 +101,7 @@
 	 * either the fault type ("code") or the eirr. We need
 	 * to use a non-shadowed register to carry the value over
 	 * the rfir in virt_map. We use %r26 since this value winds
-	 * up being passed as the argument to either do_irq_mask
+	 * up being passed as the argument to either do_cpu_irq_mask
 	 * or handle_interruption. %r29 is used to hold a pointer
 	 * the register save area, and once again, it needs to
 	 * be a non-shadowed register so that it survives the rfir.
@@ -161,7 +161,7 @@
 	.endm
 
 	/* Interrupt interruption handler
-	 * (calls irq.c:do_irq_mask) */
+	 * (calls irq.c:do_cpu_irq_mask) */
 	.macro	extint code
 	mfctl   %cr23, %r8
 	b	intr_extint
@@ -479,7 +479,7 @@ fault_vector_11:
 
 	.import		handle_interruption,code
 	.import		handle_real_interruption,code
-	.import		do_irq_mask,code
+	.import		do_cpu_irq_mask,code
 	.import		parisc_stopkernel,code
 	.import		cpu_irq_region,data
 
@@ -789,7 +789,7 @@ intr_do_signal:
 	nop
 
 	/*
-	 * External interrupts. r8 contains argument for do_irq_mask.
+	 * External interrupts. r8 contains argument for do_cpu_irq_mask.
 	 * get_stack moves value of r8 to r26.
 	 */
 
@@ -810,14 +810,17 @@ intr_extint:
 	copy		%r29, %r3	/* KWDB - update frame pointer (gr3) */
 #endif
 
-	/* sorry to put this cruft in the interrupt path */
+	/*
+	** We need to either load the CPU's ID or IRQ region.
+	** Until we have a "per CPU" IRQ regions, this is easy.
+	*/
 	ldil		L%cpu_irq_region, %r25
 	ldo		R%cpu_irq_region(%r25), %r25
+
+	bl		do_cpu_irq_mask,%r2
 #ifdef __LP64__
-	bl		do_irq_mask,%r2
 	ldo		-16(%r30),%r29	/* Reference param save area */
 #else
-	bl		do_irq_mask,%r2
 	nop
 #endif
 
Index: arch/parisc/kernel/irq.c
===================================================================
RCS file: /home/cvs/parisc/linux/arch/parisc/kernel/irq.c,v
retrieving revision 1.38
retrieving revision 1.39
diff -u -p -r1.38 -r1.39
--- irq.c	2001/03/31 07:40:10	1.38
+++ irq.c	2001/06/12 16:09:52	1.39
@@ -42,6 +42,7 @@
 #include <asm/cache.h>
 
 #undef DEBUG_IRQ
+#undef PARISC_IRQ_CR16_COUNTS
 
 extern void timer_interrupt(int, void *, struct pt_regs *);
 #ifdef CONFIG_SMP
@@ -60,7 +61,7 @@ extern void ipi_interrupt(int, void *, s
 
 static spinlock_t irq_lock = SPIN_LOCK_UNLOCKED;  /* protect IRQ regions */
 
-static void disable_cpu_irq(void *unused, int irq)
+static inline void disable_cpu_irq(void *unused, int irq)
 {
 	CLEAR_EIEM_BIT(irq);
 }
@@ -73,6 +74,17 @@ static void enable_cpu_irq(void *unused,
 	SET_EIEM_BIT(irq);
 }
 
+/* mask and disable are the same at the CPU level
+** Difference is enable clears pending interrupts
+*/
+#define mask_cpu_irq	disable_cpu_irq
+
+static inline void unmask_cpu_irq(void *unused, int irq)
+{
+	SET_EIEM_BIT(irq);
+}
+
+
 static struct irqaction cpu_irq_actions[IRQ_PER_REGION] = {
 	[IRQ_OFFSET(TIMER_IRQ)] { timer_interrupt, 0, 0, "timer", NULL, NULL },
 #ifdef CONFIG_SMP
@@ -81,7 +93,7 @@ static struct irqaction cpu_irq_actions[
 };
 
 struct irq_region cpu_irq_region = {
-	{ disable_cpu_irq, enable_cpu_irq, NULL, NULL },
+	{ disable_cpu_irq, enable_cpu_irq, unmask_cpu_irq, unmask_cpu_irq },
 	{ &cpu_data[0], "PA-CPU-00", IRQ_REG_MASK|IRQ_REG_DIS, IRQ_FROM_REGION(CPU_IRQ_REGION)},
 	cpu_irq_actions
 };
@@ -92,12 +104,15 @@ struct irq_region *irq_region[NR_IRQ_REG
 };
 
 
-
-/* we special-case the real IRQs here, which feels right given the relatively
- * high cost of indirect calls.  If anyone is bored enough to benchmark this
- * and find out whether I am right, feel free to.   prumpf */
+/*
+** Generic interfaces that device drivers can use:
+**    mask_irq()	temporarily block IRQ
+**    unmask_irq()	re-enable IRQ and trigger if IRQ is pending
+**    disable_irq()	turn IRQ off - ie ignore it
+**    enable_irq()	turn IRQ on  - ie start using it
+*/
 
-static inline void mask_irq(int irq)
+void mask_irq(int irq)
 {
 	struct irq_region *region;
 
@@ -106,16 +121,12 @@ static inline void mask_irq(int irq)
 #endif
 	DBG_IRQ("mask_irq(%d) %d+%d\n", irq, IRQ_REGION(irq), IRQ_OFFSET(irq));
 
-	if(IRQ_REGION(irq) != CPU_IRQ_REGION) {
-		region = irq_region[IRQ_REGION(irq)];
-		if(region->data.flags & IRQ_REG_MASK)
-			region->ops.mask_irq(region->data.dev, IRQ_OFFSET(irq));
-	} else {
-		CLEAR_EIEM_BIT(irq);
-	}
+	region = irq_region[IRQ_REGION(irq)];
+	if(region->ops.mask_irq)
+		region->ops.mask_irq(region->data.dev, IRQ_OFFSET(irq));
 }
 
-static inline void unmask_irq(int irq)
+void unmask_irq(int irq)
 {
 	struct irq_region *region;
 
@@ -124,13 +135,9 @@ static inline void unmask_irq(int irq)
 #endif
 	DBG_IRQ("unmask_irq(%d) %d+%d\n", irq, IRQ_REGION(irq), IRQ_OFFSET(irq));
 
-	if(IRQ_REGION(irq) != CPU_IRQ_REGION) {
-		region = irq_region[IRQ_REGION(irq)];
-		if(region->data.flags & IRQ_REG_MASK)
-			region->ops.unmask_irq(region->data.dev, IRQ_OFFSET(irq));
-	} else {
-		SET_EIEM_BIT(irq);
-	}
+	region = irq_region[IRQ_REGION(irq)];
+	if(region->ops.unmask_irq)
+		region->ops.unmask_irq(region->data.dev, IRQ_OFFSET(irq));
 }
 
 void disable_irq(int irq)
@@ -142,8 +149,7 @@ void disable_irq(int irq)
 #endif
 	DBG_IRQ("disable_irq(%d) %d+%d\n", irq, IRQ_REGION(irq), IRQ_OFFSET(irq));
 	region = irq_region[IRQ_REGION(irq)];
-
-	if(region->data.flags & IRQ_REG_DIS)
+	if(region->ops.disable_irq)
 		region->ops.disable_irq(region->data.dev, IRQ_OFFSET(irq));
 	else
 		BUG();
@@ -159,7 +165,7 @@ void enable_irq(int irq)
 	DBG_IRQ("enable_irq(%d) %d+%d\n", irq, IRQ_REGION(irq), IRQ_OFFSET(irq));
 	region = irq_region[IRQ_REGION(irq)];
 
-	if(region->data.flags & IRQ_REG_DIS)
+	if(region->ops.enable_irq)
 		region->ops.enable_irq(region->data.dev, IRQ_OFFSET(irq));
 	else
 		BUG();
@@ -172,8 +178,10 @@ int get_irq_list(char *buf)
 	struct irq_region *region;
 	int regnr;
 
-	p += sprintf(p, "           ");
-	p += sprintf(p, " IRQ count ");
+	p += sprintf(p, " IRQ      count         Region    ISR");
+#ifdef PARISC_IRQ_CR16_COUNTS
+	p += sprintf(p, "[min/avg/max] (CPU cycle counts)");
+#endif
 	*p++ = '\n';
 
 	/* We don't need *irqsave lock variants since this is
@@ -207,11 +215,35 @@ int get_irq_list(char *buf)
 #endif
 		p += sprintf(p, " %14s",
 			    region->data.name ? region->data.name : "N/A");
+#ifndef PARISC_IRQ_CR16_COUNTS
 		p += sprintf(p, "  %s", action->name);
 
 		while ((action = action->next))
 			p += sprintf(p, ", %s", action->name);
+#else
+		for ( ;action; action = action->next) {
+			unsigned int i, avg, min, max;
 
+			min = max = action->cr16_hist[0];
+
+			for (avg = i = 0; i < PARISC_CR16_HIST_SIZE; i++) {
+				int hist = action->cr16_hist[i];
+
+				if (hist) {
+					avg += hist;
+				} else
+					break;
+
+				if (hist > max) max = hist;
+				if (hist < min) min = hist;
+			}
+
+			avg /= i;
+			p += sprintf(p, " %s[%d/%d/%d]", action->name,
+					min,avg,max);
+		}
+#endif
+
 		*p++ = '\n';
 	    }
 	}
@@ -325,17 +357,62 @@ void do_irq(struct irqaction *action, in
 	if (irq != TIMER_IRQ)
 #endif
 	DBG_IRQ("do_irq(%d) %d+%d\n", irq, IRQ_REGION(irq), IRQ_OFFSET(irq));
+
+	for(; action; action = action->next) {
+#ifdef PARISC_IRQ_CR16_COUNTS
+		unsigned long cr_start = mfctl(16);
+#endif
 
-	if (action->handler == NULL)
-		printk(KERN_ERR "IRQ:  CPU:%d No handler for IRQ %d !\n", cpu, irq);
+		if (action->handler == NULL) {
+			printk(KERN_ERR "IRQ:  CPU:%d No handler for IRQ %d !\n", cpu, irq);
+			continue;
+		}
 
-	for(; action && action->handler; action = action->next) {
 		action->handler(irq, action->dev_id, regs);
+
+#ifdef PARISC_IRQ_CR16_COUNTS
+		{
+			unsigned long cr_end = mfctl(16);
+			unsigned long tmp = cr_end - cr_start;
+			/* check for roll over */
+			cr_start = (cr_end < cr_start) ?  -(tmp) : (tmp);
+		}
+		action->cr16_hist[action->cr16_idx++] = (int) cr_start;
+		action->cr16_idx &= PARISC_CR16_HIST_SIZE - 1;
+#endif
 	}
 
 	irq_exit(cpu, irq);
 }
 
+
+/* ONLY called from entry.S:intr_extint() */
+void do_cpu_irq_mask(unsigned long mask, struct irq_region *region, struct pt_regs *regs)
+{
+	unsigned long bit;
+	int irq;
+
+#ifdef DEBUG_IRQ
+	if (mask != (1L << MAX_CPU_IRQ))
+	    printk("do_irq_mask %08lx %p %p\n", mask, region, regs);
+#endif
+
+	for(bit=(1L<<MAX_CPU_IRQ), irq = 0; mask && bit; bit>>=1, irq++) {
+		int irq_num;
+		if(!(bit&mask))
+			continue;
+
+		mask &= ~bit;	/* clear bit in mask - can exit loop sooner */
+		irq_num = region->data.irqbase + irq;
+
+		CLEAR_EIEM_BIT(bit);	/* mask_cpu_irq(NULL, irq) */
+		do_irq(&region->action[irq], irq_num, regs);
+		SET_EIEM_BIT(bit);	/* unmask_cpu_irq(NULL, irq) */
+	}
+}
+
+
+/* Called from second level IRQ regions: eg dino or iosapic. */
 void do_irq_mask(unsigned long mask, struct irq_region *region, struct pt_regs *regs)
 {
 	unsigned long bit;
@@ -351,6 +428,7 @@ void do_irq_mask(unsigned long mask, str
 		if(!(bit&mask))
 			continue;
 
+		mask &= ~bit;	/* clear bit in mask - can exit loop sooner */
 		irq_num = region->data.irqbase + irq;
 
 		mask_irq(irq_num);
@@ -358,6 +436,7 @@ void do_irq_mask(unsigned long mask, str
 		unmask_irq(irq_num);
 	}
 }
+
 
 static inline int find_free_region(void)
 {