[SMP patch] io-apic-patch-2.1.99-pre3-B

MOLNAR Ingo (mingo@chiara.csoma.elte.hu)
Wed, 29 Apr 1998 18:30:14 +0200 (CEST)


On Tue, 28 Apr 1998, Linus Torvalds wrote:

> - all the irq fixes we know of to date are there (and hopefully even the
> ne2000 things should work with the SELF-IPI change)

it still had a bug which caused IRQ handler reenter under certain
conditions: the disabled_irq[] flag has two functions, first, it registers
disabled interrupts, but second, it keeps interrupts disabled if a handler
is executing. If disable_irq() is done heavily (like in 8390.c), then it
might happen that we'll have the IRQ enabled right after the
synchronize_irq() in disable_irq().

this reminded me that this was one of the main reasons why i made
enable/disable_irq() recursive.

the attached patch fixes this problem by implementing a multi-level
disabled_irq[] again, although we might want to remove the external
recuriveness of en/disable_irq(), to leave the driver API untouched.
With this patch my NE2000 card is rock stable again.

the patch also streamlines irq.c after all these changes, doc fixes and
more straightforward variable names. And it includes an io_apic.c patch to
work around one specific MPBIOS bug, which prevented some systems from
booting cleanly.

and ack_APIC() is again outside of the spinlock, the self-IPI stuff is
safe this way, see the comments. This improves latency somewhat.

this very patch was applied to vanilla-2.1.99-pre3, it boots and passes
'heavy IRQ' tests.

-- mingo

--- linux/arch/i386/kernel/irq.c.orig Tue May 5 05:18:11 1998
+++ linux/arch/i386/kernel/irq.c Tue May 5 06:20:25 1998
@@ -66,9 +66,16 @@
#define cached_21 ((cached_irq_mask | io_apic_irqs) & 0xff)
#define cached_A1 (((cached_irq_mask | io_apic_irqs) >> 8) & 0xff)

+/*
+ * on SMP this protects the IRQ controller hardware (8259A, IO-APIC, etc.)
+ * against parallel access from several CPUS. But we protect data structures
+ * too, these data structures can be understood as part of a 'generic IRQ
+ * controller'. All operations within this lock have a predictable latency,
+ * which does not depend on other locks.
+ */
spinlock_t irq_controller_lock;

-static unsigned int irq_events [NR_IRQS] = { -1, };
+static unsigned int irqs_pending [NR_IRQS] = { -1, };
static int disabled_irq [NR_IRQS] = { 0, };

/*
@@ -724,15 +731,20 @@
*/
static inline void trigger_pending_irqs(unsigned int irq)
{
- if (irq_events[irq] && !ipi_pending[irq]) {
+ if (irqs_pending[irq] && !ipi_pending[irq]) {
ipi_pending[irq] = 1;
+ /*
+ * there are no cross-CPU races, as we always send the
+ * IPI to the same CPU, and we hold the irq spinlock
+ */
send_IPI(APIC_DEST_SELF, IO_APIC_VECTOR(irq));
}
}

void enable_ioapic_irq (unsigned int irq)
{
- disabled_irq[irq] = 0;
+ if (disabled_irq[irq])
+ disabled_irq[irq]--;
trigger_pending_irqs(irq);
}

@@ -741,26 +753,29 @@
*/
static void disable_ioapic_irq(unsigned int irq)
{
- disabled_irq[irq] = 1;
+ disabled_irq[irq]++;
}

static void do_ioapic_IRQ(unsigned int irq, int cpu, struct pt_regs * regs)
{
- spin_lock(&irq_controller_lock);
-
- /* Ack the irq inside the lock! */
+ /*
+ * We ack the irq in the local APIC outside the lock
+ * since nobody is supposed to access this APIC now
+ */
ack_APIC_irq();
+
+ spin_lock(&irq_controller_lock);
ipi_pending[irq] = 0;

/* If the irq is disabled, just set a flag and return */
if (disabled_irq[irq]) {
- irq_events[irq] = 1;
+ irqs_pending[irq] = 1;
spin_unlock(&irq_controller_lock);
return;
}

- disabled_irq[irq] = 1;
- irq_events[irq] = 0;
+ disabled_irq[irq]++;
+ irqs_pending[irq] = 0;
hardirq_enter(cpu);
spin_unlock(&irq_controller_lock);

@@ -772,9 +787,9 @@
handle_IRQ_event(irq, regs);

spin_lock(&irq_controller_lock);
- pending = irq_events[irq];
- irq_events[irq] = 0;
- disabled_irq[irq] = pending;
+ pending = irqs_pending[irq];
+ irqs_pending[irq] = 0;
+ disabled_irq[irq] -= 1-pending; /* pending == {0|1} */
spin_unlock(&irq_controller_lock);

if (!pending)
@@ -818,14 +833,8 @@
* SMP cross-CPU interrupts have their own specific
* handlers).
*
- * the biggest change on SMP is the fact that we no more mask
- * interrupts in hardware, please believe me, this is unavoidable,
- * the hardware is largely message-oriented, i tried to force our
- * state-driven irq handling scheme onto the IO-APIC, but no avail.
- *
- * so we soft-disable interrupts via 'event counters', the first 'incl'
- * will do the IRQ handling. This also has the nice side effect of increased
- * overlapping ... i saw no driver problem so far.
+ * the biggest change on SMP is that we no more mask interrupts in
+ * hardware. (we could mask them in the IO-APIC, but we dont do it).
*/
asmlinkage void do_IRQ(struct pt_regs regs)
{
@@ -1070,7 +1079,7 @@
outb(LATCH >> 8 , 0x40); /* MSB */

for (i=0; i<NR_IRQS; i++) {
- irq_events[i] = 0;
+ irqs_pending[i] = 0;
disabled_irq[i] = 0;
}
/*
--- linux/arch/i386/kernel/io_apic.c.orig Tue Apr 21 22:39:49 1998
+++ linux/arch/i386/kernel/io_apic.c Tue May 5 06:24:23 1998
@@ -361,6 +361,21 @@
}
}

+ /*
+ * There are broken mptables which register ISA+high-active+level IRQs,
+ * these are illegal and are converted here to ISA+high-active+edge
+ * IRQ sources. Careful, ISA+low-active+level is another broken entry
+ * type, it represents PCI IRQs 'embedded into an ISA bus', they have
+ * to be accepted. Yes, ugh.
+ */
+ if ( (mp_bus_id_to_type[bus] == MP_BUS_ISA) &&
+ (entry.polarity == 0) /* active-high */ &&
+ (entry.trigger == 1) /* level */ )
+ {
+ printk("broken BIOS, changing pin %d to edge\n", i);
+ entry.trigger = 0;
+ }
+
io_apic_write(0x10+2*i, *(((int *)&entry)+0));
io_apic_write(0x11+2*i, *(((int *)&entry)+1));
}

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu