patch-2.1.99 linux/arch/i386/kernel/irq.c

Next file: linux/arch/i386/kernel/irq.h
Previous file: linux/arch/i386/kernel/i386_ksyms.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.98/linux/arch/i386/kernel/irq.c linux/arch/i386/kernel/irq.c
@@ -68,12 +68,6 @@
 
 spinlock_t irq_controller_lock;
 
-static unsigned int irq_events [NR_IRQS] = { -1, };
-static int disabled_irq [NR_IRQS] = { 0, };
-#ifdef __SMP__
-static int ipi_pending [NR_IRQS] = { 0, };
-#endif
-
 /*
  * Not all IRQs can be routed through the IO-APIC, eg. on certain (older)
  * boards the timer interrupt and sometimes the keyboard interrupt is
@@ -126,11 +120,34 @@
 };
 #endif
 
-struct hw_interrupt_type *irq_handles[NR_IRQS] =
-{
-	[0 ... 15] = &i8259A_irq_type			/* standard ISA IRQs */
+/*
+ * Status: reason for being disabled: somebody has
+ * done a "disable_irq()" or we must not re-enter the
+ * already executing irq..
+ */
+#define IRQ_INPROGRESS	1
+#define IRQ_DISABLED	2
+
+/*
+ * This is the "IRQ descriptor", which contains various information
+ * about the irq, including what kind of hardware handling it has,
+ * whether it is disabled etc etc.
+ *
+ * Pad this out to 32 bytes for cache and indexing reasons.
+ */
+typedef struct {
+	unsigned int status;			/* IRQ status - IRQ_INPROGRESS, IRQ_DISABLED */
+	unsigned int events;			/* Do we have any pending events? */
+	unsigned int ipi;			/* Have we sent off the pending IPI? */
+	struct hw_interrupt_type *handler;	/* handle/enable/disable functions */
+	struct irqaction *action;		/* IRQ action list */
+	unsigned int unused[3];
+} irq_desc_t;
+
+irq_desc_t irq_desc[NR_IRQS] = {
+	[0 ... 15] = { 0, 0, 0, &i8259A_irq_type, },	/* standard ISA IRQs */
 #ifdef __SMP__
-	, [16 ... NR_IRQS-1] = &ioapic_irq_type		/* 'high' PCI IRQs */
+	[16 ... 23] = { 0, 0, 0, &ioapic_irq_type, },	/* 'high' PCI IRQs */
 #endif
 };
 
