patch-2.1.125 linux/arch/i386/kernel/io_apic.c

Next file: linux/arch/i386/kernel/process.c
Previous file: linux/arch/i386/kernel/apm.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.124/linux/arch/i386/kernel/io_apic.c linux/arch/i386/kernel/io_apic.c
@@ -103,8 +103,10 @@
 
 /*
  * This is performance-critical, we want to do it O(1)
+ *
+ * the indexing order of this array favors 1:1 mappings
+ * between pins and IRQs.
  */
-static int irq_2_pin[NR_IRQS];
 
 static inline unsigned int io_apic_read(unsigned int reg)
 {
@@ -119,6 +121,15 @@
 }
 
 /*
+ * Re-write a value: to be used for read-modify-write
+ * cycles where the read already set up the index register.
+ */
+static inline void io_apic_modify(unsigned int value)
+{
+	*(IO_APIC_BASE+4) = value;
+}
+
+/*
  * Synchronize the IO-APIC and the CPU by doing
  * a dummy read from the IO-APIC
  */
@@ -128,58 +139,68 @@
 }
 
 /*
- * We disable IO-APIC IRQs by setting their 'destination CPU mask' to
- * zero. Trick by Ramesh Nalluri.
+ * Rough estimation of how many shared IRQs there are, can
+ * be changed anytime.
  */
-static inline void disable_IO_APIC_irq(unsigned int irq)
-{
-	int pin = irq_2_pin[irq];
-	struct IO_APIC_route_entry entry;
+#define MAX_PLUS_SHARED_IRQS NR_IRQS
+#define PIN_MAP_SIZE (MAX_PLUS_SHARED_IRQS + NR_IRQS)
 
-	if (pin != -1) {
-		*(((int *)&entry) + 1) = io_apic_read(0x11 + pin * 2);
-		entry.dest.logical.logical_dest = 0x0;
-		io_apic_write(0x11 + 2 * pin, *(((int *)&entry) + 1));
-		io_apic_sync();
-	}
-}
+static struct irq_pin_list {
+	int pin, next;
+} irq_2_pin[PIN_MAP_SIZE];
 
-static inline void enable_IO_APIC_irq(unsigned int irq)
+/*
+ * The common case is 1:1 IRQ<->pin mappings. Sometimes there are
+ * shared ISA-space IRQs, so we have to support them. We are super
+ * fast in the common case, and fast for shared ISA-space IRQs.
+ */
+static void add_pin_to_irq(unsigned int irq, int pin)
 {
-	int pin = irq_2_pin[irq];
-	struct IO_APIC_route_entry entry;
+	static int first_free_entry = NR_IRQS;
+	struct irq_pin_list *entry = irq_2_pin + irq;
 
-	if (pin != -1) {
-		*(((int *)&entry) + 1) = io_apic_read(0x11 + pin * 2);
-		entry.dest.logical.logical_dest = 0xff;
-		io_apic_write(0x11 + 2 * pin, *(((int *)&entry) + 1));
-	}
-}
+	while (entry->next)
+		entry = irq_2_pin + entry->next;
 
-static inline void mask_IO_APIC_irq(unsigned int irq)
-{
-	int pin = irq_2_pin[irq];
-	struct IO_APIC_route_entry entry;
-
-	if (pin != -1) {
-		*(((int *)&entry) + 0) = io_apic_read(0x10 + pin * 2);
-		entry.mask = 1;
-		io_apic_write(0x10 + 2 * pin, *(((int *)&entry) + 0));
-		io_apic_sync();
-	}
+	if (entry->pin != -1) {
+		entry->next = first_free_entry;
+		entry = irq_2_pin + entry->next;
+		if (++first_free_entry >= PIN_MAP_SIZE)
+			panic("io_apic.c: whoops");
+	}
+	entry->pin = pin;
+}
+
+#define DO_ACTION(name,R,ACTION, FINAL)					\
+									\
+static void name##_IO_APIC_irq(unsigned int irq)			\
+{									\
+	int pin;							\
+	struct irq_pin_list *entry = irq_2_pin + irq;			\
+									\
+	for (;;) {							\
+		unsigned int reg;					\
+		pin = entry->pin;					\
+		if (pin == -1)						\
+			break;						\
+		reg = io_apic_read(0x10 + R + pin*2);			\
+		reg ACTION;						\
+		io_apic_modify(reg);					\
+		if (!entry->next)					\
+			break;						\
+		entry = irq_2_pin + entry->next;			\
+	}								\
+	FINAL;								\
 }
 
-static inline void unmask_IO_APIC_irq(unsigned int irq)
-{
-	int pin = irq_2_pin[irq];
-	struct IO_APIC_route_entry entry;
-
-	if (pin != -1) {
-		*(((int *)&entry) + 0) = io_apic_read(0x10 + pin * 2);
-		entry.mask = 0;
-		io_apic_write(0x10 + 2 * pin, *(((int *)&entry) + 0));
-	}
-}
+/*
+ * We disable IO-APIC IRQs by setting their 'destination CPU mask' to
+ * zero. Trick by Ramesh Nalluri.
+ */
+DO_ACTION( disable, 1, &= 0x00ffffff, io_apic_sync())		/* destination = 0x00 */
+DO_ACTION( enable,  1, |= 0xff000000, )				/* destination = 0xff */
+DO_ACTION( mask,    0, |= 0x00010000, io_apic_sync())		/* mask = 1 */
+DO_ACTION( unmask,  0, &= 0xfffeffff, )				/* mask = 0 */
 
 static void __init clear_IO_APIC_pin(unsigned int pin)
 {
@@ -577,7 +598,7 @@
 		}
 
 		irq = pin_2_irq(idx,pin);
-		irq_2_pin[irq] = pin;
+		add_pin_to_irq(irq, pin);
 
 		if (!IO_APIC_IRQ(irq))
 			continue;
@@ -702,10 +723,20 @@
 		);
 	}
 
-	printk("IRQ to pin mappings:\n");
-	for (i = 0; i < NR_IRQS; i++)
-		printk("%d->%d ", i, irq_2_pin[i]);
-	printk("\n");
+	printk(KERN_DEBUG "IRQ to pin mappings:\n");
+	for (i = 0; i < NR_IRQS; i++) {
+		struct irq_pin_list *entry = irq_2_pin + i;
+		if (entry->pin < 0)
+			continue;
+		printk(KERN_DEBUG "IRQ%d ", i);
+		for (;;) {
+			printk("-> %d", entry->pin);
+			if (!entry->next)
+				break;
+			entry = irq_2_pin + entry->next;
+		}
+		printk("\n");
+	}
 
 	printk(".................................... done.\n");
 
@@ -716,8 +747,10 @@
 {
 	int i, pin;
 
-	for (i = 0; i < NR_IRQS; i++)
-		irq_2_pin[i] = -1;
+	for (i = 0; i < PIN_MAP_SIZE; i++) {
+		irq_2_pin[i].pin = -1;
+		irq_2_pin[i].next = 0;
+	}
 	if (!pirqs_enabled)
 		for (i = 0; i < MAX_PIRQS; i++)
 			pirq_entries[i] =- 1;

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