apic.c
来自「LINUX 2.6.17.4的源码」· C语言 代码 · 共 1,365 行 · 第 1/3 页
C
1,365 行
/* * The timer chip is already set up at HZ interrupts per second here, * but we do not accept timer interrupts yet. We only allow the BP * to calibrate. */static unsigned int __devinit get_8254_timer_count(void){ unsigned long flags; unsigned int count; spin_lock_irqsave(&i8253_lock, flags); outb_p(0x00, PIT_MODE); count = inb_p(PIT_CH0); count |= inb_p(PIT_CH0) << 8; spin_unlock_irqrestore(&i8253_lock, flags); return count;}/* next tick in 8254 can be caught by catching timer wraparound */static void __devinit wait_8254_wraparound(void){ unsigned int curr_count, prev_count; curr_count = get_8254_timer_count(); do { prev_count = curr_count; curr_count = get_8254_timer_count(); /* workaround for broken Mercury/Neptune */ if (prev_count >= curr_count + 0x100) curr_count = get_8254_timer_count(); } while (prev_count >= curr_count);}/* * Default initialization for 8254 timers. If we use other timers like HPET, * we override this later */void (*wait_timer_tick)(void) __devinitdata = wait_8254_wraparound;/* * This function sets up the local APIC timer, with a timeout of * 'clocks' APIC bus clock. During calibration we actually call * this function twice on the boot CPU, once with a bogus timeout * value, second time for real. The other (noncalibrating) CPUs * call this function only once, with the real, calibrated value. * * We do reads before writes even if unnecessary, to get around the * P5 APIC double write bug. */#define APIC_DIVISOR 16static void __setup_APIC_LVTT(unsigned int clocks){ unsigned int lvtt_value, tmp_value, ver; int cpu = smp_processor_id(); ver = GET_APIC_VERSION(apic_read(APIC_LVR)); lvtt_value = APIC_LVT_TIMER_PERIODIC | LOCAL_TIMER_VECTOR; if (!APIC_INTEGRATED(ver)) lvtt_value |= SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV); if (cpu_isset(cpu, timer_bcast_ipi)) lvtt_value |= APIC_LVT_MASKED; apic_write_around(APIC_LVTT, lvtt_value); /* * Divide PICLK by 16 */ tmp_value = apic_read(APIC_TDCR); apic_write_around(APIC_TDCR, (tmp_value & ~(APIC_TDR_DIV_1 | APIC_TDR_DIV_TMBASE)) | APIC_TDR_DIV_16); apic_write_around(APIC_TMICT, clocks/APIC_DIVISOR);}static void __devinit setup_APIC_timer(unsigned int clocks){ unsigned long flags; local_irq_save(flags); /* * Wait for IRQ0's slice: */ wait_timer_tick(); __setup_APIC_LVTT(clocks); local_irq_restore(flags);}/* * In this function we calibrate APIC bus clocks to the external * timer. Unfortunately we cannot use jiffies and the timer irq * to calibrate, since some later bootup code depends on getting * the first irq? Ugh. * * We want to do the calibration only once since we * want to have local timer irqs syncron. CPUs connected * by the same APIC bus have the very same bus frequency. * And we want to have irqs off anyways, no accidental * APIC irq that way. */static int __init calibrate_APIC_clock(void){ unsigned long long t1 = 0, t2 = 0; long tt1, tt2; long result; int i; const int LOOPS = HZ/10; apic_printk(APIC_VERBOSE, "calibrating APIC timer ...\n"); /* * Put whatever arbitrary (but long enough) timeout * value into the APIC clock, we just want to get the * counter running for calibration. */ __setup_APIC_LVTT(1000000000); /* * The timer chip counts down to zero. Let's wait * for a wraparound to start exact measurement: * (the current tick might have been already half done) */ wait_timer_tick(); /* * We wrapped around just now. Let's start: */ if (cpu_has_tsc) rdtscll(t1); tt1 = apic_read(APIC_TMCCT); /* * Let's wait LOOPS wraprounds: */ for (i = 0; i < LOOPS; i++) wait_timer_tick(); tt2 = apic_read(APIC_TMCCT); if (cpu_has_tsc) rdtscll(t2); /* * The APIC bus clock counter is 32 bits only, it * might have overflown, but note that we use signed * longs, thus no extra care needed. * * underflown to be exact, as the timer counts down ;) */ result = (tt1-tt2)*APIC_DIVISOR/LOOPS; if (cpu_has_tsc) apic_printk(APIC_VERBOSE, "..... CPU clock speed is " "%ld.%04ld MHz.\n", ((long)(t2-t1)/LOOPS)/(1000000/HZ), ((long)(t2-t1)/LOOPS)%(1000000/HZ)); apic_printk(APIC_VERBOSE, "..... host bus clock speed is " "%ld.%04ld MHz.\n", result/(1000000/HZ), result%(1000000/HZ)); return result;}static unsigned int calibration_result;void __init setup_boot_APIC_clock(void){ unsigned long flags; apic_printk(APIC_VERBOSE, "Using local APIC timer interrupts.\n"); using_apic_timer = 1; local_irq_save(flags); calibration_result = calibrate_APIC_clock(); /* * Now set up the timer for real. */ setup_APIC_timer(calibration_result); local_irq_restore(flags);}void __devinit setup_secondary_APIC_clock(void){ setup_APIC_timer(calibration_result);}void disable_APIC_timer(void){ if (using_apic_timer) { unsigned long v; v = apic_read(APIC_LVTT); apic_write_around(APIC_LVTT, v | APIC_LVT_MASKED); }}void enable_APIC_timer(void){ int cpu = smp_processor_id(); if (using_apic_timer && !cpu_isset(cpu, timer_bcast_ipi)) { unsigned long v; v = apic_read(APIC_LVTT); apic_write_around(APIC_LVTT, v & ~APIC_LVT_MASKED); }}void switch_APIC_timer_to_ipi(void *cpumask){ cpumask_t mask = *(cpumask_t *)cpumask; int cpu = smp_processor_id(); if (cpu_isset(cpu, mask) && !cpu_isset(cpu, timer_bcast_ipi)) { disable_APIC_timer(); cpu_set(cpu, timer_bcast_ipi); }}EXPORT_SYMBOL(switch_APIC_timer_to_ipi);void switch_ipi_to_APIC_timer(void *cpumask){ cpumask_t mask = *(cpumask_t *)cpumask; int cpu = smp_processor_id(); if (cpu_isset(cpu, mask) && cpu_isset(cpu, timer_bcast_ipi)) { cpu_clear(cpu, timer_bcast_ipi); enable_APIC_timer(); }}EXPORT_SYMBOL(switch_ipi_to_APIC_timer);#undef APIC_DIVISOR/* * Local timer interrupt handler. It does both profiling and * process statistics/rescheduling. * * We do profiling in every local tick, statistics/rescheduling * happen only every 'profiling multiplier' ticks. The default * multiplier is 1 and it can be changed by writing the new multiplier * value into /proc/profile. */inline void smp_local_timer_interrupt(struct pt_regs * regs){ profile_tick(CPU_PROFILING, regs);#ifdef CONFIG_SMP update_process_times(user_mode_vm(regs));#endif /* * We take the 'long' return path, and there every subsystem * grabs the apropriate locks (kernel lock/ irq lock). * * we might want to decouple profiling from the 'long path', * and do the profiling totally in assembly. * * Currently this isn't too much of an issue (performance wise), * we can take more than 100K local irqs per second on a 100 MHz P5. */}/* * Local APIC timer interrupt. This is the most natural way for doing * local interrupts, but local timer interrupts can be emulated by * broadcast interrupts too. [in case the hw doesn't support APIC timers] * * [ if a single-CPU system runs an SMP kernel then we call the local * interrupt as well. Thus we cannot inline the local irq ... ] */fastcall void smp_apic_timer_interrupt(struct pt_regs *regs){ int cpu = smp_processor_id(); /* * the NMI deadlock-detector uses this. */ per_cpu(irq_stat, cpu).apic_timer_irqs++; /* * NOTE! We'd better ACK the irq immediately, * because timer handling can be slow. */ ack_APIC_irq(); /* * update_process_times() expects us to have done irq_enter(). * Besides, if we don't timer interrupts ignore the global * interrupt lock, which is the WrongThing (tm) to do. */ irq_enter(); smp_local_timer_interrupt(regs); irq_exit();}#ifndef CONFIG_SMPstatic void up_apic_timer_interrupt_call(struct pt_regs *regs){ int cpu = smp_processor_id(); /* * the NMI deadlock-detector uses this. */ per_cpu(irq_stat, cpu).apic_timer_irqs++; smp_local_timer_interrupt(regs);}#endifvoid smp_send_timer_broadcast_ipi(struct pt_regs *regs){ cpumask_t mask; cpus_and(mask, cpu_online_map, timer_bcast_ipi); if (!cpus_empty(mask)) {#ifdef CONFIG_SMP send_IPI_mask(mask, LOCAL_TIMER_VECTOR);#else /* * We can directly call the apic timer interrupt handler * in UP case. Minus all irq related functions */ up_apic_timer_interrupt_call(regs);#endif }}int setup_profiling_timer(unsigned int multiplier){ return -EINVAL;}/* * This interrupt should _never_ happen with our APIC/SMP architecture */fastcall void smp_spurious_interrupt(struct pt_regs *regs){ unsigned long v; irq_enter(); /* * Check if this really is a spurious interrupt and ACK it * if it is a vectored one. Just in case... * Spurious interrupts should not be ACKed. */ v = apic_read(APIC_ISR + ((SPURIOUS_APIC_VECTOR & ~0x1f) >> 1)); if (v & (1 << (SPURIOUS_APIC_VECTOR & 0x1f))) ack_APIC_irq(); /* see sw-dev-man vol 3, chapter 7.4.13.5 */ printk(KERN_INFO "spurious APIC interrupt on CPU#%d, should never happen.\n", smp_processor_id()); irq_exit();}/* * This interrupt should never happen with our APIC/SMP architecture */fastcall void smp_error_interrupt(struct pt_regs *regs){ unsigned long v, v1; irq_enter(); /* First tickle the hardware, only then report what went on. -- REW */ v = apic_read(APIC_ESR); apic_write(APIC_ESR, 0); v1 = apic_read(APIC_ESR); ack_APIC_irq(); atomic_inc(&irq_err_count); /* Here is what the APIC error bits mean: 0: Send CS error 1: Receive CS error 2: Send accept error 3: Receive accept error 4: Reserved 5: Send illegal vector 6: Received illegal vector 7: Illegal register address */ printk (KERN_DEBUG "APIC error on CPU%d: %02lx(%02lx)\n", smp_processor_id(), v , v1); irq_exit();}/* * This initializes the IO-APIC and APIC hardware if this is * a UP kernel. */int __init APIC_init_uniprocessor (void){ if (enable_local_apic < 0) clear_bit(X86_FEATURE_APIC, boot_cpu_data.x86_capability); if (!smp_found_config && !cpu_has_apic) return -1; /* * Complain if the BIOS pretends there is one. */ if (!cpu_has_apic && APIC_INTEGRATED(apic_version[boot_cpu_physical_apicid])) { printk(KERN_ERR "BIOS bug, local APIC #%d not detected!...\n", boot_cpu_physical_apicid); clear_bit(X86_FEATURE_APIC, boot_cpu_data.x86_capability); return -1; } verify_local_APIC(); connect_bsp_APIC(); /* * Hack: In case of kdump, after a crash, kernel might be booting * on a cpu with non-zero lapic id. But boot_cpu_physical_apicid * might be zero if read from MP tables. Get it from LAPIC. */#ifdef CONFIG_CRASH_DUMP boot_cpu_physical_apicid = GET_APIC_ID(apic_read(APIC_ID));#endif phys_cpu_present_map = physid_mask_of_physid(boot_cpu_physical_apicid); setup_local_APIC();#ifdef CONFIG_X86_IO_APIC if (smp_found_config) if (!skip_ioapic_setup && nr_ioapics) setup_IO_APIC();#endif setup_boot_APIC_clock(); return 0;}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?