[patch] Unexpected interrupts, smp-2.2.2 #1

Ingo Molnar (mingo@chiara.csoma.elte.hu)
Tue, 9 Feb 1999 19:49:27 +0100 (CET)


this patch is against vanilla pre2-2.2.2, and it fixes a couple of SMP
problems and cleans some other things up. (the problems fixed in the patch
are noticeable only on a small number of boxes)

- the 'Unexpected interrupt 251' thing is now printed more accurately.
Instead of Philipp Rumpf's fix to that 'Silly, horrible hack' in irq.c i
rewrote the whole thing, now we report unexpected vectors reliably and
are sane too. We dont lock up either.

- some BIOSes do not fill out the APIC version reliably, fortunately we
can detect this case easily. This fixes Eric Hicks's 'secondary CPU
doesnt boot' problem.

- some more 'unexpected IO-APIC's turned into expected ones.

- (the irq.c thing prompted a somewhat bigger cleanup, which in turn
resulted in us scaling up to 240 interrupt sources from the current 64,
there are no such boxes out there AFAIK, but in the future there could
be, 2.2 could break on those, now it doesnt anymore.)

-- mingo

--- linux/include/asm-i386/irq.h.orig2 Tue Feb 9 14:30:58 1999
+++ linux/include/asm-i386/irq.h Tue Feb 9 15:40:23 1999
@@ -13,11 +13,15 @@
#define TIMER_IRQ 0

/*
- * 16 XT IRQ's, 8 potential APIC interrupt sources.
- * Right now the APIC is only used for SMP, but this
- * may change.
+ * 16 8259A IRQ's, 240 potential APIC interrupt sources.
+ * Right now the APIC is mostly only used for SMP.
+ * 256 vectors is an architectural limit. (we can have
+ * more than 256 devices theoretically, but they will
+ * have to use shared interrupts)
+ * Since vectors 0x00-0x1f are used/reserved for the CPU,
+ * the usable vector space is 0x20-0xff (224 vectors)
*/
-#define NR_IRQS 64
+#define NR_IRQS 224

static __inline__ int irq_cannonicalize(int irq)
{
--- linux/arch/i386/kernel/io_apic.c.orig2 Tue Feb 9 11:16:16 1999
+++ linux/arch/i386/kernel/io_apic.c Tue Feb 9 18:53:53 1999
@@ -569,6 +569,9 @@
printk("WARNING: ASSIGN_IRQ_VECTOR wrapped back to %02X\n",
current_vector);
}
+ if (current_vector == SYSCALL_VECTOR)
+ panic("ran out of interrupt sources!");
+
IO_APIC_VECTOR(irq) = current_vector;
return current_vector;
}
@@ -693,9 +696,11 @@

printk(".... register #01: %08X\n", *(int *)&reg_01);
printk("....... : max redirection entries: %04X\n", reg_01.entries);
- if ( (reg_01.entries != 0x0f) && /* ISA-only Neptune boards */
- (reg_01.entries != 0x17) && /* ISA+PCI boards */
- (reg_01.entries != 0x3F) /* Xeon boards */
+ if ( (reg_01.entries != 0x0f) && /* older (Neptune) boards */
+ (reg_01.entries != 0x17) && /* typical ISA+PCI boards */
+ (reg_01.entries != 0x1b) && /* Compaq Proliant boards */
+ (reg_01.entries != 0x1f) && /* dual Xeon boards */
+ (reg_01.entries != 0x3F) /* bigger Xeon boards */
)
UNEXPECTED_IO_APIC();
if (reg_01.entries == 0x0f)
@@ -1163,7 +1168,7 @@
* 0x80, because int 0x80 is hm, kind of importantish. ;)
*/
for (i = 0; i < NR_IRQS ; i++) {
- if (IO_APIC_IRQ(i)) {
+ if (IO_APIC_VECTOR(i) > 0) {
if (IO_APIC_irq_trigger(i))
irq_desc[i].handler = &ioapic_level_irq_type;
else
@@ -1173,8 +1178,10 @@
*/
if (i < 16)
disable_8259A_irq(i);
- }
+ } else
+ irq_desc[i].handler = &no_irq_type;
}
+ init_IRQ_SMP();
}

/*
@@ -1278,14 +1285,12 @@
construct_default_ISA_mptable();
}

- init_IO_APIC_traps();
-
/*
* Set up the IO-APIC IRQ routing table by parsing the MP-BIOS
* mptable:
*/
setup_IO_APIC_irqs();
- init_IRQ_SMP();
+ init_IO_APIC_traps();
check_timer();

