Re: 2.6.20-rc3: known unfixed regressions - x86_64 boot failure: "IO-APIC + timer doesn't work"

From: Yinghai Lu
Date: Wed Jan 03 2007 - 01:23:49 EST


Please check the latest version. ( 01/02/2007)

YH [PATCH] x86_64: check_timer with io apic setup before try_apic_pin

In the check_timer, it forget to set up the io apic before try_apic_pin for
timer. So add set_try_apic_pin to set up the io apic pin. otherwise the
try_apic_pin will not work.

also add remove_irq_to_pin to corresponging to add_pin_to_irq to make irq_2_pin
more complete and make the set io apic more convenient.

Also add add_irq_entry in mparese.c to add apic/pin pair to mp_irqs in case
some nvidia based MB have wrong io apic pin entry for timer. Some ck804 based
MB, MPTABLE or ACPI assume timer is on ioapic pin2. but HW is set to pin0. or
reversing case.

the check_timer will try
1. apic1, pin1 with 8259 IRQ0 disabled
2. apic=0, pin=0 with 8259 IRQ0 disabled
3. apic=0, pin=2 with 8259 IRQ0 disabled
4. apic1, pin1 with 8259 IRQ0 enabled
5. apic2, pin2, pure 8259A routing on the 8259 as reported by BIOS

without the patch:
..TIMER: trying IO-APIC=0 PIN=0 with 8259 IRQ0 disabled<3> .. failed
..TIMER: trying IO-APIC=0 PIN=0 with 8259 IRQ0 enabled<7>APIC error on CPU0: 00(40) .. failed
..TIMER: trying IO-APIC=0 PIN=2 fallback with 8259 IRQ0 disabled<3> .. failed
..TIMER: trying IO-APIC=0 PIN=0 8259A broadcast ExtINT from BIOS<7>number of MP IRQ sources: 84.
testing the IO APIC.......................


.................................... done.

with the patch:
..TIMER: trying IO-APIC=0 PIN=0 with 8259 IRQ0 disabled<3> .. failed
..TIMER: trying IO-APIC=0 PIN=2 fallback with 8259 IRQ0 disabled<7>number of MP IRQ sources: 85.
testing the IO APIC.......................


.................................... done.

cc: Andi Kleen <ak@xxxxxxx>
cc: Eric W. Biederman <ebiederm@xxxxxxxxxxxx>
Signed-off-by: Yinghai Lu <yinghai.lu@xxxxxxx>

diff --git a/arch/x86_64/kernel/io_apic.c b/arch/x86_64/kernel/io_apic.c
index 2a1dcd5..e200d0a 100644
--- a/arch/x86_64/kernel/io_apic.c
+++ b/arch/x86_64/kernel/io_apic.c
@@ -273,10 +273,17 @@ static void add_pin_to_irq(unsigned int irq, int apic, int pin)
struct irq_pin_list *entry = irq_2_pin + irq;

BUG_ON(irq >= NR_IRQS);
- while (entry->next)
+ while (entry->next) {
+ if (entry->apic == apic && entry->pin == pin)
+ return;
+ if (entry->pin == -1)
+ break;
entry = irq_2_pin + entry->next;
+ }

if (entry->pin != -1) {
+ if (entry->apic == apic && entry->pin == pin)
+ return;
entry->next = first_free_entry;
entry = irq_2_pin + entry->next;
if (++first_free_entry >= PIN_MAP_SIZE)
@@ -286,6 +293,39 @@ static void add_pin_to_irq(unsigned int irq, int apic, int pin)
entry->pin = pin;
}

+static void remove_pin_to_irq(unsigned int irq, int apic, int pin)
+{
+ struct irq_pin_list *entry = irq_2_pin + irq;
+ struct irq_pin_list *pri;
+ struct irq_pin_list *next;
+
+ BUG_ON(irq >= NR_IRQS);
+
+ for (;;) {
+ if (entry->apic == apic && entry->pin == pin) {
+ if(entry->next) {
+ next = irq_2_pin + entry->next;
+ entry->apic = next->apic;
+ entry->pin = next->pin;
+ entry->next = next->next;
+ next->apic = -1;
+ next->pin = -1;
+ next->next = 0;
+ } else {
+ entry->apic = -1;
+ entry->pin = -1;
+ }
+ return;
+ }
+ pri = entry;
+ if (pri->next)
+ entry = irq_2_pin + pri->next;
+ else
+ break;
+ }
+
+}
+

