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

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

diff -u --recursive --new-file v2.1.101/linux/arch/i386/kernel/irq.c linux/arch/i386/kernel/irq.c
@@ -87,6 +87,7 @@
 unsigned int io_apic_irqs = 0;
 
 struct hw_interrupt_type {
+	const char * typename;
 	void (*handle)(unsigned int irq, int cpu, struct pt_regs * regs);
 	void (*enable)(unsigned int irq);
 	void (*disable)(unsigned int irq);
@@ -98,6 +99,7 @@
 static void disable_8259A_irq (unsigned int irq);
 
 static struct hw_interrupt_type i8259A_irq_type = {
+	"XT-PIC",
 	do_8259A_IRQ,
 	enable_8259A_irq,
 	disable_8259A_irq
@@ -108,7 +110,11 @@
 
 /*
  * Level and edge triggered IO-APIC interrupts need different handling,
- * so we use two separate irq descriptors:
+ * so we use two separate irq descriptors. edge triggered IRQs can be
+ * handled with the level-triggered descriptor, but that one has slightly
+ * more overhead. Level-triggered interrupts cannot be handled with the
+ * edge-triggered handler, without risking IRQ storms and other ugly
+ * races.
  */
 
 static void do_edge_ioapic_IRQ (unsigned int irq, int cpu,
@@ -117,6 +123,7 @@
 static void disable_edge_ioapic_irq (unsigned int irq);
 
 static struct hw_interrupt_type ioapic_edge_irq_type = {
+	"IO-APIC-edge",
 	do_edge_ioapic_IRQ,
 	enable_edge_ioapic_irq,
 	disable_edge_ioapic_irq
@@ -128,6 +135,7 @@
 static void disable_level_ioapic_irq (unsigned int irq);
 
 static struct hw_interrupt_type ioapic_level_irq_type = {
+	"IO-APIC-level",
 	do_level_ioapic_IRQ,
 	enable_level_ioapic_irq,
 	disable_level_ioapic_irq
@@ -207,17 +215,6 @@
 	}
 }
 
-void unmask_generic_irq(unsigned int irq)
-{
-	irq_desc[irq].status = 0;
-	if (IO_APIC_IRQ(irq))
-		enable_IO_APIC_irq(irq);
-	else {
-		cached_irq_mask &= ~(1 << irq);
-		set_8259A_irq_mask(irq);
-	}
-}
-
 /*
  * This builds up the IRQ handler stubs using some ugly macros in irq.h
  *
@@ -277,6 +274,7 @@
 BUILD_SMP_INTERRUPT(invalidate_interrupt)
 BUILD_SMP_INTERRUPT(stop_cpu_interrupt)
 BUILD_SMP_INTERRUPT(mtrr_interrupt)
+BUILD_SMP_INTERRUPT(spurious_interrupt)
 
 /*
  * every pentium local APIC has two 'local interrupts', with a
@@ -356,16 +354,7 @@
 			p += sprintf(p, "%10u ",
 				kstat.irqs[cpu_logical_map(j)][i]);
 #endif
-		if (IO_APIC_IRQ(i)) {
-			p += sprintf(p, " IO-APIC");
-#ifdef __SMP__
-			if (irq_desc[i].handler == &ioapic_level_irq_type)
-				p += sprintf(p, "-level ");
-			else
-				p += sprintf(p, "-edge  ");
-#endif
-		} else
-			p += sprintf(p, "  XT-PIC       ");
+		p += sprintf(p, " %14s", irq_desc[i].handler->typename);
 		p += sprintf(p, "  %s", action->name);
 
 		for (action=action->next; action; action = action->next) {
@@ -695,6 +684,16 @@
 	set_8259A_irq_mask(irq);
 }
 
+int i8259A_irq_pending (unsigned int irq)
+{
+	unsigned int mask = 1<<irq;
+
+	if (irq < 8)
+                return (inb(0x20) & mask);
+        return (inb(0xA0) & (mask >> 8));
+}
+
+
 void make_8259A_irq (unsigned int irq)
 {
 	io_apic_irqs &= ~(1<<irq);
@@ -771,29 +770,20 @@
 {
 }
 
-/*
- * if we enable this, why does it cause a hang in the BusLogic
- * driver, when level triggered PCI IRQs are used?
- */
-#define NOT_BROKEN 0
-
 static void enable_level_ioapic_irq(unsigned int irq)
 {
-#if NOT_BROKEN
 	enable_IO_APIC_irq(irq);
-#endif
 	self_IPI(irq);
 }
 
 static void disable_level_ioapic_irq(unsigned int irq)
 {
-#if NOT_BROKEN
 	disable_IO_APIC_irq(irq);
-#endif
 }
 
 /*
- * Has to be called with the irq controller locked
+ * Has to be called with the irq controller locked, and has to exit
+ * with with the lock held as well.
  */
 static void handle_ioapic_event (unsigned int irq, int cpu,
 						struct pt_regs * regs)
@@ -808,13 +798,18 @@
 	while (test_bit(0,&global_irq_lock)) barrier();
 
 	for (;;) {
-		int pending;
+		int pending, handler;
 
-		/* If there is no IRQ handler, exit early, leaving the irq "in progress" */
-		if (!handle_IRQ_event(irq, regs))
-			goto no_handler;
+		/*
+		 * If there is no IRQ handler, exit early, leaving the irq
+		 * "in progress"
+		 */
+		handler = handle_IRQ_event(irq, regs);
 
 		spin_lock(&irq_controller_lock);
+		if (!handler)
+			goto no_handler;
+
 		pending = desc->events;
 		desc->events = 0;
 		if (!pending)
@@ -822,7 +817,6 @@
 		spin_unlock(&irq_controller_lock);
 	}
 	desc->status &= IRQ_DISABLED;
-	spin_unlock(&irq_controller_lock);
 
 no_handler:
 }
@@ -850,6 +844,7 @@
 	}
 
 	handle_ioapic_event(irq,cpu,regs);
+	spin_unlock(&irq_controller_lock);
 
 	hardirq_exit(cpu);
 	release_irqlock(cpu);
@@ -862,13 +857,14 @@
 
 	spin_lock(&irq_controller_lock);
 	/*
-	 * in the level triggered case we first disable the IRQ
+	 * In the level triggered case we first disable the IRQ
 	 * in the IO-APIC, then we 'early ACK' the IRQ, then we
 	 * handle it and enable the IRQ when finished.
+	 *
+	 * disable has to happen before the ACK, to avoid IRQ storms.
+	 * So this all has to be within the spinlock.
 	 */
-#if NOT_BROKEN
 	disable_IO_APIC_irq(irq);
-#endif
 	ack_APIC_irq();
 	desc->ipi = 0;
 
@@ -883,6 +879,10 @@
 	}
 
 	handle_ioapic_event(irq,cpu,regs);