@@ -177,6 +194,7 @@
 
 void unmask_generic_irq(unsigned int irq)
 {
+	irq_desc[irq].status = 0;
 	if (IO_APIC_IRQ(irq))
 		enable_IO_APIC_irq(irq);
 	else {
@@ -243,6 +261,7 @@
 BUILD_SMP_INTERRUPT(reschedule_interrupt)
 BUILD_SMP_INTERRUPT(invalidate_interrupt)
 BUILD_SMP_INTERRUPT(stop_cpu_interrupt)
+BUILD_SMP_INTERRUPT(mtrr_interrupt)
 
 /*
  * every pentium local APIC has two 'local interrupts', with a
@@ -299,17 +318,6 @@
  */
 static struct irqaction irq2  = { no_action, 0, 0, "cascade", NULL, NULL};
 
-static struct irqaction *irq_action[NR_IRQS] = {
-	NULL, NULL, NULL, NULL,
-	NULL, NULL, NULL, NULL,
-	NULL, NULL, NULL, NULL,
-	NULL, NULL, NULL, NULL
-#ifdef __SMP__
-	,NULL, NULL, NULL, NULL,
-	NULL, NULL, NULL, NULL
-#endif
-};
-
 int get_irq_list(char *buf)
 {
 	int i, j;
@@ -322,7 +330,7 @@
 	*p++ = '\n';
 
 	for (i = 0 ; i < NR_IRQS ; i++) {
-		action = irq_action[i];
+		action = irq_desc[i].action;
 		if (!action) 
 			continue;
 		p += sprintf(p, "%3d: ",i);
@@ -630,7 +638,7 @@
 	int status;
 
 	status = 0;
-	action = *(irq + irq_action);
+	action = irq_desc[irq].action;
 
 	if (action) {
 		status |= 1;
@@ -651,18 +659,6 @@
 	return status;
 }
 
-
-void disable_irq(unsigned int irq)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&irq_controller_lock, flags);
-	irq_handles[irq]->disable(irq);
-	spin_unlock_irqrestore(&irq_controller_lock, flags);
-
-	synchronize_irq();
-}
-
 /*
  * disable/enable_irq() wait for all irq contexts to finish
  * executing. Also it's recursive.
@@ -673,61 +669,16 @@
 	set_8259A_irq_mask(irq);
 }
 
-#ifdef __SMP__
-static void disable_ioapic_irq(unsigned int irq)
-{
-	disabled_irq[irq] = 1;
-	/*
-	 * We do not disable IO-APIC irqs in hardware ...
-	 */
-}
-#endif
-
 void enable_8259A_irq (unsigned int irq)
 {
-	unsigned long flags;
-	spin_lock_irqsave(&irq_controller_lock, flags);
 	cached_irq_mask &= ~(1 << irq);
 	set_8259A_irq_mask(irq);
-	spin_unlock_irqrestore(&irq_controller_lock, flags);
-}
-
-#ifdef __SMP__
-void enable_ioapic_irq (unsigned int irq)
-{
-	unsigned long flags, should_handle_irq;
-	int cpu = smp_processor_id();
-
-	spin_lock_irqsave(&irq_controller_lock, flags);
-	disabled_irq[irq] = 0;
-
-	/*
-	 * In the SMP+IOAPIC case it might happen that there are an unspecified
-	 * number of pending IRQ events unhandled. These cases are very rare,
-	 * so we 'resend' these IRQs via IPIs, to the same CPU. It's much
-	 * better to do it this way as thus we dont have to be aware of
-	 * 'pending' interrupts in the IRQ path, except at this point.
-	 */
-	if (irq_events[irq]) {
-		if (!ipi_pending[irq]) {
-			ipi_pending[irq] = 1;
-			--irq_events[irq];
-			send_IPI(cpu,IO_APIC_VECTOR(irq));
-		}
-	}
-	spin_unlock_irqrestore(&irq_controller_lock, flags);
-}
-#endif
-
-void enable_irq(unsigned int irq)
-{
-	irq_handles[irq]->enable(irq);
 }
 
 void make_8259A_irq (unsigned int irq)
 {
 	io_apic_irqs &= ~(1<<irq);
-	irq_handles[irq] = &i8259A_irq_type;
+	irq_desc[irq].handler = &i8259A_irq_type;
 	disable_irq(irq);
 	enable_irq(irq);
 }
@@ -741,6 +692,7 @@
 static inline void mask_and_ack_8259A(unsigned int irq)
 {
 	spin_lock(&irq_controller_lock);
+	irq_desc[irq].status |= IRQ_INPROGRESS;
 	cached_irq_mask |= 1 << irq;
 	if (irq & 8) {
 		inb(0xA1);	/* DUMMY */
@@ -763,7 +715,8 @@
 
 	if (handle_IRQ_event(irq, regs)) {
 		spin_lock(&irq_controller_lock);
-		unmask_8259A(irq);
+		if (!(irq_desc[irq].status &= IRQ_DISABLED))
+			unmask_8259A(irq);
 		spin_unlock(&irq_controller_lock);
 	}
 
@@ -771,41 +724,119 @@
 }
 
 #ifdef __SMP__
+
+/*
+ * In the SMP+IOAPIC case it might happen that there are an unspecified
+ * number of pending IRQ events unhandled. These cases are very rare,
+ * so we 'resend' these IRQs via IPIs, to the same CPU. It's much
+ * better to do it this way as thus we dont have to be aware of
+ * 'pending' interrupts in the IRQ path, except at this point.
+ */
+static void enable_ioapic_irq(unsigned int irq)
+{
+	irq_desc_t *desc = irq_desc + irq;
+	if (desc->events && !desc->ipi) {
+		desc->ipi = 1;
+		send_IPI(APIC_DEST_SELF, IO_APIC_VECTOR(irq));
+	}
+}
+
+/*
+ * We do not actually disable IO-APIC irqs in hardware ...
+ */
+static void disable_ioapic_irq(unsigned int irq)
+{
+}
+
 static void do_ioapic_IRQ(unsigned int irq, int cpu, struct pt_regs * regs)
 {
-	int should_handle_irq = 0;
+	irq_desc_t *desc = irq_desc + irq;
+
+	spin_lock(&irq_controller_lock);
 
+	/* Ack the irq inside the lock! */
 	ack_APIC_irq();
+	desc->ipi = 0;
 
-	spin_lock(&irq_controller_lock);
-	if (ipi_pending[irq])
-		ipi_pending[irq] = 0;
+	/* If the irq is disabled for whatever reason, just set a flag and return */
+	if (desc->status & (IRQ_DISABLED | IRQ_INPROGRESS)) {
+		desc->events = 1;
+		spin_unlock(&irq_controller_lock);
+		return;
+	}
 
-	if (!irq_events[irq]++ && !disabled_irq[irq])
-		should_handle_irq = 1;
+	desc->status = IRQ_INPROGRESS;
+	desc->events = 0;
 	hardirq_enter(cpu);
 	spin_unlock(&irq_controller_lock);
 
-	if (should_handle_irq) {
-		while (test_bit(0,&global_irq_lock)) mb();
-again:
-		handle_IRQ_event(irq, regs);
+	while (test_bit(0,&global_irq_lock)) barrier();
+
+	for (;;) {
+		int pending;
+
+		/* If there is no IRQ handler, exit early, leaving the irq "in progress" */
+		if (!handle_IRQ_event(irq, regs))
+			goto no_handler;
 
 		spin_lock(&irq_controller_lock);
-		should_handle_irq=0;
-		if (--irq_events[irq] && !disabled_irq[irq])
-			should_handle_irq=1;
+		pending = desc->events;
+		desc->events = 0;
+		if (!pending)
+			break;
 		spin_unlock(&irq_controller_lock);
-
-		if (should_handle_irq)
-			goto again;
 	}
+	desc->status &= IRQ_DISABLED;
+	spin_unlock(&irq_controller_lock);
 
+no_handler:
 	hardirq_exit(cpu);
 	release_irqlock(cpu);
 }
+
 #endif
 
+
+/*
+ * Generic enable/disable code: this just calls
+ * down into the PIC-specific version for the actual
+ * hardware disable after having gotten the irq
+ * controller lock. 
+ */
+void disable_irq(unsigned int irq)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&irq_controller_lock, flags);
+	/*
+	 * At this point we may actually have a pending interrupt being active
+	 * on another CPU. So don't touch the IRQ_INPROGRESS bit..
+	 */
+	irq_desc[irq].status |= IRQ_DISABLED;
+	irq_desc[irq].handler->disable(irq);
+	spin_unlock_irqrestore(&irq_controller_lock, flags);
+
+	synchronize_irq();
+}
+
+void enable_irq(unsigned int irq)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&irq_controller_lock, flags);
+	/*
+	 * In contrast to the above, we should _not_ have any concurrent
+	 * interrupt activity here, so we just clear both disabled bits.
+	 *
+	 * This allows us to have IRQ_INPROGRESS set until we actually
+	 * install a handler for this interrupt (make irq autodetection
+	 * work by just looking at the status field for the irq)
+	 */
+	irq_desc[irq].status = 0;
+	irq_desc[irq].handler->enable(irq);
+	spin_unlock_irqrestore(&irq_controller_lock, flags);
+}
+
 /*
  * do_IRQ handles all normal device IRQ's (the special
  * SMP cross-CPU interrupts have their own specific
@@ -836,7 +867,7 @@
 	int cpu = smp_processor_id();
 
 	kstat.irqs[cpu][irq]++;
-	irq_handles[irq]->handle(irq, cpu, &regs);
+	irq_desc[irq].handler->handle(irq, cpu, &regs);
 
 	/*
 	 * This should be conditional: we should really get
@@ -856,7 +887,7 @@
 	struct irqaction *old, **p;
 	unsigned long flags;
 
-	p = irq_action + irq;
+	p = &irq_desc[irq].action;
 	if ((old = *p) != NULL) {
 		/* Can't share interrupts unless both agree to */
 		if (!(old->flags & new->flags & SA_SHIRQ))
@@ -881,7 +912,7 @@
 		spin_lock(&irq_controller_lock);
 #ifdef __SMP__
 		if (IO_APIC_IRQ(irq)) {
-			irq_handles[irq] = &ioapic_irq_type;
+			irq_desc[irq].handler = &ioapic_irq_type;
 			/*
 			 * First disable it in the 8259A:
 			 */
@@ -939,7 +970,7 @@
 		printk("Trying to free IRQ%d\n",irq);
 		return;
 	}
-	for (p = irq + irq_action; (action = *p) != NULL; p = &action->next) {
+	for (p = &irq_desc[irq].action; (action = *p) != NULL; p = &action->next) {
 		if (action->dev_id != dev_id)
 			continue;
 
@@ -955,32 +986,29 @@
 }
 
 /*
- * probing is always single threaded [FIXME: is this true?]
+ * IRQ autodetection code..
+ *
+ * This depends on the fact that any interrupt that
+ * comes in on to an unassigned handler will get stuck
+ * with "IRQ_INPROGRESS" asserted and the interrupt
+ * disabled.
  */
-static unsigned int probe_irqs[NR_CPUS][NR_IRQS];
-
 unsigned long probe_irq_on (void)
 {
-	unsigned int i, j, irqs = 0;
+	unsigned int i, irqs = 0;
 	unsigned long delay;
 
 	/*
-	 * save current irq counts
-	 */
-	memcpy(probe_irqs,kstat.irqs,NR_CPUS*NR_IRQS*sizeof(int));
-
-	/*
 	 * first, enable any unassigned irqs
 	 */
+	spin_lock_irq(&irq_controller_lock);
 	for (i = NR_IRQS-1; i > 0; i--) {
-		if (!irq_action[i]) {
-			unsigned long flags;
-			spin_lock_irqsave(&irq_controller_lock, flags);
+		if (!irq_desc[i].action) {
 			unmask_generic_irq(i);
 			irqs |= (1 << i);
-			spin_unlock_irqrestore(&irq_controller_lock, flags);
 		}
 	}
+	spin_unlock_irq(&irq_controller_lock);
 
 	/*
 	 * wait for spurious interrupts to increase counters
@@ -991,35 +1019,35 @@
 	/*
 	 * now filter out any obviously spurious interrupts
 	 */
-	for (i=0; i<NR_IRQS; i++)
-		for (j=0; j<NR_CPUS; j++)
-			if (kstat.irqs[j][i] != probe_irqs[j][i])
-				irqs &= ~(1UL << i);
+	spin_lock_irq(&irq_controller_lock);
+	for (i=0; i<NR_IRQS; i++) {
+		if (irq_desc[i].status & IRQ_INPROGRESS)
+			irqs &= ~(1UL << i);
+	}
+	spin_unlock_irq(&irq_controller_lock);
 
 	return irqs;
 }
 
 int probe_irq_off (unsigned long irqs)
 {
-	int i,j, irq_found = -1;
+	int i, irq_found = -1;
 
+	spin_lock_irq(&irq_controller_lock);
 	for (i=0; i<NR_IRQS; i++) {
-		int sum = 0;
-		for (j=0; j<NR_CPUS; j++) {
-			sum += kstat.irqs[j][i];
-			sum -= probe_irqs[j][i];
-		}
-		if (sum && (irqs & (1UL << i))) {
+		if ((irqs & 1) && (irq_desc[i].status & IRQ_INPROGRESS)) {
 			if (irq_found != -1) {
 				irq_found = -irq_found;
 				goto out;
-			} else
-				irq_found = i;
+			}
+			irq_found = i;
 		}
+		irqs >>= 1;
 	}
 	if (irq_found == -1)
 		irq_found = 0;
 out:
+	spin_unlock_irq(&irq_controller_lock);
 	return irq_found;
 }
 
@@ -1041,7 +1069,7 @@
 	for (i = 0; i < NR_IRQS ; i++)
 		if (IO_APIC_VECTOR(i) <= 0xfe)  /* HACK */ {
 			if (IO_APIC_IRQ(i)) {
-				irq_handles[i] = &ioapic_irq_type;
+				irq_desc[i].handler = &ioapic_irq_type;
 				/*
 				 * First disable it in the 8259A:
 				 */
@@ -1063,8 +1091,8 @@
 	outb(LATCH >> 8 , 0x40);	/* MSB */
 
 	for (i=0; i<NR_IRQS; i++) {
-		irq_events[i] = 0;
-		disabled_irq[i] = 0;
+		irq_desc[i].events = 0;
+		irq_desc[i].status = 0;
 	}
 	/*
 	 * 16 old-style INTA-cycle interrupt gates:
@@ -1099,6 +1127,9 @@
 
 	/* self generated IPI for local APIC timer */
 	set_intr_gate(0x41, apic_timer_interrupt);
+
+	/* IPI for MTRR control */
+	set_intr_gate(0x50, mtrr_interrupt);
 
 #endif	
 	request_region(0x20,0x20,"pic1");

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov