⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 apic_64.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
{	unsigned int lvtt_value, tmp_value;	lvtt_value = LOCAL_TIMER_VECTOR;	if (!oneshot)		lvtt_value |= APIC_LVT_TIMER_PERIODIC;	if (!irqen)		lvtt_value |= APIC_LVT_MASKED;	apic_write(APIC_LVTT, lvtt_value);	/*	 * Divide PICLK by 16	 */	tmp_value = apic_read(APIC_TDCR);	apic_write(APIC_TDCR, (tmp_value				& ~(APIC_TDR_DIV_1 | APIC_TDR_DIV_TMBASE))				| APIC_TDR_DIV_16);	if (!oneshot)		apic_write(APIC_TMICT, clocks);}static void setup_APIC_timer(void){	struct clock_event_device *levt = &__get_cpu_var(lapic_events);	memcpy(levt, &lapic_clockevent, sizeof(*levt));	levt->cpumask = cpumask_of_cpu(smp_processor_id());	clockevents_register_device(levt);}/* * 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. */#define TICK_COUNT 100000000static void __init calibrate_APIC_clock(void){	unsigned apic, apic_start;	unsigned long tsc, tsc_start;	int result;	local_irq_disable();	/*	 * Put whatever arbitrary (but long enough) timeout	 * value into the APIC clock, we just want to get the	 * counter running for calibration.	 *	 * No interrupt enable !	 */	__setup_APIC_LVTT(250000000, 0, 0);	apic_start = apic_read(APIC_TMCCT);#ifdef CONFIG_X86_PM_TIMER	if (apic_calibrate_pmtmr && pmtmr_ioport) {		pmtimer_wait(5000);  /* 5ms wait */		apic = apic_read(APIC_TMCCT);		result = (apic_start - apic) * 1000L / 5;	} else#endif	{		rdtscll(tsc_start);		do {			apic = apic_read(APIC_TMCCT);			rdtscll(tsc);		} while ((tsc - tsc_start) < TICK_COUNT &&				(apic_start - apic) < TICK_COUNT);		result = (apic_start - apic) * 1000L * tsc_khz /					(tsc - tsc_start);	}	local_irq_enable();	printk(KERN_DEBUG "APIC timer calibration result %d\n", result);	printk(KERN_INFO "Detected %d.%03d MHz APIC timer.\n",		result / 1000 / 1000, result / 1000 % 1000);	/* Calculate the scaled math multiplication factor */	lapic_clockevent.mult = div_sc(result, NSEC_PER_SEC, 32);	lapic_clockevent.max_delta_ns =		clockevent_delta2ns(0x7FFFFF, &lapic_clockevent);	lapic_clockevent.min_delta_ns =		clockevent_delta2ns(0xF, &lapic_clockevent);	calibration_result = result / HZ;}void __init setup_boot_APIC_clock (void){	/*	 * The local apic timer can be disabled via the kernel commandline.	 * Register the lapic timer as a dummy clock event source on SMP	 * systems, so the broadcast mechanism is used. On UP systems simply	 * ignore it.	 */	if (disable_apic_timer) {		printk(KERN_INFO "Disabling APIC timer\n");		/* No broadcast on UP ! */		if (num_possible_cpus() > 1)			setup_APIC_timer();		return;	}	printk(KERN_INFO "Using local APIC timer interrupts.\n");	calibrate_APIC_clock();	/*	 * If nmi_watchdog is set to IO_APIC, we need the	 * PIT/HPET going.  Otherwise register lapic as a dummy	 * device.	 */	if (nmi_watchdog != NMI_IO_APIC)		lapic_clockevent.features &= ~CLOCK_EVT_FEAT_DUMMY;	else		printk(KERN_WARNING "APIC timer registered as dummy,"		       " due to nmi_watchdog=1!\n");	setup_APIC_timer();}/* * AMD C1E enabled CPUs have a real nasty problem: Some BIOSes set the * C1E flag only in the secondary CPU, so when we detect the wreckage * we already have enabled the boot CPU local apic timer. Check, if * disable_apic_timer is set and the DUMMY flag is cleared. If yes, * set the DUMMY flag again and force the broadcast mode in the * clockevents layer. */void __cpuinit check_boot_apic_timer_broadcast(void){	if (!disable_apic_timer ||	    (lapic_clockevent.features & CLOCK_EVT_FEAT_DUMMY))		return;	printk(KERN_INFO "AMD C1E detected late. Force timer broadcast.\n");	lapic_clockevent.features |= CLOCK_EVT_FEAT_DUMMY;	local_irq_enable();	clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_FORCE, &boot_cpu_id);	local_irq_disable();}void __cpuinit setup_secondary_APIC_clock(void){	check_boot_apic_timer_broadcast();	setup_APIC_timer();}int setup_profiling_timer(unsigned int multiplier){	return -EINVAL;}void setup_APIC_extended_lvt(unsigned char lvt_off, unsigned char vector,			     unsigned char msg_type, unsigned char mask){	unsigned long reg = (lvt_off << 4) + K8_APIC_EXT_LVT_BASE;	unsigned int  v   = (mask << 16) | (msg_type << 8) | vector;	apic_write(reg, v);}/* * 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. */void smp_local_timer_interrupt(void){	int cpu = smp_processor_id();	struct clock_event_device *evt = &per_cpu(lapic_events, cpu);	/*	 * Normally we should not be here till LAPIC has been initialized but	 * in some cases like kdump, its possible that there is a pending LAPIC	 * timer interrupt from previous kernel's context and is delivered in	 * new kernel the moment interrupts are enabled.	 *	 * Interrupts are enabled early and LAPIC is setup much later, hence	 * its possible that when we get here evt->event_handler is NULL.	 * Check for event_handler being NULL and discard the interrupt as	 * spurious.	 */	if (!evt->event_handler) {		printk(KERN_WARNING		       "Spurious LAPIC timer interrupt on cpu %d\n", cpu);		/* Switch it off */		lapic_timer_setup(CLOCK_EVT_MODE_SHUTDOWN, evt);		return;	}	/*	 * the NMI deadlock-detector uses this.	 */	add_pda(apic_timer_irqs, 1);	evt->event_handler(evt);}/* * 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 ... ] */void smp_apic_timer_interrupt(struct pt_regs *regs){	struct pt_regs *old_regs = set_irq_regs(regs);	/*	 * 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.	 */	exit_idle();	irq_enter();	smp_local_timer_interrupt();	irq_exit();	set_irq_regs(old_regs);}/* * apic_is_clustered_box() -- Check if we can expect good TSC * * Thus far, the major user of this is IBM's Summit2 series: * * Clustered boxes may have unsynced TSC problems if they are * multi-chassis. Use available data to take a good guess. * If in doubt, go HPET. */__cpuinit int apic_is_clustered_box(void){	int i, clusters, zeros;	unsigned id;	DECLARE_BITMAP(clustermap, NUM_APIC_CLUSTERS);	bitmap_zero(clustermap, NUM_APIC_CLUSTERS);	for (i = 0; i < NR_CPUS; i++) {		id = bios_cpu_apicid[i];		if (id != BAD_APICID)			__set_bit(APIC_CLUSTERID(id), clustermap);	}	/* Problem:  Partially populated chassis may not have CPUs in some of	 * the APIC clusters they have been allocated.  Only present CPUs have	 * bios_cpu_apicid entries, thus causing zeroes in the bitmap.  Since	 * clusters are allocated sequentially, count zeros only if they are	 * bounded by ones.	 */	clusters = 0;	zeros = 0;	for (i = 0; i < NUM_APIC_CLUSTERS; i++) {		if (test_bit(i, clustermap)) {			clusters += 1 + zeros;			zeros = 0;		} else			++zeros;	}	/*	 * If clusters > 2, then should be multi-chassis.	 * May have to revisit this when multi-core + hyperthreaded CPUs come	 * out, but AFAIK this will work even for them.	 */	return (clusters > 2);}/* * This interrupt should _never_ happen with our APIC/SMP architecture */asmlinkage void smp_spurious_interrupt(void){	unsigned int v;	exit_idle();	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();	add_pda(irq_spurious_count, 1);	irq_exit();}/* * This interrupt should never happen with our APIC/SMP architecture */asmlinkage void smp_error_interrupt(void){	unsigned int v, v1;	exit_idle();	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: %02x(%02x)\n",		smp_processor_id(), v , v1);	irq_exit();}int disable_apic;/* * This initializes the IO-APIC and APIC hardware if this is * a UP kernel. */int __init APIC_init_uniprocessor (void){	if (disable_apic) {		printk(KERN_INFO "Apic disabled\n");		return -1;	}	if (!cpu_has_apic) {		disable_apic = 1;		printk(KERN_INFO "Apic disabled by BIOS\n");		return -1;	}	verify_local_APIC();	phys_cpu_present_map = physid_mask_of_physid(boot_cpu_id);	apic_write(APIC_ID, SET_APIC_ID(boot_cpu_id));	setup_local_APIC();	if (smp_found_config && !skip_ioapic_setup && nr_ioapics)		setup_IO_APIC();	else		nr_ioapics = 0;	setup_boot_APIC_clock();	check_nmi_watchdog();	return 0;}static __init int setup_disableapic(char *str){	disable_apic = 1;	clear_bit(X86_FEATURE_APIC, boot_cpu_data.x86_capability);	return 0;}early_param("disableapic", setup_disableapic);/* same as disableapic, for compatibility */static __init int setup_nolapic(char *str){	return setup_disableapic(str);}early_param("nolapic", setup_nolapic);static int __init parse_lapic_timer_c2_ok(char *arg){	local_apic_timer_c2_ok = 1;	return 0;}early_param("lapic_timer_c2_ok", parse_lapic_timer_c2_ok);static __init int setup_noapictimer(char *str){	if (str[0] != ' ' && str[0] != 0)		return 0;	disable_apic_timer = 1;	return 1;}__setup("noapictimer", setup_noapictimer);static __init int setup_apicpmtimer(char *s){	apic_calibrate_pmtmr = 1;	notsc_setup(NULL);	return 0;}__setup("apicpmtimer", setup_apicpmtimer);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -