[PATCH] IO-APIC partial enabler, 2.1.84

MOLNAR Ingo (mingo@chiara.csoma.elte.hu)
Sat, 31 Jan 1998 21:56:25 +0100 (CET)


this patch brings IO-APIC IRQ handling in sync with the changed IRQ model,
and enables IO-APIC IRQs in some very basic & safe situations:

- when the IO-APIC has no PCI pins connected
- when the user has explicitly specified a pirq= line

i've tested these changes with very heavy parallel networking and disk IO
load, and saw not a single problem. The UP and 486 case is not tested yet
(it's untouched).

there is no black/whitelist yet, will add them ASAP.

the patch also includes 8390.c changes, with them i got rid of those
annoying 'reentering ...' messages.

the pt_regs solution in disable_irq() is quite ugly, is there any better
solution to 'simulate' interrupts from syscall level?

-- mingo

--- 2.1.84/linux/arch/i386/kernel/irq.c Fri Feb 6 02:28:30 1998
+++ linux/arch/i386/kernel/irq.c Fri Feb 6 09:21:56 1998
@@ -98,9 +98,6 @@

static int irq_events [NR_IRQS] = { -1, };
static int disabled_irq [NR_IRQS] = { 0, };
-#ifdef __SMP__
-static int irq_owner [NR_IRQS] = { NO_PROC_ID, };
-#endif

/*
* Not all IRQs can be routed through the IO-APIC, eg. on certain (older)
@@ -125,7 +122,7 @@
* - explicitly use irq 16-19 depending on which PCI irq
* line your PCI controller uses.
*/
- unsigned int io_apic_irqs = 0xff0000;
+ unsigned int io_apic_irqs = 0;
#endif

static inline void mask_8259A(int irq)
@@ -609,12 +606,40 @@

void enable_irq(unsigned int irq)
{
+ int cpu = smp_processor_id(), should_handle_irq;
unsigned long flags;

spin_lock_irqsave(&irq_controller_lock, flags);
disabled_irq[irq]--;
- unmask_irq(irq);
- spin_unlock_irqrestore(&irq_controller_lock, flags);
+
+ if (IO_APIC_IRQ(irq) && !disabled_irq[irq] && irq_events[irq]) {
+ struct pt_regs regs;
+
+ disabled_irq[irq]++;
+ spin_unlock(&irq_controller_lock);
+ release_irqlock(cpu);
+ irq_enter(cpu, irq);
+again:
+ handle_IRQ_event(irq, &regs);
+
+ spin_lock(&irq_controller_lock);
+ disabled_irq[irq]--;
+ should_handle_irq=0;
+ if (--irq_events[irq] && !disabled_irq[irq]) {
+ should_handle_irq=1;
+ disabled_irq[irq]++;
+ }
+ spin_unlock(&irq_controller_lock);
+
+ if (should_handle_irq)
+ goto again;
+
+ irq_exit(cpu, irq);
+ __restore_flags(flags);
+ } else {
+ unmask_irq(irq);
+ spin_unlock_irqrestore(&irq_controller_lock, flags);
+ }
}

/*
@@ -661,40 +686,34 @@
*/
static void do_ioapic_IRQ(int irq, int cpu, struct pt_regs * regs)
{
- int should_handle_irq;
+ int should_handle_irq = 0;
+
+ ack_APIC_irq();

spin_lock(&irq_controller_lock);
- should_handle_irq = 0;
- if (!irq_events[irq]++ && !disabled_irq[irq]) {
- should_handle_irq = 1;
- irq_owner[irq] = cpu;
- hardirq_enter(cpu);
- }

- ack_APIC_irq();
+ if (!irq_events[irq]++ && !disabled_irq[irq])
+ should_handle_irq = 1;

spin_unlock(&irq_controller_lock);
-
- if (should_handle_irq) {
-again:
- if (!handle_IRQ_event(irq, regs))
- disabled_irq[irq] = 1;

- }
+ irq_enter(cpu, irq);

- spin_lock(&irq_controller_lock);
- release_irqlock(cpu);
+ if (should_handle_irq) {
+again:
+ handle_IRQ_event(irq, regs);

- if ((--irq_events[irq]) && (!disabled_irq[irq]) && should_handle_irq) {
+ spin_lock(&irq_controller_lock);
+ should_handle_irq=0;
+ if (--irq_events[irq] && !disabled_irq[irq])
+ should_handle_irq=1;
spin_unlock(&irq_controller_lock);
- goto again;
- }

- irq_owner[irq] = NO_PROC_ID;
- hardirq_exit(cpu);
- spin_unlock(&irq_controller_lock);
+ if (should_handle_irq)
+ goto again;
+ }

- enable_IO_APIC_irq(irq);
+ irq_exit(cpu, irq);
}
#endif

@@ -861,7 +880,7 @@
unsigned long probe_irq_on (void)
{
unsigned int i, j, irqs = 0;
- unsigned long delay;
+ unsigned long delay, flags;

/*
* save current irq counts
@@ -873,10 +892,10 @@
*/
for (i = NR_IRQS-1; i > 0; i--) {
if (!irq_action[i]) {
- spin_lock(&irq_controller_lock);
+ spin_lock_irqsave(&irq_controller_lock,flags);
unmask_irq(i);
irqs |= (1 << i);
- spin_unlock(&irq_controller_lock);
+ spin_unlock_irqrestore(&irq_controller_lock,flags);
}
}

@@ -961,9 +980,6 @@
printk("INIT IRQ\n");
for (i=0; i<NR_IRQS; i++) {
irq_events[i] = 0;
-#ifdef __SMP__
- irq_owner[i] = NO_PROC_ID;
-#endif
disabled_irq[i] = 0;
}
/*
--- 2.1.84/linux/arch/i386/kernel/smp.c Sun Feb 1 01:17:45 1998
+++ linux/arch/i386/kernel/smp.c Fri Feb 6 09:32:24 1998
@@ -367,14 +367,6 @@
--mp_irq_entries;
}

-printk(" Itype:%d Iflag:%d srcbus:%d srcbusI:%d dstapic:%d dstI:%d.\n",
- m->mpc_irqtype,
- m->mpc_irqflag,
- m->mpc_srcbus,
- m->mpc_srcbusirq,
- m->mpc_dstapic,
- m->mpc_dstirq);
-
mpt+=sizeof(*m);
count+=sizeof(*m);
break;
--- 2.1.84/linux/arch/i386/kernel/io_apic.c Tue Jan 27 04:22:12 1998
+++ linux/arch/i386/kernel/io_apic.c Fri Feb 6 09:45:13 1998
@@ -129,7 +129,7 @@
* Disable it in the IO-APIC irq-routing table:
*/
*(((int *)&entry)+0) = io_apic_read(0x10+irq*2);
- entry.mask = 1;
+ entry.mask = 0;
io_apic_write(0x10+2*irq, *(((int *)&entry)+0));
}

@@ -151,8 +151,9 @@
* specific CPU-side IRQs.
*/

-#define MAX_PIRQS 4
+#define MAX_PIRQS 8
int pirq_entries [MAX_PIRQS];
+int pirqs_enabled;

void ioapic_pirq_setup(char *str, int *ints)
{
@@ -161,9 +162,12 @@
for (i=0; i<MAX_PIRQS; i++)
pirq_entries[i]=-1;

- if (!ints)
+ if (!ints) {
+ pirqs_enabled=0;
printk("PIRQ redirection SETUP, trusting MP-BIOS.\n");
- else {
+
+ } else {
+ pirqs_enabled=1;
printk("PIRQ redirection SETUP, working around broken MP-BIOS.\n");
max = MAX_PIRQS;
if (ints[0] < MAX_PIRQS)
@@ -503,6 +507,18 @@
if (IO_APIC_IRQ(i))
setup_IO_APIC_irq_ISA_default (i);
#endif
+
+ /*
+ * the following IO-APIC's can be enabled without any
+ * blacklist/whitelist checking:
+ *
+ * - which have no PCI pins connected
+ * - for which the user has specified a pirq= parameter
+ */
+ if ((nr_ioapic_registers == 16) || pirqs_enabled)
+ io_apic_irqs = ~((1<<0)|(1<<2)|(1<<13));
+ else
+ io_apic_irqs = 0;

setup_IO_APIC_irqs ();

--- 2.1.84/linux/drivers/net/8390.c Thu Jan 8 12:16:26 1998
+++ linux/drivers/net/8390.c Fri Feb 6 06:48:40 1998
@@ -54,6 +54,8 @@
#include <asm/uaccess.h>
#include <asm/bitops.h>
#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/delay.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/in.h>
@@ -185,10 +187,12 @@

/* Mask interrupts from the ethercard. */
outb_p(0x00, e8390_base + EN0_IMR);
+ disable_irq(dev->irq);
synchronize_irq();
if (dev->interrupt) {
printk("%s: Tx request while isr active.\n",dev->name);
outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+ enable_irq(dev->irq);
ei_local->stat.tx_errors++;
dev_kfree_skb(skb, FREE_WRITE);
return 0;
@@ -226,6 +230,7 @@
ei_local->irqlock = 0;
dev->tbusy = 1;
outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+ enable_irq(dev->irq);
ei_local->stat.tx_errors++;
return 1;
}
@@ -272,6 +277,7 @@
/* Turn 8390 interrupts back on. */
ei_local->irqlock = 0;
outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+ enable_irq(dev->irq);

dev_kfree_skb (skb, FREE_WRITE);
ei_local->stat.tx_bytes += send_length;
@@ -608,7 +614,6 @@
static void ei_rx_overrun(struct device *dev)
{
int e8390_base = dev->base_addr;
- unsigned long wait_start_time;
unsigned char was_txing, must_resend = 0;
struct ei_device *ei_local = (struct ei_device *) dev->priv;

@@ -629,9 +634,7 @@
* it "is not a reliable indicator and subsequently should be ignored."
* We wait at least 10ms.
*/
- wait_start_time = jiffies;
- while (jiffies - wait_start_time <= 1*HZ/100)
- barrier();
+ udelay(10*1000);

/*
* Reset RBCR[01] back to zero as per magic incantation.