[PATCH] x86_64: check_timer with io apic setup before try_apic_pin add io apic setup before try_apic_pin for check_timer also add remove_irq_to_pin call in io_apic.c cc: Andi Kleen cc: Eric W. Biederman Signed-off-by: Yinghai Lu 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]; diff --git a/arch/x86_64/kernel/mpparse.c b/arch/x86_64/kernel/mpparse.c index 0807256..a054798 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/arch/x86_64/kernel/io_apic.c b/arch/x86_64/kernel/io_apic.c index 2a1dcd5..ad1a28a 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,51 @@ 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; - /* 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; + /* 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); + } + + /* Always try pin0 and pin2 on APIC 0 to handle buggy timer overrides + on Nvidia boards */ + 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); } /* Tried all possibilities to go through the IO-APIC. Now come the