+	/* we still have the spinlock held here */
+
+	enable_IO_APIC_irq(irq);
+	spin_unlock(&irq_controller_lock);
 
 	hardirq_exit(cpu);
 	release_irqlock(cpu);
@@ -981,11 +981,34 @@
 	struct irqaction *old, **p;
 	unsigned long flags;
 
+	/*
+	 * Some drivers like serial.c use request_irq() heavily,
+	 * so we have to be careful not to interfere with a
+	 * running system.
+	 */
+	if (new->flags & SA_SAMPLE_RANDOM) {
+		/*
+		 * This function might sleep, we want to call it first,
+		 * outside of the atomic block.
+		 * Yes, this might clear the entropy pool if the wrong
+		 * driver is attempted to be loaded, without actually
+		 * installing a new handler, but is this really a problem,
+		 * only the sysadmin is able to do this.
+		 */
+		rand_initialize_irq(irq);
+	}
+
+	/*
+	 * The following block of code has to be executed atomically
+	 */
+	spin_lock_irqsave(&irq_controller_lock,flags);
 	p = &irq_desc[irq].action;
 	if ((old = *p) != NULL) {
 		/* Can't share interrupts unless both agree to */
-		if (!(old->flags & new->flags & SA_SHIRQ))
+		if (!(old->flags & new->flags & SA_SHIRQ)) {
+			spin_unlock_irqrestore(&irq_controller_lock,flags);
 			return -EBUSY;
+		}
 
 		/* add new interrupt at end of irq queue */
 		do {
@@ -995,15 +1018,9 @@
 		shared = 1;
 	}
 
-	if (new->flags & SA_SAMPLE_RANDOM)
-		rand_initialize_irq(irq);
-
-	save_flags(flags);
-	cli();
 	*p = new;
 
 	if (!shared) {
-		spin_lock(&irq_controller_lock);
 #ifdef __SMP__
 		if (IO_APIC_IRQ(irq)) {
 			if (IO_APIC_VECTOR(irq) > 0xfe)
@@ -1016,14 +1033,21 @@
 			 * First disable it in the 8259A:
 			 */
 			cached_irq_mask |= 1 << irq;
-			if (irq < 16)
+			if (irq < 16) {
 				set_8259A_irq_mask(irq);
+				/*
+				 * transport pending ISA IRQs to
+				 * the new descriptor
+				 */
+				if (i8259A_irq_pending(irq))
+					irq_desc[irq].events = 1;
+			}
 		}
 #endif
-		unmask_generic_irq(irq);
-		spin_unlock(&irq_controller_lock);
+		irq_desc[irq].status = 0;
+		irq_desc[irq].handler->enable(irq);
 	}
-	restore_flags(flags);
+	spin_unlock_irqrestore(&irq_controller_lock,flags);
 	return 0;
 }
 
