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

📄 timer.c

📁 linux 2.6.19 kernel source code before patching
💻 C
📖 第 1 页 / 共 3 页
字号:
/* *  linux/kernel/timer.c * *  Kernel internal timers, basic process system calls * *  Copyright (C) 1991, 1992  Linus Torvalds * *  1997-01-28  Modified by Finn Arne Gangstad to make timers scale better. * *  1997-09-10  Updated NTP code according to technical memorandum Jan '96 *              "A Kernel Model for Precision Timekeeping" by Dave Mills *  1998-12-24  Fixed a xtime SMP race (we need the xtime_lock rw spinlock to *              serialize accesses to xtime/lost_ticks). *                              Copyright (C) 1998  Andrea Arcangeli *  1999-03-10  Improved NTP compatibility by Ulrich Windl *  2002-05-31	Move sys_sysinfo here and make its locking sane, Robert Love *  2000-10-05  Implemented scalable SMP per-CPU timer handling. *                              Copyright (C) 2000, 2001, 2002  Ingo Molnar *              Designed by David S. Miller, Alexey Kuznetsov and Ingo Molnar */#include <linux/kernel_stat.h>#include <linux/module.h>#include <linux/interrupt.h>#include <linux/percpu.h>#include <linux/init.h>#include <linux/mm.h>#include <linux/swap.h>#include <linux/notifier.h>#include <linux/thread_info.h>#include <linux/time.h>#include <linux/jiffies.h>#include <linux/posix-timers.h>#include <linux/cpu.h>#include <linux/syscalls.h>#include <linux/delay.h>#include <linux/tick.h>#include <linux/kallsyms.h>#include <asm/uaccess.h>#include <asm/unistd.h>#include <asm/div64.h>#include <asm/timex.h>#include <asm/io.h>u64 jiffies_64 __cacheline_aligned_in_smp = INITIAL_JIFFIES;EXPORT_SYMBOL(jiffies_64);/* * per-CPU timer vector definitions: */#define TVN_BITS (CONFIG_BASE_SMALL ? 4 : 6)#define TVR_BITS (CONFIG_BASE_SMALL ? 6 : 8)#define TVN_SIZE (1 << TVN_BITS)#define TVR_SIZE (1 << TVR_BITS)#define TVN_MASK (TVN_SIZE - 1)#define TVR_MASK (TVR_SIZE - 1)typedef struct tvec_s {	struct list_head vec[TVN_SIZE];} tvec_t;typedef struct tvec_root_s {	struct list_head vec[TVR_SIZE];} tvec_root_t;struct tvec_t_base_s {	spinlock_t lock;	struct timer_list *running_timer;	unsigned long timer_jiffies;	tvec_root_t tv1;	tvec_t tv2;	tvec_t tv3;	tvec_t tv4;	tvec_t tv5;} ____cacheline_aligned;typedef struct tvec_t_base_s tvec_base_t;tvec_base_t boot_tvec_bases;EXPORT_SYMBOL(boot_tvec_bases);static DEFINE_PER_CPU(tvec_base_t *, tvec_bases) = &boot_tvec_bases;/* * Note that all tvec_bases is 2 byte aligned and lower bit of * base in timer_list is guaranteed to be zero. Use the LSB for * the new flag to indicate whether the timer is deferrable */#define TBASE_DEFERRABLE_FLAG		(0x1)/* Functions below help us manage 'deferrable' flag */static inline unsigned int tbase_get_deferrable(tvec_base_t *base){	return ((unsigned int)(unsigned long)base & TBASE_DEFERRABLE_FLAG);}static inline tvec_base_t *tbase_get_base(tvec_base_t *base){	return ((tvec_base_t *)((unsigned long)base & ~TBASE_DEFERRABLE_FLAG));}static inline void timer_set_deferrable(struct timer_list *timer){	timer->base = ((tvec_base_t *)((unsigned long)(timer->base) |	                               TBASE_DEFERRABLE_FLAG));}static inline voidtimer_set_base(struct timer_list *timer, tvec_base_t *new_base){	timer->base = (tvec_base_t *)((unsigned long)(new_base) |	                              tbase_get_deferrable(timer->base));}/** * __round_jiffies - function to round jiffies to a full second * @j: the time in (absolute) jiffies that should be rounded * @cpu: the processor number on which the timeout will happen * * __round_jiffies() rounds an absolute time in the future (in jiffies) * up or down to (approximately) full seconds. This is useful for timers * for which the exact time they fire does not matter too much, as long as * they fire approximately every X seconds. * * By rounding these timers to whole seconds, all such timers will fire * at the same time, rather than at various times spread out. The goal * of this is to have the CPU wake up less, which saves power. * * The exact rounding is skewed for each processor to avoid all * processors firing at the exact same time, which could lead * to lock contention or spurious cache line bouncing. * * The return value is the rounded version of the @j parameter. */unsigned long __round_jiffies(unsigned long j, int cpu){	int rem;	unsigned long original = j;	/*	 * We don't want all cpus firing their timers at once hitting the	 * same lock or cachelines, so we skew each extra cpu with an extra	 * 3 jiffies. This 3 jiffies came originally from the mm/ code which	 * already did this.	 * The skew is done by adding 3*cpunr, then round, then subtract this	 * extra offset again.	 */	j += cpu * 3;	rem = j % HZ;	/*	 * If the target jiffie is just after a whole second (which can happen	 * due to delays of the timer irq, long irq off times etc etc) then	 * we should round down to the whole second, not up. Use 1/4th second	 * as cutoff for this rounding as an extreme upper bound for this.	 */	if (rem < HZ/4) /* round down */		j = j - rem;	else /* round up */		j = j - rem + HZ;	/* now that we have rounded, subtract the extra skew again */	j -= cpu * 3;	if (j <= jiffies) /* rounding ate our timeout entirely; */		return original;	return j;}EXPORT_SYMBOL_GPL(__round_jiffies);/** * __round_jiffies_relative - function to round jiffies to a full second * @j: the time in (relative) jiffies that should be rounded * @cpu: the processor number on which the timeout will happen * * __round_jiffies_relative() rounds a time delta  in the future (in jiffies) * up or down to (approximately) full seconds. This is useful for timers * for which the exact time they fire does not matter too much, as long as * they fire approximately every X seconds. * * By rounding these timers to whole seconds, all such timers will fire * at the same time, rather than at various times spread out. The goal * of this is to have the CPU wake up less, which saves power. * * The exact rounding is skewed for each processor to avoid all * processors firing at the exact same time, which could lead * to lock contention or spurious cache line bouncing. * * The return value is the rounded version of the @j parameter. */unsigned long __round_jiffies_relative(unsigned long j, int cpu){	/*	 * In theory the following code can skip a jiffy in case jiffies	 * increments right between the addition and the later subtraction.	 * However since the entire point of this function is to use approximate	 * timeouts, it's entirely ok to not handle that.	 */	return  __round_jiffies(j + jiffies, cpu) - jiffies;}EXPORT_SYMBOL_GPL(__round_jiffies_relative);/** * round_jiffies - function to round jiffies to a full second * @j: the time in (absolute) jiffies that should be rounded * * round_jiffies() rounds an absolute time in the future (in jiffies) * up or down to (approximately) full seconds. This is useful for timers * for which the exact time they fire does not matter too much, as long as * they fire approximately every X seconds. * * By rounding these timers to whole seconds, all such timers will fire * at the same time, rather than at various times spread out. The goal * of this is to have the CPU wake up less, which saves power. * * The return value is the rounded version of the @j parameter. */unsigned long round_jiffies(unsigned long j){	return __round_jiffies(j, raw_smp_processor_id());}EXPORT_SYMBOL_GPL(round_jiffies);/** * round_jiffies_relative - function to round jiffies to a full second * @j: the time in (relative) jiffies that should be rounded * * round_jiffies_relative() rounds a time delta  in the future (in jiffies) * up or down to (approximately) full seconds. This is useful for timers * for which the exact time they fire does not matter too much, as long as * they fire approximately every X seconds. * * By rounding these timers to whole seconds, all such timers will fire * at the same time, rather than at various times spread out. The goal * of this is to have the CPU wake up less, which saves power. * * The return value is the rounded version of the @j parameter. */unsigned long round_jiffies_relative(unsigned long j){	return __round_jiffies_relative(j, raw_smp_processor_id());}EXPORT_SYMBOL_GPL(round_jiffies_relative);static inline void set_running_timer(tvec_base_t *base,					struct timer_list *timer){#ifdef CONFIG_SMP	base->running_timer = timer;#endif}static void internal_add_timer(tvec_base_t *base, struct timer_list *timer){	unsigned long expires = timer->expires;	unsigned long idx = expires - base->timer_jiffies;	struct list_head *vec;	if (idx < TVR_SIZE) {		int i = expires & TVR_MASK;		vec = base->tv1.vec + i;	} else if (idx < 1 << (TVR_BITS + TVN_BITS)) {		int i = (expires >> TVR_BITS) & TVN_MASK;		vec = base->tv2.vec + i;	} else if (idx < 1 << (TVR_BITS + 2 * TVN_BITS)) {		int i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK;		vec = base->tv3.vec + i;	} else if (idx < 1 << (TVR_BITS + 3 * TVN_BITS)) {		int i = (expires >> (TVR_BITS + 2 * TVN_BITS)) & TVN_MASK;		vec = base->tv4.vec + i;	} else if ((signed long) idx < 0) {		/*		 * Can happen if you add a timer with expires == jiffies,		 * or you set a timer to go off in the past		 */		vec = base->tv1.vec + (base->timer_jiffies & TVR_MASK);	} else {		int i;		/* If the timeout is larger than 0xffffffff on 64-bit		 * architectures then we use the maximum timeout:		 */		if (idx > 0xffffffffUL) {			idx = 0xffffffffUL;			expires = idx + base->timer_jiffies;		}		i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK;		vec = base->tv5.vec + i;	}	/*	 * Timers are FIFO:	 */	list_add_tail(&timer->entry, vec);}#ifdef CONFIG_TIMER_STATSvoid __timer_stats_timer_set_start_info(struct timer_list *timer, void *addr){	if (timer->start_site)		return;	timer->start_site = addr;	memcpy(timer->start_comm, current->comm, TASK_COMM_LEN);	timer->start_pid = current->pid;}#endif/** * init_timer - initialize a timer. * @timer: the timer to be initialized * * init_timer() must be done to a timer prior calling *any* of the * other timer functions. */void fastcall init_timer(struct timer_list *timer){	timer->entry.next = NULL;	timer->base = __raw_get_cpu_var(tvec_bases);#ifdef CONFIG_TIMER_STATS	timer->start_site = NULL;	timer->start_pid = -1;	memset(timer->start_comm, 0, TASK_COMM_LEN);#endif}EXPORT_SYMBOL(init_timer);void fastcall init_timer_deferrable(struct timer_list *timer){	init_timer(timer);	timer_set_deferrable(timer);}EXPORT_SYMBOL(init_timer_deferrable);static inline void detach_timer(struct timer_list *timer,				int clear_pending){	struct list_head *entry = &timer->entry;	__list_del(entry->prev, entry->next);	if (clear_pending)		entry->next = NULL;	entry->prev = LIST_POISON2;}/* * We are using hashed locking: holding per_cpu(tvec_bases).lock * means that all timers which are tied to this base via timer->base are * locked, and the base itself is locked too. * * So __run_timers/migrate_timers can safely modify all timers which could * be found on ->tvX lists. * * When the timer's base is locked, and the timer removed from list, it is * possible to set timer->base = NULL and drop the lock: the timer remains * locked. */static tvec_base_t *lock_timer_base(struct timer_list *timer,					unsigned long *flags)	__acquires(timer->base->lock){	tvec_base_t *base;	for (;;) {		tvec_base_t *prelock_base = timer->base;		base = tbase_get_base(prelock_base);		if (likely(base != NULL)) {			spin_lock_irqsave(&base->lock, *flags);			if (likely(prelock_base == timer->base))				return base;			/* The timer has migrated to another CPU */			spin_unlock_irqrestore(&base->lock, *flags);		}		cpu_relax();	}}int __mod_timer(struct timer_list *timer, unsigned long expires){	tvec_base_t *base, *new_base;	unsigned long flags;	int ret = 0;	timer_stats_timer_set_start_info(timer);	BUG_ON(!timer->function);	base = lock_timer_base(timer, &flags);	if (timer_pending(timer)) {		detach_timer(timer, 0);		ret = 1;	}	new_base = __get_cpu_var(tvec_bases);	if (base != new_base) {		/*		 * We are trying to schedule the timer on the local CPU.		 * However we can't change timer's base while it is running,		 * otherwise del_timer_sync() can't detect that the timer's		 * handler yet has not finished. This also guarantees that		 * the timer is serialized wrt itself.		 */		if (likely(base->running_timer != timer)) {			/* See the comment in lock_timer_base() */			timer_set_base(timer, NULL);			spin_unlock(&base->lock);			base = new_base;			spin_lock(&base->lock);			timer_set_base(timer, base);		}	}	timer->expires = expires;	internal_add_timer(base, timer);	spin_unlock_irqrestore(&base->lock, flags);	return ret;}EXPORT_SYMBOL(__mod_timer);/** * add_timer_on - start a timer on a particular CPU * @timer: the timer to be added * @cpu: the CPU to start it on * * This is not very scalable on SMP. Double adds are not possible. */void add_timer_on(struct timer_list *timer, int cpu){	tvec_base_t *base = per_cpu(tvec_bases, cpu);  	unsigned long flags;	timer_stats_timer_set_start_info(timer);  	BUG_ON(timer_pending(timer) || !timer->function);	spin_lock_irqsave(&base->lock, flags);	timer_set_base(timer, base);	internal_add_timer(base, timer);	spin_unlock_irqrestore(&base->lock, flags);}/** * mod_timer - modify a timer's timeout * @timer: the timer to be modified * @expires: new timeout in jiffies * * mod_timer() is a more efficient way to update the expire field of an * active timer (if the timer is inactive it will be activated) * * mod_timer(timer, expires) is equivalent to: * *     del_timer(timer); timer->expires = expires; add_timer(timer); * * Note that if there are multiple unserialized concurrent users of the * same timer, then mod_timer() is the only safe way to modify the timeout, * since add_timer() cannot modify an already running timer. * * The function returns whether it has modified a pending timer or not. * (ie. mod_timer() of an inactive timer returns 0, mod_timer() of an * active timer returns 1.) */int mod_timer(struct timer_list *timer, unsigned long expires){	BUG_ON(!timer->function);	timer_stats_timer_set_start_info(timer);	/*	 * This is a common optimization triggered by the	 * networking code - if the timer is re-modified	 * to be the same thing then just return:	 */	if (timer->expires == expires && timer_pending(timer))		return 1;	return __mod_timer(timer, expires);}EXPORT_SYMBOL(mod_timer);/** * del_timer - deactive a timer. * @timer: the timer to be deactivated * * del_timer() deactivates a timer - this works on both active and inactive * timers. * * The function returns whether it has deactivated a pending timer or not. * (ie. del_timer() of an inactive timer returns 0, del_timer() of an * active timer returns 1.) */int del_timer(struct timer_list *timer){	tvec_base_t *base;	unsigned long flags;	int ret = 0;	timer_stats_timer_clear_start_info(timer);	if (timer_pending(timer)) {		base = lock_timer_base(timer, &flags);		if (timer_pending(timer)) {			detach_timer(timer, 1);			ret = 1;		}		spin_unlock_irqrestore(&base->lock, flags);	}	return ret;}EXPORT_SYMBOL(del_timer);#ifdef CONFIG_SMP/** * try_to_del_timer_sync - Try to deactivate a timer * @timer: timer do del

⌨️ 快捷键说明

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