print_IO_APIC();
--- linux/arch/i386/kernel/smp.c.orig2 Mon Feb 8 20:42:52 1999
+++ linux/arch/i386/kernel/smp.c Tue Feb 9 17:09:21 1999
@@ -42,7 +42,7 @@

#include "irq.h"

-extern unsigned long start_kernel, _etext;
+extern unsigned long start_kernel;
extern void update_one_process( struct task_struct *p,
unsigned long ticks, unsigned long user,
unsigned long system, int cpu);
@@ -319,8 +319,17 @@
printk("Processor #%d unused. (Max %d processors).\n",m->mpc_apicid, NR_CPUS);
else
{
+ int ver = m->mpc_apicver;
+
cpu_present_map|=(1<<m->mpc_apicid);
- apic_version[m->mpc_apicid]=m->mpc_apicver;
+ /*
+ * Validate version
+ */
+ if (ver == 0x0) {
+ printk("BIOS bug, APIC version is 0 for CPU#%d! fixing up to 0x10. (tell your hw vendor)\n", m->mpc_apicid);
+ ver = 0x10;
+ }
+ apic_version[m->mpc_apicid] = ver;
}
}
mpt+=sizeof(*m);
@@ -1806,8 +1820,10 @@
*/
asmlinkage void smp_spurious_interrupt(void)
{
- /* ack_APIC_irq(); see sw-dev-man vol 3, chapter 7.4.13.5 */
- printk("spurious APIC interrupt, ayiee, should never happen.\n");
+ ack_APIC_irq();
+ /* see sw-dev-man vol 3, chapter 7.4.13.5 */
+ printk("spurious APIC interrupt on CPU#%d, should never happen.\n",
+ smp_processor_id());
}

/*
--- linux/arch/i386/kernel/irq.c.orig2 Tue Feb 9 12:16:40 1999
+++ linux/arch/i386/kernel/irq.c Tue Feb 9 19:06:05 1999
@@ -70,11 +70,24 @@
*/
spinlock_t irq_controller_lock;

-
/*
* Dummy controller type for unused interrupts
*/
-static void do_none(unsigned int irq, struct pt_regs * regs) { }
+static void do_none(unsigned int irq, struct pt_regs * regs)
+{
+ /*
+ * we are careful. While for ISA irqs it's common to happen
+ * outside of any driver (think autodetection), this is not
+ * at all nice for PCI interrupts. So we are stricter and
+ * print a warning when such spurious interrupts happen.
+ * Spurious interrupts can confuse other drivers if the PCI
+ * IRQ line is shared.
+ *
+ * Such spurious interrupts are either driver bugs, or
+ * sometimes hw (chipset) bugs.
+ */
+ printk("unexpected IRQ vector %d on CPU#%d!\n",irq, smp_processor_id());
+}
static void enable_none(unsigned int irq) { }
static void disable_none(unsigned int irq) { }

@@ -82,7 +95,7 @@
#define startup_none enable_none
#define shutdown_none disable_none

