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
- Lines: 498
- Date:
Thu Apr 30 13:08:47 1998
- Orig file:
v2.1.98/linux/arch/i386/kernel/irq.c
- Orig date:
Sat Apr 25 18:13:10 1998
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, ®s);
+ irq_desc[irq].handler->handle(irq, cpu, ®s);
/*
* 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