[Patrick, does the attached patch fix your problem?]
On Mon, 5 Oct 1998, Linus Torvalds wrote:
> I'd really just prefer to get rid of the 1:1 mapping completely, and use a
> linked list approach everywhere instead.
>
> So you'd just build up something like irq_to_pin[] that's a singly linked
> list
ok, the attached patch implements the data structure described in your
pseudo-code. Note that pin == 0 is a valid case too. (usually it's
allocated to the timer interrupt which has different handling due to
(real) BIOS bugs, but i dont think we should take this granted). Also, we
want to safely handle unallocated irqs too, thus we have to do one more
extra check for pin == -1.
> and to make sure it's fast in the single-irq case you do the above by just
> expanding the irq_2_pin[] array to have two entries instead of just one,
> where the second one is the link ptr.. The only extra overhead for the
> common case is one single load (cached, because you already loaded the
> irq) and a test, and the code is about 100 times cleaner than your current
> patch (sorry, I don't think you were too proud of it either ;)
nope but it was the simplest one, i wasnt even sure wether this approach
is the best way to fix the problem. it was just a suggestion, code instead
of words ;) i hope the attached patch hits the treshold. The #define looks
ugly but so does code repetition.
-- mingo
--- linux/arch/i386/kernel/io_apic.c.orig Fri Feb 2 09:38:58 1996
+++ linux/arch/i386/kernel/io_apic.c Tue Oct 6 00:10:15 1998
@@ -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)
{
@@ -128,58 +130,69 @@
}
/*
- * 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) \
+ \
+static void name##_IO_APIC_irq(unsigned int irq) \
+{ \
+ int pin; \
+ struct IO_APIC_route_entry reg; \
+ struct irq_pin_list *entry = irq_2_pin + irq; \
+ \
+ for (;;) { \
+ pin = entry->pin; \
+ if (pin == -1) \
+ break; \
+ *(((int *)®) + R) = io_apic_read(0x10 + R + pin*2); \
+ ACTION; \
+ io_apic_write(0x10 + R + 2*pin, *(((int *)®) + R)); \
+ if (!entry->next) \
+ break; \
+ entry = irq_2_pin + entry->next; \
+ } \
+ io_apic_sync(); \
}
-static inline void unmask_IO_APIC_irq(unsigned int irq)
-{
- int pin = irq_2_pin[irq];
- struct IO_APIC_route_entry entry;
+/*
+ * We disable IO-APIC IRQs by setting their 'destination CPU mask' to
+ * zero. Trick by Ramesh Nalluri.
+ */
- if (pin != -1) {
- *(((int *)&entry) + 0) = io_apic_read(0x10 + pin * 2);
- entry.mask = 0;
- io_apic_write(0x10 + 2 * pin, *(((int *)&entry) + 0));
- }
-}
+DO_ACTION( disable, 1, reg.dest.logical.logical_dest = 0x0)
+DO_ACTION( enable, 1, reg.dest.logical.logical_dest = 0xff)
+DO_ACTION( mask, 0, reg.mask = 1)
+DO_ACTION( unmask, 0, reg.mask = 0)
static void __init clear_IO_APIC_pin(unsigned int pin)
{
@@ -577,7 +590,7 @@
}
irq = pin_2_irq(idx,pin);
- irq_2_pin[irq] = pin;
+ add_pin_to_irq(irq, pin);
if (!IO_APIC_IRQ(irq))
continue;
@@ -703,8 +716,8 @@
}
printk("IRQ to pin mappings:\n");
- for (i = 0; i < NR_IRQS; i++)
- printk("%d->%d ", i, irq_2_pin[i]);
+ for (i = 0; i < PIN_MAP_SIZE; i++)
+ printk("%d->%d(%d) ", i, irq_2_pin[i].pin, irq_2_pin[i].next);
printk("\n");
printk(".................................... done.\n");
@@ -716,8 +729,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;