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

📄 timer_tsc.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * This code largely moved from arch/i386/kernel/time.c. * See comments there for proper credits. * * 2004-06-25    Jesper Juhl *      moved mark_offset_tsc below cpufreq_delayed_get to avoid gcc 3.4 *      failing to inline. */#include <linux/spinlock.h>#include <linux/init.h>#include <linux/timex.h>#include <linux/errno.h>#include <linux/cpufreq.h>#include <linux/string.h>#include <linux/jiffies.h>#include <asm/timer.h>#include <asm/io.h>/* processor.h for distable_tsc flag */#include <asm/processor.h>#include "io_ports.h"#include "mach_timer.h"#include <asm/hpet.h>#include <asm/i8253.h>#ifdef CONFIG_HPET_TIMERstatic unsigned long hpet_usec_quotient;static unsigned long hpet_last;static struct timer_opts timer_tsc;#endifstatic inline void cpufreq_delayed_get(void);int tsc_disable __devinitdata = 0;static int use_tsc;/* Number of usecs that the last interrupt was delayed */static int delay_at_last_interrupt;static unsigned long last_tsc_low; /* lsb 32 bits of Time Stamp Counter */static unsigned long last_tsc_high; /* msb 32 bits of Time Stamp Counter */static unsigned long long monotonic_base;static seqlock_t monotonic_lock = SEQLOCK_UNLOCKED;/* convert from cycles(64bits) => nanoseconds (64bits) *  basic equation: *		ns = cycles / (freq / ns_per_sec) *		ns = cycles * (ns_per_sec / freq) *		ns = cycles * (10^9 / (cpu_khz * 10^3)) *		ns = cycles * (10^6 / cpu_khz) * *	Then we use scaling math (suggested by george@mvista.com) to get: *		ns = cycles * (10^6 * SC / cpu_khz) / SC *		ns = cycles * cyc2ns_scale / SC * *	And since SC is a constant power of two, we can convert the div *  into a shift. * *  We can use khz divisor instead of mhz to keep a better percision, since *  cyc2ns_scale is limited to 10^6 * 2^10, which fits in 32 bits. *  (mathieu.desnoyers@polymtl.ca) * *			-johnstul@us.ibm.com "math is hard, lets go shopping!" */static unsigned long cyc2ns_scale; #define CYC2NS_SCALE_FACTOR 10 /* 2^10, carefully chosen */static inline void set_cyc2ns_scale(unsigned long cpu_khz){	cyc2ns_scale = (1000000 << CYC2NS_SCALE_FACTOR)/cpu_khz;}static inline unsigned long long cycles_2_ns(unsigned long long cyc){	return (cyc * cyc2ns_scale) >> CYC2NS_SCALE_FACTOR;}static int count2; /* counter for mark_offset_tsc() *//* Cached *multiplier* to convert TSC counts to microseconds. * (see the equation below). * Equal to 2^32 * (1 / (clocks per usec) ). * Initialized in time_init. */static unsigned long fast_gettimeoffset_quotient;static unsigned long get_offset_tsc(void){	register unsigned long eax, edx;	/* Read the Time Stamp Counter */	rdtsc(eax,edx);	/* .. relative to previous jiffy (32 bits is enough) */	eax -= last_tsc_low;	/* tsc_low delta */	/*         * Time offset = (tsc_low delta) * fast_gettimeoffset_quotient         *             = (tsc_low delta) * (usecs_per_clock)         *             = (tsc_low delta) * (usecs_per_jiffy / clocks_per_jiffy)	 *	 * Using a mull instead of a divl saves up to 31 clock cycles	 * in the critical path.         */	__asm__("mull %2"		:"=a" (eax), "=d" (edx)		:"rm" (fast_gettimeoffset_quotient),		 "0" (eax));	/* our adjusted time offset in microseconds */	return delay_at_last_interrupt + edx;}static unsigned long long monotonic_clock_tsc(void){	unsigned long long last_offset, this_offset, base;	unsigned seq;		/* atomically read monotonic base & last_offset */	do {		seq = read_seqbegin(&monotonic_lock);		last_offset = ((unsigned long long)last_tsc_high<<32)|last_tsc_low;		base = monotonic_base;	} while (read_seqretry(&monotonic_lock, seq));	/* Read the Time Stamp Counter */	rdtscll(this_offset);	/* return the value in ns */	return base + cycles_2_ns(this_offset - last_offset);}/* * Scheduler clock - returns current time in nanosec units. */unsigned long long sched_clock(void){	unsigned long long this_offset;	/*	 * In the NUMA case we dont use the TSC as they are not	 * synchronized across all CPUs.	 */#ifndef CONFIG_NUMA	if (!use_tsc)#endif		/* no locking but a rare wrong value is not a big deal */		return jiffies_64 * (1000000000 / HZ);	/* Read the Time Stamp Counter */	rdtscll(this_offset);	/* return the value in ns */	return cycles_2_ns(this_offset);}static void delay_tsc(unsigned long loops){	unsigned long bclock, now;		rdtscl(bclock);	do	{		rep_nop();		rdtscl(now);	} while ((now-bclock) < loops);}#ifdef CONFIG_HPET_TIMERstatic void mark_offset_tsc_hpet(void){	unsigned long long this_offset, last_offset; 	unsigned long offset, temp, hpet_current;	write_seqlock(&monotonic_lock);	last_offset = ((unsigned long long)last_tsc_high<<32)|last_tsc_low;	/*	 * It is important that these two operations happen almost at	 * the same time. We do the RDTSC stuff first, since it's	 * faster. To avoid any inconsistencies, we need interrupts	 * disabled locally.	 */	/*	 * Interrupts are just disabled locally since the timer irq	 * has the SA_INTERRUPT flag set. -arca	 */	/* read Pentium cycle counter */	hpet_current = hpet_readl(HPET_COUNTER);	rdtsc(last_tsc_low, last_tsc_high);	/* lost tick compensation */	offset = hpet_readl(HPET_T0_CMP) - hpet_tick;	if (unlikely(((offset - hpet_last) > hpet_tick) && (hpet_last != 0))) {		int lost_ticks = (offset - hpet_last) / hpet_tick;		jiffies_64 += lost_ticks;	}	hpet_last = hpet_current;	/* update the monotonic base value */	this_offset = ((unsigned long long)last_tsc_high<<32)|last_tsc_low;	monotonic_base += cycles_2_ns(this_offset - last_offset);	write_sequnlock(&monotonic_lock);	/* calculate delay_at_last_interrupt */	/*	 * Time offset = (hpet delta) * ( usecs per HPET clock )	 *             = (hpet delta) * ( usecs per tick / HPET clocks per tick)	 *             = (hpet delta) * ( hpet_usec_quotient ) / (2^32)	 * Where,	 * hpet_usec_quotient = (2^32 * usecs per tick)/HPET clocks per tick	 */	delay_at_last_interrupt = hpet_current - offset;	ASM_MUL64_REG(temp, delay_at_last_interrupt,			hpet_usec_quotient, delay_at_last_interrupt);}#endif#ifdef CONFIG_CPU_FREQ#include <linux/workqueue.h>static unsigned int cpufreq_delayed_issched = 0;static unsigned int cpufreq_init = 0;static struct work_struct cpufreq_delayed_get_work;static void handle_cpufreq_delayed_get(void *v){	unsigned int cpu;	for_each_online_cpu(cpu) {		cpufreq_get(cpu);	}	cpufreq_delayed_issched = 0;}/* if we notice lost ticks, schedule a call to cpufreq_get() as it tries * to verify the CPU frequency the timing core thinks the CPU is running * at is still correct. */static inline void cpufreq_delayed_get(void) {	if (cpufreq_init && !cpufreq_delayed_issched) {		cpufreq_delayed_issched = 1;		printk(KERN_DEBUG "Losing some ticks... checking if CPU frequency changed.\n");		schedule_work(&cpufreq_delayed_get_work);	}}/* If the CPU frequency is scaled, TSC-based delays will need a different * loops_per_jiffy value to function properly. */static unsigned int  ref_freq = 0;static unsigned long loops_per_jiffy_ref = 0;#ifndef CONFIG_SMPstatic unsigned long fast_gettimeoffset_ref = 0;static unsigned int cpu_khz_ref = 0;#endifstatic inttime_cpufreq_notifier(struct notifier_block *nb, unsigned long val,		       void *data){	struct cpufreq_freqs *freq = data;	if (val != CPUFREQ_RESUMECHANGE)		write_seqlock_irq(&xtime_lock);	if (!ref_freq) {		ref_freq = freq->old;		loops_per_jiffy_ref = cpu_data[freq->cpu].loops_per_jiffy;#ifndef CONFIG_SMP		fast_gettimeoffset_ref = fast_gettimeoffset_quotient;		cpu_khz_ref = cpu_khz;#endif	}	if ((val == CPUFREQ_PRECHANGE  && freq->old < freq->new) ||	    (val == CPUFREQ_POSTCHANGE && freq->old > freq->new) ||	    (val == CPUFREQ_RESUMECHANGE)) {		if (!(freq->flags & CPUFREQ_CONST_LOOPS))			cpu_data[freq->cpu].loops_per_jiffy = cpufreq_scale(loops_per_jiffy_ref, ref_freq, freq->new);#ifndef CONFIG_SMP		if (cpu_khz)			cpu_khz = cpufreq_scale(cpu_khz_ref, ref_freq, freq->new);		if (use_tsc) {			if (!(freq->flags & CPUFREQ_CONST_LOOPS)) {				fast_gettimeoffset_quotient = cpufreq_scale(fast_gettimeoffset_ref, freq->new, ref_freq);				set_cyc2ns_scale(cpu_khz);			}		}#endif	}	if (val != CPUFREQ_RESUMECHANGE)		write_sequnlock_irq(&xtime_lock);

⌨️ 快捷键说明

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