Messages in this thread Patch in this message |  | | | Date | Thu, 14 May 1998 12:02:09 +0200 (CEST) | | From | MOLNAR Ingo <> | | Subject | [SMP patch] io-apic-patch-2.1.101-J |
| |
On Thu, 14 May 1998, Harald Hoyer wrote:
> But not this one, if this is a typo: > > - irq_desc[i].handler = &ioapic_level_irq_type; > + irq_desc[i].handler = &ioapic_edge_irq_type;
no, it's not a typo, it's in the announcement -F, it's a debugging thing:). We can handle edge triggered IRQs with the new level-triggered handler. But this does not mean we set it up as level-triggered. If you think about it, it's valid, but suboptimal for edge-triggered IRQs. But i wanted to test the new level-triggered part, the edge triggered stuff was working almost always.
> Besides that the -F and -I patch break serial interrupts, after > switching console from svga-apps or X-Server on my computer. Only one > interrupt is served in 2 seconds. pre-2.1.102 works fine...
yes, this is already fixed in io-apic-patch-2.1.101-J. The problem was setup_x86_irq()/free_irq(), they have to atomically change irq descriptors, and they have to mask/umask IRQs properly.
this fix also has a chance to fix other problems, most notably maybe the 'double IRQs on NE2000' problem, and/or the BusLogic boot-hang? Please re-report if there are still problems. The io-apic-patch-2.1.101-J patch is against vanilla pre-patch-2.1.102.
-- mingo
--- linux/arch/i386/kernel/smp.c.orig Mon May 18 04:41:26 1998 +++ linux/arch/i386/kernel/smp.c Mon May 18 04:57:32 1998 @@ -636,9 +636,16 @@ value = apic_read(APIC_SPIV); value |= (1<<8); /* Enable APIC (bit==1) */ value &= ~(1<<9); /* Enable focus processor (bit==0) */ + value |= 0xff; /* Set spurious IRQ vector to 0xff */ apic_write(APIC_SPIV,value); + value = apic_read(APIC_TASKPRI); + value &= ~APIC_TPRI_MASK; /* Set Task Priority to 'accept all' */ + apic_write(APIC_TASKPRI,value); + udelay(100); /* B safe */ + ack_APIC_irq(); + udelay(100); } __initfunc(void smp_callin(void)) @@ -1152,12 +1159,6 @@ __restore_flags(flags); } -void funny (void) -{ - send_IPI(APIC_DEST_ALLBUT,0x30 /*IO_APIC_VECTOR(11)*/); - for(;;)__cli(); -} - /* * A non wait message cannot pass data or cpu source info. This current setup * is only safe because the kernel lock owner is the only person who can send @@ -1474,19 +1475,18 @@ } /* - * Reschedule call back + * Reschedule call back (not used currently) */ + asmlinkage void smp_reschedule_interrupt(void) { int cpu = smp_processor_id(); ack_APIC_irq(); - for (;;) __cli(); /* * This looks silly, but we actually do need to wait * for the global interrupt lock. */ - printk("huh, this is used, where???\n"); irq_enter(cpu, 0); need_resched = 1; irq_exit(cpu, 0); @@ -1519,6 +1519,15 @@ { ack_APIC_irq (); if (mtrr_hook) (*mtrr_hook) (); +} + +/* + * This interrupt should _never_ happen with our APIC/SMP architecture + */ +asmlinkage void smp_spurious_interrupt(void) +{ + ack_APIC_irq (); + printk("spurious APIC interrupt, ayiee, should never happen.\n"); } /* --- linux/arch/i386/kernel/irq.c.orig Mon May 18 04:41:33 1998 +++ linux/arch/i386/kernel/irq.c Wed May 20 00:06:41 1998 @@ -108,7 +108,11 @@ /* * Level and edge triggered IO-APIC interrupts need different handling, - * so we use two separate irq descriptors: + * so we use two separate irq descriptors. edge triggered IRQs can be + * handled with the level-triggered descriptor, but that one has slightly + * more overhead. Level-triggered interrupts cannot be handled with the + * edge-triggered handler, without risking IRQ storms and other ugly + * races. */ static void do_edge_ioapic_IRQ (unsigned int irq, int cpu, @@ -207,17 +211,6 @@ } } -void unmask_generic_irq(unsigned int irq) -{ - irq_desc[irq].status = 0; - if (IO_APIC_IRQ(irq)) - enable_IO_APIC_irq(irq); - else { - cached_irq_mask &= ~(1 << irq); - set_8259A_irq_mask(irq); - } -} - /* * This builds up the IRQ handler stubs using some ugly macros in irq.h * @@ -277,6 +270,7 @@ BUILD_SMP_INTERRUPT(invalidate_interrupt) BUILD_SMP_INTERRUPT(stop_cpu_interrupt) BUILD_SMP_INTERRUPT(mtrr_interrupt) +BUILD_SMP_INTERRUPT(spurious_interrupt) /* * every pentium local APIC has two 'local interrupts', with a @@ -376,6 +370,7 @@ p += sprintf(p, "NMI: %10u\n", atomic_read(&nmi_counter)); #ifdef __SMP__ p += sprintf(p, "IPI: %10lu\n", ipi_count); + print_IO_APIC(); #endif return p - buf; } @@ -695,6 +690,16 @@ set_8259A_irq_mask(irq); } +int i8259A_irq_pending (unsigned int irq) +{ + unsigned int mask = 1<<irq; + + if (irq < 8) + return (inb(0x20) & mask); + return (inb(0xA0) & (mask >> 8)); +} + + void make_8259A_irq (unsigned int irq) { io_apic_irqs &= ~(1<<irq); @@ -771,29 +776,20 @@ { } -/* - * if we enable this, why does it cause a hang in the BusLogic - * driver, when level triggered PCI IRQs are used? - */ -#define NOT_BROKEN 0 - static void enable_level_ioapic_irq(unsigned int irq) { -#if NOT_BROKEN enable_IO_APIC_irq(irq); -#endif self_IPI(irq); } static void disable_level_ioapic_irq(unsigned int irq) { -#if NOT_BROKEN disable_IO_APIC_irq(irq); -#endif } /* - * Has to be called with the irq controller locked + * Has to be called with the irq controller locked, and has to exit + * with with the lock held as well. */ static void handle_ioapic_event (unsigned int irq, int cpu, struct pt_regs * regs) @@ -808,13 +804,18 @@ while (test_bit(0,&global_irq_lock)) barrier(); for (;;) { - int pending; + int pending, handler; - /* If there is no IRQ handler, exit early, leaving the irq "in progress" */ - if (!handle_IRQ_event(irq, regs)) - goto no_handler; + /* + * If there is no IRQ handler, exit early, leaving the irq + * "in progress" + */ + handler = handle_IRQ_event(irq, regs); spin_lock(&irq_controller_lock); + if (!handler) + goto no_handler; + pending = desc->events; desc->events = 0; if (!pending) @@ -822,7 +823,6 @@ spin_unlock(&irq_controller_lock); } desc->status &= IRQ_DISABLED; - spin_unlock(&irq_controller_lock); no_handler: } @@ -850,6 +850,7 @@ } handle_ioapic_event(irq,cpu,regs); + spin_unlock(&irq_controller_lock); hardirq_exit(cpu); release_irqlock(cpu); @@ -862,13 +863,14 @@ spin_lock(&irq_controller_lock); /* - * in the level triggered case we first disable the IRQ + * In the level triggered case we first disable the IRQ * in the IO-APIC, then we 'early ACK' the IRQ, then we * handle it and enable the IRQ when finished. + * + * disable has to happen before the ACK, to avoid IRQ storms. + * So this all has to be within the spinlock. */ -#if NOT_BROKEN disable_IO_APIC_irq(irq); -#endif ack_APIC_irq(); desc->ipi = 0; @@ -883,6 +885,10 @@ } handle_ioapic_event(irq,cpu,regs); + /* we still have the spinlock held here */ + + enable_IO_APIC_irq(irq); + spin_unlock(&irq_controller_lock); hardirq_exit(cpu); release_irqlock(cpu); @@ -981,11 +987,34 @@ struct irqaction *old, **p; unsigned long flags; + /* + * Some drivers like serial.c use request_irq() heavily, + * so we have to be careful not to interfere with a + * running system. + */ + if (new->flags & SA_SAMPLE_RANDOM) { + /* + * This function might sleep, we want to call it first, + * outside of the atomic block. + * Yes, this might clear the entropy pool if the wrong + * driver is attempted to be loaded, without actually + * installing a new handler, but is this really a problem, + * only the sysadmin is able to do this. + */ + rand_initialize_irq(irq); + } + + /* + * The following block of code has to be executed atomically + */ + spin_lock_irqsave(&irq_controller_lock,flags); p = &irq_desc[irq].action; if ((old = *p) != NULL) { /* Can't share interrupts unless both agree to */ - if (!(old->flags & new->flags & SA_SHIRQ)) + if (!(old->flags & new->flags & SA_SHIRQ)) { + spin_unlock_irqrestore(&irq_controller_lock,flags); return -EBUSY; + } /* add new interrupt at end of irq queue */ do { @@ -995,15 +1024,9 @@ shared = 1; } - if (new->flags & SA_SAMPLE_RANDOM) - rand_initialize_irq(irq); - - save_flags(flags); - cli(); *p = new; if (!shared) { - spin_lock(&irq_controller_lock); #ifdef __SMP__ if (IO_APIC_IRQ(irq)) { if (IO_APIC_VECTOR(irq) > 0xfe) @@ -1016,14 +1039,20 @@ * First disable it in the 8259A: */ cached_irq_mask |= 1 << irq; - if (irq < 16) + if (irq < 16) { set_8259A_irq_mask(irq); + /* + * transport pending ISA IRQs to + * the new descriptor + */ + if (i8259A_irq_pending(irq)) + irq_desc[irq].events = 1; + } } #endif - unmask_generic_irq(irq); - spin_unlock(&irq_controller_lock); + irq_desc[irq].handler->enable(irq); } - restore_flags(flags); + spin_unlock_irqrestore(&irq_controller_lock,flags); return 0; } @@ -1065,23 +1094,23 @@ struct irqaction * action, **p; unsigned long flags; - if (irq >= NR_IRQS) { - printk("Trying to free IRQ%d\n",irq); + if (irq >= NR_IRQS) return; - } + + spin_lock_irqsave(&irq_controller_lock,flags); for (p = &irq_desc[irq].action; (action = *p) != NULL; p = &action->next) { if (action->dev_id != dev_id) continue; /* Found it - now free it */ - save_flags(flags); - cli(); *p = action->next; - restore_flags(flags); kfree(action); - return; + irq_desc[irq].handler->disable(irq); + goto out; } printk("Trying to free free IRQ%d\n",irq); +out: + spin_unlock_irqrestore(&irq_controller_lock,flags); } /* @@ -1103,7 +1132,7 @@ spin_lock_irq(&irq_controller_lock); for (i = NR_IRQS-1; i > 0; i--) { if (!irq_desc[i].action) { - unmask_generic_irq(i); + irq_desc[i].handler->enable(i); irqs |= (1 << i); } } @@ -1170,8 +1199,8 @@ (IO_APIC_IRQ(i))) { if (IO_APIC_irq_trigger(i)) irq_desc[i].handler = &ioapic_level_irq_type; - else - irq_desc[i].handler = &ioapic_edge_irq_type; + else /* edge */ + irq_desc[i].handler = &ioapic_level_irq_type; /* * disable it in the 8259A: */ @@ -1233,6 +1262,8 @@ /* IPI for MTRR control */ set_intr_gate(0x50, mtrr_interrupt); + /* IPI vector for APIC spurious interrupts */ + set_intr_gate(0xff, spurious_interrupt); #endif request_region(0x20,0x20,"pic1"); request_region(0xa0,0x20,"pic2"); --- linux/arch/i386/kernel/io_apic.c.orig Mon May 18 04:41:43 1998 +++ linux/arch/i386/kernel/io_apic.c Wed May 20 00:08:36 1998 @@ -110,6 +110,12 @@ int mpc_default_type = 0; /* non-0 if default (table-less) MP configuration */ + +/* + * This is performance-critical, we want to do it O(1) + */ +static int irq_2_pin[NR_IRQS]; + unsigned int io_apic_read (unsigned int reg) { *IO_APIC_BASE = reg; @@ -122,32 +128,32 @@ *(IO_APIC_BASE+4) = value; } -void enable_IO_APIC_irq (unsigned int irq) +/* + * We disable IO-APIC IRQs by setting their 'destination CPU mask' to + * zero. Trick, trick. + */ +void disable_IO_APIC_irq(unsigned int irq) { + int pin = irq_2_pin[irq]; struct IO_APIC_route_entry entry; - /* - * Enable it in the IO-APIC irq-routing table: - */ - *(((int *)&entry)+0) = io_apic_read(0x10+irq*2); - entry.mask = 0; - io_apic_write(0x10+2*irq, *(((int *)&entry)+0)); + if (pin != -1) { + *(((int *)&entry)+1) = io_apic_read(0x11+pin*2); + entry.dest.logical.logical_dest = 0x0; + io_apic_write(0x11+2*pin, *(((int *)&entry)+1)); + } } -/* - * this function is just here to make things complete, otherwise it's - * unused - */ -void disable_IO_APIC_irq (unsigned int irq) +void enable_IO_APIC_irq(unsigned int irq) { + int pin = irq_2_pin[irq]; struct IO_APIC_route_entry entry; - /* - * Disable it in the IO-APIC irq-routing table: - */ - *(((int *)&entry)+0) = io_apic_read(0x10+irq*2); - entry.mask = 1; - io_apic_write(0x10+2*irq, *(((int *)&entry)+0)); + if (pin != -1) { + *(((int *)&entry)+1) = io_apic_read(0x11+pin*2); + entry.dest.logical.logical_dest = 0xff; + io_apic_write(0x11+2*pin, *(((int *)&entry)+1)); + } } void clear_IO_APIC_pin (unsigned int pin) @@ -163,6 +169,7 @@ io_apic_write(0x11+2*pin, *(((int *)&entry)+1)); } + /* * support for broken MP BIOSes, enables hand-redirection of PIRQ0-7 to * specific CPU-side IRQs. @@ -258,129 +265,176 @@ return -1; } -static int irq_trigger(int idx) +/* + * 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. + */ + +static int MPBIOS_polarity(int idx) { int bus = mp_irqs[idx].mpc_srcbus; - int trigger; + int polarity; /* - * Determine IRQ trigger mode (edge or level sensitive): + * Determine IRQ line polarity (high active or low active): */ - switch ((mp_irqs[idx].mpc_irqflag>>2) & 3) + switch (mp_irqs[idx].mpc_irqflag & 3) { - case 0: /* conforms, ie. bus-type dependent */ + case 0: /* conforms, ie. bus-type dependent polarity */ { switch (mp_bus_id_to_type[bus]) { - case MP_BUS_ISA: /* ISA pin, edge */ + case MP_BUS_ISA: /* ISA pin */ { - trigger = 0; + polarity = 0; break; } - case MP_BUS_PCI: /* PCI pin, level */ + case MP_BUS_PCI: /* PCI pin */ { - trigger = 1; + polarity = 1; break; } default: { printk("broken BIOS!!\n"); - trigger = 1; + polarity = 1; break; } } break; } - case 1: /* edge */ + case 1: /* high active */ { - trigger = 0; + polarity = 0; break; } case 2: /* reserved */ { printk("broken BIOS!!\n"); - trigger = 1; + polarity = 1; break; } - case 3: /* level */ + case 3: /* low active */ { - trigger = 1; + polarity = 1; break; } default: /* invalid */ { printk("broken BIOS!!\n"); - trigger = 0; + polarity = 1; break; } } - return trigger; + return polarity; } -__initfunc(static int irq_polarity(int idx)) + +static int MPBIOS_trigger(int idx) { int bus = mp_irqs[idx].mpc_srcbus; - int polarity; + int trigger; /* - * Determine IRQ line polarity (high active or low active): + * Determine IRQ trigger mode (edge or level sensitive): */ - switch (mp_irqs[idx].mpc_irqflag & 3) + switch ((mp_irqs[idx].mpc_irqflag>>2) & 3) { - case 0: /* conforms, ie. bus-type dependent polarity */ + case 0: /* conforms, ie. bus-type dependent */ { switch (mp_bus_id_to_type[bus]) { - case MP_BUS_ISA: /* ISA pin */ + case MP_BUS_ISA: /* ISA pin, edge */ { - polarity = 0; + trigger = 0; break; } - case MP_BUS_PCI: /* PCI pin */ + case MP_BUS_PCI: /* PCI pin, level */ { - polarity = 1; + trigger = 1; break; } default: { printk("broken BIOS!!\n"); - polarity = 1; + trigger = 1; break; } } break; } - case 1: /* high active */ + case 1: /* edge */ { - polarity = 0; + trigger = 0; break; } case 2: /* reserved */ { printk("broken BIOS!!\n"); - polarity = 1; + trigger = 1; break; } - case 3: /* low active */ + case 3: /* level */ { - polarity = 1; + trigger = 1; break; } default: /* invalid */ { printk("broken BIOS!!\n"); - polarity = 1; + trigger = 0; break; } } - return polarity; + return trigger; +} + +static int trigger_flag_broken (int idx) +{ + int bus = mp_irqs[idx].mpc_srcbus; + int polarity = MPBIOS_polarity(idx); + int trigger = MPBIOS_trigger(idx); + + if ( (mp_bus_id_to_type[bus] == MP_BUS_ISA) && + (polarity == 0) /* active-high */ && + (trigger == 1) /* level */ ) + + return 1; /* broken */ + + return 0; +} + +static int irq_polarity (int idx) +{ + /* + * There are no known BIOS bugs wrt polarity. yet. + */ + return MPBIOS_polarity(idx); } -__initfunc(static int pin_2_irq (int idx, int pin)) +static int irq_trigger (int idx) +{ + int trigger = MPBIOS_trigger(idx); + + if (trigger_flag_broken (idx)) + trigger = 0; + return trigger; +} + +static int pin_2_irq (int idx, int pin) { int irq; int bus = mp_irqs[idx].mpc_srcbus; + /* + * Debugging check, we are in big trouble if this message pops up! + */ + if (mp_irqs[idx].mpc_dstirq != pin) + printk("broken BIOS or MPTABLE parser, ayiee!!\n"); + switch (mp_bus_id_to_type[bus]) { case MP_BUS_ISA: /* ISA pin */ @@ -423,12 +477,12 @@ int IO_APIC_irq_trigger (int irq) { - int idx, i; + int idx, pin; - for (i=0; i<nr_ioapic_registers; i++) { - idx = find_irq_entry(i,mp_INT); - if (irq == pin_2_irq(idx,i)) - return irq_trigger(idx); + for (pin=0; pin<nr_ioapic_registers; pin++) { + idx = find_irq_entry(pin,mp_INT); + if ((idx != -1) && (irq == pin_2_irq(idx,pin))) + return (irq_trigger(idx)); } /* * nonexistant IRQs are edge default @@ -439,11 +493,11 @@ __initfunc(void setup_IO_APIC_irqs (void)) { struct IO_APIC_route_entry entry; - int i, idx, bus, irq, first_notcon=1; + int pin, idx, bus, irq, first_notcon=1; printk("init IO_APIC IRQs\n"); - for (i=0; i<nr_ioapic_registers; i++) { + for (pin=0; pin<nr_ioapic_registers; pin++) { /* * add it to the IO-APIC irq-routing table: @@ -455,45 +509,34 @@ entry.mask = 0; /* enable IRQ */ entry.dest.logical.logical_dest = 0xff; /* all CPUs */ - idx = find_irq_entry(i,mp_INT); + idx = find_irq_entry(pin,mp_INT); if (idx == -1) { if (first_notcon) { - printk(" IO-APIC pin %d", i); + printk(" IO-APIC pin %d", pin); first_notcon=0; } else - printk(", %d", i); + printk(", %d", pin); continue; } entry.trigger = irq_trigger(idx); entry.polarity = irq_polarity(idx); - irq = pin_2_irq(idx,i); + irq = pin_2_irq(idx,pin); + irq_2_pin[irq] = pin; if (!IO_APIC_IRQ(irq)) continue; entry.vector = IO_APIC_VECTOR(irq); - /* - * 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. - */ bus = mp_irqs[idx].mpc_srcbus; - 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; - } + if (trigger_flag_broken (idx)) + printk("broken BIOS, changing pin %d to edge\n", pin); - io_apic_write(0x10+2*i, *(((int *)&entry)+0)); - io_apic_write(0x11+2*i, *(((int *)&entry)+1)); + io_apic_write(0x10+2*pin, *(((int *)&entry)+0)); + io_apic_write(0x11+2*pin, *(((int *)&entry)+1)); } if (!first_notcon) @@ -549,7 +592,7 @@ io_apic_write(0x11+2*pin, *(((int *)&entry)+1)); } -__initfunc(void print_IO_APIC (void)) +void print_IO_APIC (void) { int i; struct IO_APIC_reg_00 reg_00; @@ -598,7 +641,7 @@ printk(".... IRQ redirection table:\n"); - printk(" NR Log Phy "); + printk(" NR Log Phy "); printk("Mask Trig IRR Pol Stat Dest Deli Vect: \n"); for (i=0; i<=reg_01.entries; i++) { @@ -607,7 +650,7 @@ *(((int *)&entry)+0) = io_apic_read(0x10+i*2); *(((int *)&entry)+1) = io_apic_read(0x11+i*2); - printk(" %02x %03X %02X ", + printk(" %02x %03X %02X ", i, entry.dest.logical.logical_dest, entry.dest.physical.physical_dest @@ -625,6 +668,11 @@ ); } + printk("IRQ to pin mappings:\n"); + for (i=0; i<NR_IRQS; i++) + printk("%d->%d ", i, irq_2_pin[i]); + printk("\n"); + printk(".................................... done.\n"); return; @@ -632,8 +680,10 @@ __initfunc(static void init_sym_mode (void)) { - int i; + int i, pin; + for (i=0; i<NR_IRQS; i++) + irq_2_pin[i] = -1; if (!pirqs_enabled) for (i=0; i<MAX_PIRQS; i++) pirq_entries[i]=-1; @@ -658,8 +708,8 @@ /* * Do not trust the IO-APIC being empty at bootup */ - for (i=0; i<nr_ioapic_registers; i++) - clear_IO_APIC_pin (i); + for (pin=0; pin<nr_ioapic_registers; pin++) + clear_IO_APIC_pin (pin); } /* --- linux/arch/i386/kernel/irq.h.orig Mon May 18 05:39:42 1998 +++ linux/arch/i386/kernel/irq.h Tue May 19 22:43:23 1998 @@ -12,7 +12,8 @@ void unmask_irq(unsigned int irq); void enable_IO_APIC_irq (unsigned int irq); void disable_IO_APIC_irq (unsigned int irq); -void set_8259A_irq_mask(unsigned int irq); +void set_8259A_irq_mask (unsigned int irq); +int i8259A_irq_pending (unsigned int irq); void ack_APIC_irq (void); void setup_IO_APIC (void); void init_IO_APIC_traps(void); @@ -21,6 +22,7 @@ void make_8259A_irq (unsigned int irq); void send_IPI (int dest, int vector); void init_pic_mode (void); +void print_IO_APIC (void); extern unsigned int io_apic_irqs;
|  |