📄 io_apic.c
字号:
/* Try to find the IRQ that is closest to the imbalance * without going over. */ if (move_this_load < IRQ_DELTA(max_loaded,j)) { move_this_load = IRQ_DELTA(max_loaded,j); selected_irq = j; } } if (selected_irq == -1) { goto tryanothercpu; } imbalance = move_this_load; /* For physical_balance case, we accumlated both load * values in the one of the siblings cpu_irq[], * to use the same code for physical and logical processors * as much as possible. * * NOTE: the cpu_irq[] array holds the sum of the load for * sibling A and sibling B in the slot for the lowest numbered * sibling (A), _AND_ the load for sibling B in the slot for * the higher numbered sibling. * * We seek the least loaded sibling by making the comparison * (A+B)/2 vs B */ load = CPU_IRQ(min_loaded) >> 1; for_each_cpu_mask(j, cpu_sibling_map[min_loaded]) { if (load > CPU_IRQ(j)) { /* This won't change cpu_sibling_map[min_loaded] */ load = CPU_IRQ(j); min_loaded = j; } } cpus_and(allowed_mask, cpu_online_map, irq_affinity[selected_irq]); target_cpu_mask = cpumask_of_cpu(min_loaded); cpus_and(tmp, target_cpu_mask, allowed_mask); if (!cpus_empty(tmp)) { irq_desc_t *desc = irq_desc + selected_irq; unsigned long flags; Dprintk("irq = %d moved to cpu = %d\n", selected_irq, min_loaded); /* mark for change destination */ spin_lock_irqsave(&desc->lock, flags); pending_irq_balance_cpumask[selected_irq] = cpumask_of_cpu(min_loaded); spin_unlock_irqrestore(&desc->lock, flags); /* Since we made a change, come back sooner to * check for more variation. */ balanced_irq_interval = max((long)MIN_BALANCED_IRQ_INTERVAL, balanced_irq_interval - BALANCED_IRQ_LESS_DELTA); return; } goto tryanotherirq;not_worth_the_effort: /* * if we did not find an IRQ to move, then adjust the time interval * upward */ balanced_irq_interval = min((long)MAX_BALANCED_IRQ_INTERVAL, balanced_irq_interval + BALANCED_IRQ_MORE_DELTA); Dprintk("IRQ worth rotating not found\n"); return;}static int balanced_irq(void *unused){ int i; unsigned long prev_balance_time = jiffies; long time_remaining = balanced_irq_interval; daemonize("kirqd"); /* push everything to CPU 0 to give us a starting point. */ for (i = 0 ; i < NR_IRQS ; i++) { pending_irq_balance_cpumask[i] = cpumask_of_cpu(0); } for ( ; ; ) { set_current_state(TASK_INTERRUPTIBLE); time_remaining = schedule_timeout(time_remaining); if (time_after(jiffies, prev_balance_time+balanced_irq_interval)) { do_irq_balance(); prev_balance_time = jiffies; time_remaining = balanced_irq_interval; } } return 0;}static int __init balanced_irq_init(void){ int i; struct cpuinfo_x86 *c; cpumask_t tmp; cpus_shift_right(tmp, cpu_online_map, 2); c = &boot_cpu_data; /* When not overwritten by the command line ask subarchitecture. */ if (irqbalance_disabled == IRQBALANCE_CHECK_ARCH) irqbalance_disabled = NO_BALANCE_IRQ; if (irqbalance_disabled) return 0; /* disable irqbalance completely if there is only one processor online */ if (num_online_cpus() < 2) { irqbalance_disabled = 1; return 0; } /* * Enable physical balance only if more than 1 physical processor * is present */ if (smp_num_siblings > 1 && !cpus_empty(tmp)) physical_balance = 1; for (i = 0; i < NR_CPUS; i++) { if (!cpu_online(i)) continue; irq_cpu_data[i].irq_delta = kmalloc(sizeof(unsigned long) * NR_IRQS, GFP_KERNEL); irq_cpu_data[i].last_irq = kmalloc(sizeof(unsigned long) * NR_IRQS, GFP_KERNEL); if (irq_cpu_data[i].irq_delta == NULL || irq_cpu_data[i].last_irq == NULL) { printk(KERN_ERR "balanced_irq_init: out of memory"); goto failed; } memset(irq_cpu_data[i].irq_delta,0,sizeof(unsigned long) * NR_IRQS); memset(irq_cpu_data[i].last_irq,0,sizeof(unsigned long) * NR_IRQS); } printk(KERN_INFO "Starting balanced_irq\n"); if (kernel_thread(balanced_irq, NULL, CLONE_KERNEL) >= 0) return 0; else printk(KERN_ERR "balanced_irq_init: failed to spawn balanced_irq");failed: for (i = 0; i < NR_CPUS; i++) { if(irq_cpu_data[i].irq_delta) kfree(irq_cpu_data[i].irq_delta); if(irq_cpu_data[i].last_irq) kfree(irq_cpu_data[i].last_irq); } return 0;}static int __init irqbalance_disable(char *str){ irqbalance_disabled = 1; return 0;}__setup("noirqbalance", irqbalance_disable);static inline void move_irq(int irq){ /* note - we hold the desc->lock */ if (unlikely(!cpus_empty(pending_irq_balance_cpumask[irq]))) { set_ioapic_affinity_irq(irq, pending_irq_balance_cpumask[irq]); cpus_clear(pending_irq_balance_cpumask[irq]); }}__initcall(balanced_irq_init);#else /* !CONFIG_IRQBALANCE */static inline void move_irq(int irq) { }#endif /* CONFIG_IRQBALANCE */#ifndef CONFIG_SMPvoid fastcall send_IPI_self(int vector){ unsigned int cfg; /* * Wait for idle. */ apic_wait_icr_idle(); cfg = APIC_DM_FIXED | APIC_DEST_SELF | vector | APIC_DEST_LOGICAL; /* * Send the IPI. The write to APIC_ICR fires this off. */ apic_write_around(APIC_ICR, cfg);}#endif /* !CONFIG_SMP *//* * support for broken MP BIOSs, enables hand-redirection of PIRQ0-7 to * specific CPU-side IRQs. */#define MAX_PIRQS 8int pirq_entries [MAX_PIRQS];int pirqs_enabled;int skip_ioapic_setup;static int __init ioapic_setup(char *str){ skip_ioapic_setup = 1; return 1;}__setup("noapic", ioapic_setup);static int __init ioapic_pirq_setup(char *str){ int i, max; int ints[MAX_PIRQS+1]; get_options(str, ARRAY_SIZE(ints), ints); for (i = 0; i < MAX_PIRQS; i++) pirq_entries[i] = -1; pirqs_enabled = 1; printk(KERN_INFO "PIRQ redirection, working around broken MP-BIOS.\n"); max = MAX_PIRQS; if (ints[0] < MAX_PIRQS) max = ints[0]; for (i = 0; i < max; i++) { printk(KERN_DEBUG "... PIRQ%d -> IRQ %d\n", i, ints[i+1]); /* * PIRQs are mapped upside down, usually. */ pirq_entries[MAX_PIRQS-i-1] = ints[i+1]; } return 1;}__setup("pirq=", ioapic_pirq_setup);/* * Find the IRQ entry number of a certain pin. */static int __init find_irq_entry(int apic, int pin, int type){ int i; for (i = 0; i < mp_irq_entries; i++) if (mp_irqs[i].mpc_irqtype == type && (mp_irqs[i].mpc_dstapic == mp_ioapics[apic].mpc_apicid || mp_irqs[i].mpc_dstapic == MP_APIC_ALL) && mp_irqs[i].mpc_dstirq == pin) return i; return -1;}/* * Find the pin to which IRQ[irq] (ISA) is connected */static int __init find_isa_irq_pin(int irq, int type){ int i; for (i = 0; i < mp_irq_entries; i++) { int lbus = mp_irqs[i].mpc_srcbus; if ((mp_bus_id_to_type[lbus] == MP_BUS_ISA || mp_bus_id_to_type[lbus] == MP_BUS_EISA || mp_bus_id_to_type[lbus] == MP_BUS_MCA || mp_bus_id_to_type[lbus] == MP_BUS_NEC98 ) && (mp_irqs[i].mpc_irqtype == type) && (mp_irqs[i].mpc_srcbusirq == irq)) return mp_irqs[i].mpc_dstirq; } return -1;}/* * Find a specific PCI IRQ entry. * Not an __init, possibly needed by modules */static int pin_2_irq(int idx, int apic, int pin);int IO_APIC_get_PCI_irq_vector(int bus, int slot, int pin){ int apic, i, best_guess = -1; Dprintk("querying PCI -> IRQ mapping bus:%d, slot:%d, pin:%d.\n", bus, slot, pin); if (mp_bus_id_to_pci_bus[bus] == -1) { printk(KERN_WARNING "PCI BIOS passed nonexistent PCI bus %d!\n", bus); return -1; } for (i = 0; i < mp_irq_entries; i++) { int lbus = mp_irqs[i].mpc_srcbus; for (apic = 0; apic < nr_ioapics; apic++) if (mp_ioapics[apic].mpc_apicid == mp_irqs[i].mpc_dstapic || mp_irqs[i].mpc_dstapic == MP_APIC_ALL) break; if ((mp_bus_id_to_type[lbus] == MP_BUS_PCI) && !mp_irqs[i].mpc_irqtype && (bus == lbus) && (slot == ((mp_irqs[i].mpc_srcbusirq >> 2) & 0x1f))) { int irq = pin_2_irq(i,apic,mp_irqs[i].mpc_dstirq); if (!(apic || IO_APIC_IRQ(irq))) continue; if (pin == (mp_irqs[i].mpc_srcbusirq & 3)) return irq; /* * Use the first all-but-pin matching entry as a * best-guess fuzzy result for broken mptables. */ if (best_guess < 0) best_guess = irq; } } return best_guess;}/* * This function currently is only a helper for the i386 smp boot process where * we need to reprogram the ioredtbls to cater for the cpus which have come online * so mask in all cases should simply be TARGET_CPUS */void __init setup_ioapic_dest(void){ int pin, ioapic, irq, irq_entry; if (skip_ioapic_setup == 1) return; for (ioapic = 0; ioapic < nr_ioapics; ioapic++) { for (pin = 0; pin < nr_ioapic_registers[ioapic]; pin++) { irq_entry = find_irq_entry(ioapic, pin, mp_INT); if (irq_entry == -1) continue; irq = pin_2_irq(irq_entry, ioapic, pin); set_ioapic_affinity_irq(irq, TARGET_CPUS); } }}/* * EISA Edge/Level control register, ELCR */static int __init EISA_ELCR(unsigned int irq){ if (irq < 16) { unsigned int port = 0x4d0 + (irq >> 3); return (inb(port) >> (irq & 7)) & 1; } printk(KERN_INFO "Broken MPtable reports ISA irq %d\n", irq); return 0;}/* EISA interrupts are always polarity zero and can be edge or level * trigger depending on the ELCR value. If an interrupt is listed as * EISA conforming in the MP table, that means its trigger type must * be read in from the ELCR */#define default_EISA_trigger(idx) (EISA_ELCR(mp_irqs[idx].mpc_srcbusirq))#define default_EISA_polarity(idx) (0)/* ISA interrupts are always polarity zero edge triggered, * when listed as conforming in the MP table. */#define default_ISA_trigger(idx) (0)#define default_ISA_polarity(idx) (0)/* PCI interrupts are always polarity one level triggered, * when listed as conforming in the MP table. */#define default_PCI_trigger(idx) (1)#define default_PCI_polarity(idx) (1)/* MCA interrupts are always polarity zero level triggered, * when listed as conforming in the MP table. */#define default_MCA_trigger(idx) (1)#define default_MCA_polarity(idx) (0)/* NEC98 interrupts are always polarity zero edge triggered, * when listed as conforming in the MP table. */#define default_NEC98_trigger(idx) (0)#define default_NEC98_polarity(idx) (0)static int __init MPBIOS_polarity(int idx){ int bus = mp_irqs[idx].mpc_srcbus; int polarity; /* * Determine IRQ line polarity (high active or low active): */ switch (mp_irqs[idx].mpc_irqflag & 3) { case 0: /* conforms, ie. bus-type dependent polarity */ { switch (mp_bus_id_to_type[bus]) { case MP_BUS_ISA: /* ISA pin */ { polarity = default_ISA_polarity(idx); break; } case MP_BUS_EISA: /* EISA pin */ { polarity = default_EISA_polarity(idx); break; } case MP_BUS_PCI: /* PCI pin */ { polarity = default_PCI_polarity(idx); break; } case MP_BUS_MCA: /* MCA pin */ { polarity = default_MCA_polarity(idx); break; } case MP_BUS_NEC98: /* NEC 98 pin */ { polarity = default_NEC98_polarity(idx); break; } default: { printk(KERN_WARNING "broken BIOS!!\n"); polarity = 1; break; } } break; } case 1: /* high active */ { polarity = 0; break; } case 2: /* reserved */ { printk(KERN_WARNING "broken BIOS!!\n"); polarity = 1; break; } case 3: /* low active */ { polarity = 1; break; } default: /* invalid */ { printk(KERN_WARNING "broken BIOS!!\n"); polarity = 1; break; } } return polarity;}static int __init MPBIOS_trigger(int idx){ int bus = mp_irqs[idx].mpc_srcbus; int trigger; /* * Determine IRQ trigger mode (edge or level sensitive): */ switch ((mp_irqs[idx].mpc_irqflag>>2) & 3) { case 0: /* conforms, ie. bus-type dependent */ { switch (mp_bus_id_to_type[bus]) { case MP_BUS_ISA: /* ISA pin */ { trigger = default_ISA_trigger(idx); break; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -