[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(®ion->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)
{