#define DO_ACTION(name,R,ACTION, FINAL) \
\
@@ -1570,6 +1610,22 @@ static inline void unlock_ExtINT_logic(void)
* fanatically on his truly buggy board.
*/

+static void set_try_apic_pin(int apic, int pin, int type)
+{
+ int idx;
+ int irq = 0;
+ int bus = 0; /* MP_ISA_BUS */
+ int irqflag = 5; /* MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH */
+
+ idx = find_irq_entry(apic,pin,type);
+
+ if (idx == -1)
+ idx = add_irq_entry(type, irqflag, bus, irq, apic, pin);
+
+ add_pin_to_irq(irq, apic, pin);
+ setup_IO_APIC_irq(apic, pin, idx, irq);
+}
+
static int try_apic_pin(int apic, int pin, char *msg)
{
apic_printk(APIC_VERBOSE, KERN_INFO
@@ -1588,7 +1644,7 @@ static int try_apic_pin(int apic, int pin, char *msg)
}
return 1;
}
- clear_IO_APIC_pin(apic, pin);
+
apic_printk(APIC_QUIET, KERN_ERR " .. failed\n");
return 0;
}
@@ -1599,6 +1655,7 @@ static void check_timer(void)
int apic1, pin1, apic2, pin2;
int vector;
cpumask_t mask;
+ int i;

/*
* get/set the timer IRQ vector:
@@ -1621,33 +1678,60 @@ static void check_timer(void)
pin2 = ioapic_i8259.pin;
apic2 = ioapic_i8259.apic;

- /* Do this first, otherwise we get double interrupts on ATI boards */
- if ((pin1 != -1) && try_apic_pin(apic1, pin1,"with 8259 IRQ0 disabled"))
- return;
+ apic_printk(APIC_VERBOSE,KERN_INFO "..TIMER: vector=0x%02X apic1=%d pin1=%d apic2=%d pin2=%d\n",
+ vector, apic1, pin1, apic2, pin2);

- /* Now try again with IRQ0 8259A enabled.
- Assumes timer is on IO-APIC 0 ?!? */
- enable_8259A_irq(0);
- unmask_IO_APIC_irq(0);
- if (try_apic_pin(apic1, pin1, "with 8259 IRQ0 enabled"))
- return;
- disable_8259A_irq(0);
+ if (pin1 != -1) {
+ /* Do this first, otherwise we get double interrupts on ATI boards */
+ /* set_try_apic_pin will call disable_8259A_irq */
+ set_try_apic_pin(apic1, pin1, mp_INT);
+ unmask_IO_APIC_irq(0);
+ if (try_apic_pin(apic1, pin1,"with 8259 IRQ0 disabled"))
+ return;
+
+ clear_IO_APIC_pin(apic1, pin1);
+ remove_pin_to_irq(0, apic1, pin1);
+ }

/* Always try pin0 and pin2 on APIC 0 to handle buggy timer overrides
on Nvidia boards */
- if (!(apic1 == 0 && pin1 == 0) &&
- try_apic_pin(0, 0, "fallback with 8259 IRQ0 disabled"))
- return;
- if (!(apic1 == 0 && pin1 == 2) &&
- try_apic_pin(0, 2, "fallback with 8259 IRQ0 disabled"))
- return;
+ for (i = 0; i <= 2; i += 2)
+ if (!(apic1 == 0 && pin1 == i)) {
+ /* set_try_apic_pin will call disable_8259A_irq */
+ set_try_apic_pin(0, i, mp_INT);
+ unmask_IO_APIC_irq(0);
+ if (try_apic_pin(0, i, "fallback with 8259 IRQ0 disabled"))
+ return;
+
+ clear_IO_APIC_pin(0, i);
+ remove_pin_to_irq(0, 0, i);
+ }
+
+ if (pin1 != -1) {
+ /* set_try_apic_pin will call disable_8259A_irq */
+ set_try_apic_pin(apic1, pin1, mp_INT);
+ unmask_IO_APIC_irq(0);
+
+ /* Now try again with IRQ0 8259A enabled.
+ Assumes timer is on IO-APIC 0 ?!? */
+ enable_8259A_irq(0);
+ if (try_apic_pin(apic1, pin1, "with 8259 IRQ0 enabled"))
+ return;
+ disable_8259A_irq(0);
+
+ clear_IO_APIC_pin(apic1, pin1);
+ remove_pin_to_irq(0, apic1, pin1);
+ }