-static struct hw_interrupt_type no_irq_type = {
+struct hw_interrupt_type no_irq_type = {
"none",
startup_none,
shutdown_none,
@@ -141,10 +154,10 @@
* fed to the CPU IRQ line directly.
*
* Any '1' bit in this mask means the IRQ is routed through the IO-APIC.
- * this 'mixed mode' IRQ handling costs us one more branch in do_IRQ,
- * but we have _much_ higher compatibility and robustness this way.
+ * this 'mixed mode' IRQ handling costs nothing because it's only used
+ * at IRQ setup time.
*/
-unsigned long long io_apic_irqs = 0;
+unsigned long io_apic_irqs = 0;

/*
* These have to be protected by the irq controller spinlock
@@ -254,32 +267,43 @@


BUILD_COMMON_IRQ()
+
+#define BI(x,y) \
+ BUILD_IRQ(##x##y)
+
+#define BUILD_16_IRQS(x) \
+ BI(x,0) BI(x,1) BI(x,2) BI(x,3) \
+ BI(x,4) BI(x,5) BI(x,6) BI(x,7) \
+ BI(x,8) BI(x,9) BI(x,a) BI(x,b) \
+ BI(x,c) BI(x,d) BI(x,e) BI(x,f)
+
/*
* ISA PIC or low IO-APIC triggered (INTA-cycle or APIC) interrupts:
+ * (these are usually mapped to vectors 0x20-0x30)
*/
-BUILD_IRQ(0) BUILD_IRQ(1) BUILD_IRQ(2) BUILD_IRQ(3)
-BUILD_IRQ(4) BUILD_IRQ(5) BUILD_IRQ(6) BUILD_IRQ(7)
-BUILD_IRQ(8) BUILD_IRQ(9) BUILD_IRQ(10) BUILD_IRQ(11)
-BUILD_IRQ(12) BUILD_IRQ(13) BUILD_IRQ(14) BUILD_IRQ(15)
+BUILD_16_IRQS(0x0)

#ifdef CONFIG_X86_IO_APIC
/*
- * The IO-APIC gives us many more interrupt sources..
+ * The IO-APIC gives us many more interrupt sources. Most of these
+ * are unused but an SMP system is supposed to have enough memory ...
+ * sometimes (mostly wrt. hw bugs) we get corrupted vectors all
+ * across the spectrum, so we really want to be prepared to get all
+ * of these. Plus, more powerful systems might have more than 64
+ * IO-APIC registers.
+ *
+ * (these are usually mapped into the 0x30-0xff vector range)
*/
-BUILD_IRQ(16) BUILD_IRQ(17) BUILD_IRQ(18) BUILD_IRQ(19)
-BUILD_IRQ(20) BUILD_IRQ(21) BUILD_IRQ(22) BUILD_IRQ(23)
-BUILD_IRQ(24) BUILD_IRQ(25) BUILD_IRQ(26) BUILD_IRQ(27)
-BUILD_IRQ(28) BUILD_IRQ(29) BUILD_IRQ(30) BUILD_IRQ(31)
-BUILD_IRQ(32) BUILD_IRQ(33) BUILD_IRQ(34) BUILD_IRQ(35)
-BUILD_IRQ(36) BUILD_IRQ(37) BUILD_IRQ(38) BUILD_IRQ(39)
-BUILD_IRQ(40) BUILD_IRQ(41) BUILD_IRQ(42) BUILD_IRQ(43)
-BUILD_IRQ(44) BUILD_IRQ(45) BUILD_IRQ(46) BUILD_IRQ(47)
-BUILD_IRQ(48) BUILD_IRQ(49) BUILD_IRQ(50) BUILD_IRQ(51)
-BUILD_IRQ(52) BUILD_IRQ(53) BUILD_IRQ(54) BUILD_IRQ(55)
-BUILD_IRQ(56) BUILD_IRQ(57) BUILD_IRQ(58) BUILD_IRQ(59)
-BUILD_IRQ(60) BUILD_IRQ(61) BUILD_IRQ(62) BUILD_IRQ(63)
+ BUILD_16_IRQS(0x1) BUILD_16_IRQS(0x2) BUILD_16_IRQS(0x3)
+BUILD_16_IRQS(0x4) BUILD_16_IRQS(0x5) BUILD_16_IRQS(0x6) BUILD_16_IRQS(0x7)
+BUILD_16_IRQS(0x8) BUILD_16_IRQS(0x9) BUILD_16_IRQS(0xa) BUILD_16_IRQS(0xb)
+BUILD_16_IRQS(0xc) BUILD_16_IRQS(0xd)
#endif

+#undef BUILD_16_IRQS
+#undef BI
+
+
#ifdef __SMP__
/*
* The following vectors are part of the Linux architecture, there
@@ -303,37 +327,35 @@

#endif

+#define IRQ(x,y) \
+ IRQ##x##y##_interrupt
+
+#define IRQLIST_16(x) \
+ IRQ(x,0), IRQ(x,1), IRQ(x,2), IRQ(x,3), \
+ IRQ(x,4), IRQ(x,5), IRQ(x,6), IRQ(x,7), \
+ IRQ(x,8), IRQ(x,9), IRQ(x,a), IRQ(x,b), \
+ IRQ(x,c), IRQ(x,d), IRQ(x,e), IRQ(x,f)
+
static void (*interrupt[NR_IRQS])(void) = {
- IRQ0_interrupt, IRQ1_interrupt, IRQ2_interrupt, IRQ3_interrupt,
- IRQ4_interrupt, IRQ5_interrupt, IRQ6_interrupt, IRQ7_interrupt,
- IRQ8_interrupt, IRQ9_interrupt, IRQ10_interrupt, IRQ11_interrupt,
- IRQ12_interrupt, IRQ13_interrupt, IRQ14_interrupt, IRQ15_interrupt
+ IRQLIST_16(0x0),
+
#ifdef CONFIG_X86_IO_APIC
- ,IRQ16_interrupt, IRQ17_interrupt, IRQ18_interrupt, IRQ19_interrupt,
- IRQ20_interrupt, IRQ21_interrupt, IRQ22_interrupt, IRQ23_interrupt,
- IRQ24_interrupt, IRQ25_interrupt, IRQ26_interrupt, IRQ27_interrupt,
- IRQ28_interrupt, IRQ29_interrupt,
- IRQ30_interrupt, IRQ31_interrupt, IRQ32_interrupt, IRQ33_interrupt,
- IRQ34_interrupt, IRQ35_interrupt, IRQ36_interrupt, IRQ37_interrupt,
- IRQ38_interrupt, IRQ39_interrupt,
- IRQ40_interrupt, IRQ41_interrupt, IRQ42_interrupt, IRQ43_interrupt,
- IRQ44_interrupt, IRQ45_interrupt, IRQ46_interrupt, IRQ47_interrupt,
- IRQ48_interrupt, IRQ49_interrupt,
- IRQ50_interrupt, IRQ51_interrupt, IRQ52_interrupt, IRQ53_interrupt,
- IRQ54_interrupt, IRQ55_interrupt, IRQ56_interrupt, IRQ57_interrupt,
- IRQ58_interrupt, IRQ59_interrupt,
- IRQ60_interrupt, IRQ61_interrupt, IRQ62_interrupt, IRQ63_interrupt
+ IRQLIST_16(0x1), IRQLIST_16(0x2), IRQLIST_16(0x3),
+ IRQLIST_16(0x4), IRQLIST_16(0x5), IRQLIST_16(0x6), IRQLIST_16(0x7),
+ IRQLIST_16(0x8), IRQLIST_16(0x9), IRQLIST_16(0xa), IRQLIST_16(0xb),
+ IRQLIST_16(0xc), IRQLIST_16(0xd)
#endif
};

+#undef IRQ
+#undef IRQLIST_16
+

/*
- * Initial irq handlers.
+ * Special irq handlers.
*/

-void no_action(int cpl, void *dev_id, struct pt_regs *regs)
-{
-}
+void no_action(int cpl, void *dev_id, struct pt_regs *regs) { }

#ifndef CONFIG_VISWS
/*
@@ -770,7 +792,7 @@
* 0 return value means that this irq is already being
* handled by some other CPU. (or is disabled)
*/
- unsigned int irq = regs.orig_eax & 0xff;
+ int irq = regs.orig_eax;
int cpu = smp_processor_id();

kstat.irqs[cpu][irq]++;
@@ -986,42 +1008,6 @@
return irq_found;
}

-/*
- * Silly, horrible hack
- */
-static char uglybuffer[10*256];
-
-__asm__("\n" __ALIGN_STR"\n"
- "common_unexpected:\n\t"
- SAVE_ALL
- "pushl $ret_from_intr\n\t"
- "jmp strange_interrupt");
-
-void strange_interrupt(int irqnum)
-{
- printk("Unexpected interrupt %d\n", irqnum & 255);
- for (;;);
-}
-
-extern int common_unexpected;
-__initfunc(void init_unexpected_irq(void))
-{
- int i;
- for (i = 0; i < 256; i++) {
- char *code = uglybuffer + 10*i;
- unsigned long jumpto = (unsigned long) &common_unexpected;
-
- jumpto -= (unsigned long)(code+10);
- code[0] = 0x68; /* pushl */
- *(int *)(code+1) = i - 512;
- code[5] = 0xe9; /* jmp */
- *(int *)(code+6) = jumpto;
-
- set_intr_gate(i,code);
- }
-}
-
-
void init_ISA_irqs (void)
{
int i;
@@ -1033,7 +1019,7 @@

if (i < 16) {
/*
- * 16 old-style INTA-cycle interrupt gates:
+ * 16 old-style INTA-cycle interrupts:
*/
irq_desc[i].handler = &i8259A_irq_type;
} else {
@@ -1054,9 +1040,16 @@
#else
init_VISWS_APIC_irqs();
#endif
-
- for (i = 0; i < 16; i++)
- set_intr_gate(0x20+i,interrupt[i]);
+ /*
+ * Cover the whole vector space, no vector can escape
+ * us. (some of these will be overridden and become
+ * 'special' SMP interrupts)
+ */
+ for (i = 0; i < NR_IRQS; i++) {
+ int vector = FIRST_EXTERNAL_VECTOR + i;
+ if (vector != SYSCALL_VECTOR)
+ set_intr_gate(vector, interrupt[i]);
+ }

#ifdef __SMP__

@@ -1067,13 +1060,9 @@
set_intr_gate(IRQ0_TRAP_VECTOR, interrupt[0]);

/*
- * The reschedule interrupt slowly changes it's functionality,
- * while so far it was a kind of broadcasted timer interrupt,
- * in the future it should become a CPU-to-CPU rescheduling IPI,
- * driven by schedule() ?
+ * The reschedule interrupt is a CPU-to-CPU reschedule-helper
+ * IPI, driven by wakeup.
*/
-
- /* IPI for rescheduling */
set_intr_gate(RESCHEDULE_VECTOR, reschedule_interrupt);

/* IPI for invalidation */
--- linux/arch/i386/kernel/irq.h.orig2 Tue Feb 9 12:36:07 1999
+++ linux/arch/i386/kernel/irq.h Tue Feb 9 18:40:46 1999
@@ -16,6 +16,7 @@
void (*disable)(unsigned int irq);
};

+extern struct hw_interrupt_type no_irq_type;

/*
* IRQ line status.
@@ -41,6 +42,18 @@
} irq_desc_t;

/*
+ * IDT vectors usable for external interrupt sources start
+ * at 0x20:
+ */
+#define FIRST_EXTERNAL_VECTOR 0x20
+
+#define SYSCALL_VECTOR 0x80
+
+/*
+ * Vectors 0x20-0x2f are used for ISA interrupts.
+ */
+
+/*
* Special IRQ vectors used by the SMP architecture:
*
* (some of the following vectors are 'rare', they might be merged
@@ -54,7 +67,7 @@
#define MTRR_CHANGE_VECTOR 0x50

/*
- * First vector available to drivers: (vectors 0x51-0xfe)
+ * First APIC vector available to drivers: (vectors 0x51-0xfe)
*/
#define IRQ0_TRAP_VECTOR 0x51

@@ -94,7 +107,9 @@
extern void init_pic_mode(void);
extern void print_IO_APIC(void);

-extern unsigned long long io_apic_irqs;
+extern unsigned long io_apic_irqs;
+
+extern char _stext, _etext;

#define MAX_IRQ_SOURCES 128
#define MAX_MP_BUSSES 32
@@ -126,7 +141,7 @@
hardirq_exit(cpu);
}

-#define IO_APIC_IRQ(x) ((1<<x) & io_apic_irqs)
+#define IO_APIC_IRQ(x) (((x) >= 16) || ((1<<(x)) & io_apic_irqs))

#else

@@ -206,7 +221,7 @@
__asm__( \
"\n"__ALIGN_STR"\n" \
SYMBOL_NAME_STR(IRQ) #nr "_interrupt:\n\t" \
- "pushl $"#nr"-256\n\t" \
+ "pushl $"#nr"\n\t" \
"jmp common_interrupt");

/*
@@ -216,7 +231,6 @@
static inline void x86_do_profile (unsigned long eip)
{
if (prof_buffer && current->pid) {
- extern int _stext;
eip -= (unsigned long) &_stext;
eip >>= prof_shift;
/*
--- linux/arch/i386/kernel/traps.c.orig2 Tue Feb 9 15:05:55 1999
+++ linux/arch/i386/kernel/traps.c Tue Feb 9 16:13:02 1999
@@ -42,6 +42,8 @@
#include <asm/lithium.h>
#endif

+#include "irq.h"
+
asmlinkage int system_call(void);
asmlinkage void lcall7(void);

@@ -125,7 +127,6 @@
unsigned long esp;
unsigned short ss;
unsigned long *stack, addr, module_start, module_end;
- extern char _stext, _etext;

__cli();
smp_send_stop();
@@ -676,9 +677,6 @@
#endif
void __init trap_init(void)
{
- /* Initially up all of the IDT to jump to unexpected */
- init_unexpected_irq();
-
if (readl(0x0FFFD9) == 'E' + ('I'<<8) + ('S'<<16) + ('A'<<24))
EISA_bus = 1;
set_call_gate(&default_ldt,lcall7);
@@ -700,7 +698,7 @@
set_trap_gate(15,&spurious_interrupt_bug);
set_trap_gate(16,&coprocessor_error);
set_trap_gate(17,&alignment_check);
- set_system_gate(0x80,&system_call);
+ set_system_gate(SYSCALL_VECTOR,&system_call);

/* set up GDT task & ldt entries */
set_tss_desc(0, &init_task.tss);

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/