@@ -1065,23 +1089,23 @@
 	struct irqaction * action, **p;
 	unsigned long flags;
 
-	if (irq >= NR_IRQS) {
-		printk("Trying to free IRQ%d\n",irq);
+	if (irq >= NR_IRQS)
 		return;
-	}
+
+	spin_lock_irqsave(&irq_controller_lock,flags);
 	for (p = &irq_desc[irq].action; (action = *p) != NULL; p = &action->next) {
 		if (action->dev_id != dev_id)
 			continue;
 
 		/* Found it - now free it */
-		save_flags(flags);
-		cli();
 		*p = action->next;
-		restore_flags(flags);
 		kfree(action);
-		return;
+		irq_desc[irq].handler->disable(irq);
+		goto out;
 	}
 	printk("Trying to free free IRQ%d\n",irq);
+out:
+	spin_unlock_irqrestore(&irq_controller_lock,flags);
 }
 
 /*
@@ -1103,7 +1127,7 @@
 	spin_lock_irq(&irq_controller_lock);
 	for (i = NR_IRQS-1; i > 0; i--) {
 		if (!irq_desc[i].action) {
-			unmask_generic_irq(i);
+			irq_desc[i].handler->enable(i);
 			irqs |= (1 << i);
 		}
 	}
@@ -1170,8 +1194,8 @@
 		    (IO_APIC_IRQ(i))) {
 			if (IO_APIC_irq_trigger(i))
 				irq_desc[i].handler = &ioapic_level_irq_type;
-			else
-				irq_desc[i].handler = &ioapic_edge_irq_type;
+			else /* edge */
+				irq_desc[i].handler = &ioapic_level_irq_type;
 			/*
 			 * disable it in the 8259A:
 			 */
@@ -1233,6 +1257,8 @@
 	/* IPI for MTRR control */
 	set_intr_gate(0x50, mtrr_interrupt);
 
+	/* IPI vector for APIC spurious interrupts */
+	set_intr_gate(0xff, spurious_interrupt);
 #endif	
 	request_region(0x20,0x20,"pic1");
 	request_region(0xa0,0x20,"pic2");

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