On Mon, 5 Oct 1998, Linus Torvalds wrote:
> INT active-lo level 1 10 2 17
> INT active-lo level 1 9 2 18
> INT active-lo level 1 9 2 19
>
> The interesting thing is that you have _two_ IO-APIC inputs that both
> point to IRQ9. And we get confused by that. The only reason 2.1.122 works
> for you is pure and utter luck.
sigh, darn. Ok, i dont think it makes sense to fight this kind of thing,
as by my reading it's not specifically forbidden by the MP spec. (nothing
says that two IO-APIC pins cannot point to the same source IRQ ... it's
just uncommon to have sub-16 pins mapped in such a way.)
i've implemented support for this without impacting the common 1:1 case,
please take a look at the attached patch. It actually also simplifies some
of io_apic.c too. The common case is inlined, while in the uncommon
'multiple pins' case we fall back to a 'slow_*' function. It all looks
fine on the assembly level here, and works on my boards, but a real 1:N
mapping was not yet tested. It should work since we are protected by the
irq_controller spinlock and we also have local interrupts masked. Is there
any issue i'm overlooking? It does not feel too 'good' but shared
interrupts are never good anyway, they lead to similar iterations always.
(for shared IRQ sources we have to get into every driver to check for work
anyway, so we already have a 1:N scaling property there.)
i dont think it would be safe for us to try separating those two IRQ
sources. We have no driver API for this, and even if we implement some
kind of IRQ-reshuffling for PCI, it will affect lots of things. Plus if we
start rewriting PCI configuration space we will meet another set of
hardware bugs i'm sure. I think some PCI devices even _know_ their IRQ
vector in the firmware ... (the BIOS feeds them this). So if we change PCI
configuration space we are probably in trouble.
anyway, the patch works for me, i'm curious wether it helps Patrick's
problem. It's against vanilla 2.1.124.
-- mingo
--- linux/arch/i386/kernel/io_apic.c.orig Fri Feb 2 09:38:58 1996
+++ linux/arch/i386/kernel/io_apic.c Mon Oct 5 22:00:40 1998
@@ -104,7 +104,7 @@
/*
* This is performance-critical, we want to do it O(1)
*/
-static int irq_2_pin[NR_IRQS];
+static int fast_irq_2_pin[NR_IRQS];
static inline unsigned int io_apic_read(unsigned int reg)
{
@@ -131,55 +131,49 @@
* We disable IO-APIC IRQs by setting their 'destination CPU mask' to
* zero. Trick by Ramesh Nalluri.
*/
-static inline void disable_IO_APIC_irq(unsigned int irq)
-{
- int pin = irq_2_pin[irq];
- struct IO_APIC_route_entry entry;
-
- 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 inline void enable_IO_APIC_irq(unsigned int irq)
-{
- int pin = irq_2_pin[irq];
- struct IO_APIC_route_entry entry;
-
- 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));
- }
-}
-
-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();
- }
-}
-
-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));
- }
-}
+#define ITERATE_PINS(irq,i) \
+ for (i = 0; i < mp_irq_entries; i++) \
+ if ( (mp_irqs[i].mpc_irqtype == mp_INT) && \
+ (mp_irqs[i].mpc_dstirq == irq))
+
+#define DO_REGISTER_ACTION(pin,REG,ACTION) \
+{ \
+ struct IO_APIC_route_entry entry; \
+ \
+ *(((int *)&entry) + REG) = io_apic_read(0x10 + REG + pin*2); \
+ ACTION; \
+ io_apic_write(0x10 + REG + 2*pin, *(((int *)&entry) + REG)); \
+ io_apic_sync(); \
+}
+
+#define SLOW_DO_ACTION(name,irq,ACTION,REG) \
+static void slow_IO_APIC_irq_##name(unsigned int irq) \
+{ \
+ int i; \
+ \
+ ITERATE_PINS(irq, i) \
+ DO_REGISTER_ACTION(i,REG,ACTION) \
+}
+
+#define DO_ACTION(name,REG,ACTION) \
+ \
+SLOW_DO_ACTION(name,irq,ACTION,REG) \
+ \
+static inline void name##_IO_APIC_irq(unsigned int irq) \
+{ \
+ int pin = fast_irq_2_pin[irq]; \
+ \
+ if (pin >= 0) \
+ DO_REGISTER_ACTION(pin,REG,ACTION) \
+ else \
+ slow_IO_APIC_irq_##name(irq); \
+}
+
+DO_ACTION( disable, 1, entry.dest.logical.logical_dest = 0x0)
+DO_ACTION( enable, 1, entry.dest.logical.logical_dest = 0xff)
+DO_ACTION( mask, 0, entry.mask = 1)
+DO_ACTION( unmask, 0, entry.mask = 0)
static void __init clear_IO_APIC_pin(unsigned int pin)
{
@@ -577,7 +571,15 @@
}
irq = pin_2_irq(idx,pin);
- irq_2_pin[irq] = pin;
+ /*
+ * The common case is 1:1 IRQ<->pin mappings.
+ * sometimes this doesnt happen. We are fast
+ * for the common case.
+ */
+ if (fast_irq_2_pin[irq] != -1)
+ fast_irq_2_pin[irq] = -2;
+ else
+ fast_irq_2_pin[irq] = pin;
if (!IO_APIC_IRQ(irq))
continue;
@@ -704,7 +706,7 @@
printk("IRQ to pin mappings:\n");
for (i = 0; i < NR_IRQS; i++)
- printk("%d->%d ", i, irq_2_pin[i]);
+ printk("%d->%d ", i, fast_irq_2_pin[i]);
printk("\n");
printk(".................................... done.\n");
@@ -717,7 +719,7 @@
int i, pin;
for (i = 0; i < NR_IRQS; i++)
- irq_2_pin[i] = -1;
+ fast_irq_2_pin[i] = -1;
if (!pirqs_enabled)
for (i = 0; i < MAX_PIRQS; i++)
pirq_entries[i] =- 1;