/* Then try pure 8259A routing on the 8259 as reported by BIOS*/
- enable_8259A_irq(0);
if (pin2 != -1) {
setup_ExtINT_IRQ0_pin(apic2, pin2, vector);
+ add_pin_to_irq(0, apic2, pin2);
+ enable_8259A_irq(0);
if (try_apic_pin(apic2,pin2,"8259A broadcast ExtINT from BIOS"))
return;
+ clear_IO_APIC_pin(apic2, pin2);
+ remove_pin_to_irq(0, apic2, pin2);
}

/* Tried all possibilities to go through the IO-APIC. Now come the
diff --git a/arch/x86_64/kernel/mpparse.c b/arch/x86_64/kernel/mpparse.c
index 0807256..5f7a95a 100644
--- a/arch/x86_64/kernel/mpparse.c
+++ b/arch/x86_64/kernel/mpparse.c
@@ -314,6 +314,34 @@ static int __init ELCR_trigger(unsigned int irq)
return (inb(port) >> (irq & 7)) & 1;
}

+int add_irq_entry(int type, int irqflag, int bus, int irq, int apic, int pin)
+{
+ struct mpc_config_intsrc intsrc;
+ int idx;
+
+ intsrc.mpc_type = MP_INTSRC;
+ intsrc.mpc_irqflag = irqflag; /* conforming */
+ intsrc.mpc_srcbus = bus;
+ intsrc.mpc_dstapic = (apic != -1) ? mp_ioapics[apic].mpc_apicid: MP_APIC_ALL;
+
+ intsrc.mpc_irqtype = type;
+
+ intsrc.mpc_srcbusirq = irq;
+ intsrc.mpc_dstirq = pin;
+
+ mp_irqs [mp_irq_entries] = intsrc;
+ Dprintk("Int: type %d, pol %d, trig %d, bus %d,"
+ " IRQ %02x, APIC ID %x, APIC INT %02x\n",
+ intsrc.mpc_irqtype, intsrc.mpc_irqflag & 3,
+ (intsrc.mpc_irqflag >> 2) & 3, intsrc.mpc_srcbus,
+ intsrc.mpc_srcbusirq, intsrc.mpc_dstapic, intsrc.mpc_dstirq);
+ idx = mp_irq_entries;
+ if (++mp_irq_entries >= MAX_IRQ_SOURCES)
+ panic("Max # of irq sources exceeded!!\n");
+ return idx;
+
+}
+
static void __init construct_default_ioirq_mptable(int mpc_default_type)
{
struct mpc_config_intsrc intsrc;
diff --git a/include/asm-x86_64/mpspec.h b/include/asm-x86_64/mpspec.h
index 017fddb..1ddfd4d 100644
--- a/include/asm-x86_64/mpspec.h
+++ b/include/asm-x86_64/mpspec.h
@@ -165,6 +165,7 @@ extern int mp_bus_id_to_pci_bus [MAX_MP_BUSSES];
extern unsigned int boot_cpu_physical_apicid;
extern int smp_found_config;
extern void find_smp_config (void);
+extern int add_irq_entry (int type, int irqflag, int bus, int irq, int apic, int pin);
extern void get_smp_config (void);
extern int nr_ioapics;
extern unsigned char apic_version [MAX_APICS];