Timer源代码分析
u64 jiffies_64 __cacheline_aligned_in_smp = INITIAL_JIFFIES;
EXPORT_SYMBOL(jiffies_64);
/*
* Have the 32 bit jiffies value wrap 5 minutes after boot
* so jiffies wrap bugs show up earlier.
*/
#define INITIAL_JIFFIES ((unsigned long)(unsigned int) (-300*HZ))
一般在嵌入式的平台上,HZ取值为100,所以300*HZ的值为30000。30000的十六进制表示为0x7530。数据都是以补码的形式存储的,正数的补码是它本身,负数的补码是取反再加1。0x7530取反后是0xffff8acf,再加1后是0xffff8ad0。由于经过了强制类型转换(unsigned long)(unsigned int),所以jiffies_64被赋值为0xffff8ad0
/*
* 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)
struct tvec {
struct list_head vec[TVN_SIZE];
};
struct tvec_root {
struct list_head vec[TVR_SIZE];
};
struct tvec_base {
spinlock_t lock;
struct timer_list *running_timer;
unsigned long timer_jiffies;
unsigned long next_timer;
struct tvec_root tv1;
struct tvec tv2;
struct tvec tv3;
struct tvec tv4;
struct tvec tv5;
} ____cacheline_aligned;
struct tvec_base boot_tvec_bases;
EXPORT_SYMBOL(boot_tvec_bases);
static DEFINE_PER_CPU(struct tvec_base *, tvec_bases) = &boot_tvec_bases;
这段代码是per-CPU定时器矢量的定义。
一般在内存比较少的平台才会定义CONFIG_BASE_SMALL为1,以便缩减内存的占用。在我们的平台上,CONFIG_BASE_SMALL定义为0。所以TVN_BITS为6,TVR_BITS为8,TVN_SIZE为26 =64,TVN_SIZE为28=256,TVN_MASK为0X3F,TVR_MASK为0XFF。
struct tvec 是一个有64个链表头的数组,struct tvec_base是一个有256个链表头的数组。
struct tvec_base结构体中:
lock是个自旋锁,操作struct tvec_base结构体中的变量值时都必须持有锁,以免引起同步等问题。
running_timer是本地cpu正在运行的定时器。
timer_jiffies 表示需要检查的动态定时器的最早到期时间。该字段在系统启动时被设置成jiffies的值,且只能由run_timer_softirq()函数增加它的值。一般情况下,它的值bijiffies的值大1。
next_timer是本地cpu最早到期的定时器。
tv1是包含256个list_head元素的数组,包含了在28-1个节拍内将要到期的所有动态定时器。
tv2是包含64个list_head元素的数组,包含了在214-1个节拍内将要到期的所有动态定时器。
tv3是包含64个list_head元素的数组,包含了在220-1个节拍内将要到期的所有动态定时器。
tv4是包含64个list_head元素的数组,包含了在226-1个节拍内将要到期的所有动态定时器。
tv5 与前面的字段几乎相同,但唯一的区别就是vec数组的最后一项是一个大expires字段值的动态定时器。
/* Functions below help us manage 'deferrable' flag */
static inline unsigned int tbase_get_deferrable(struct tvec_base *base)
{
return ((unsigned int)(unsigned long)base & TBASE_DEFERRABLE_FLAG);
}
static inline struct tvec_base *tbase_get_base(struct tvec_base *base)
{
return ((struct tvec_base *)((unsigned long)base & ~TBASE_DEFERRABLE_FLAG));
}
static inline void timer_set_deferrable(struct timer_list *timer)
{
timer->base = TBASE_MAKE_DEFERRED(timer->base);
}
static inline void
timer_set_base(struct timer_list *timer, struct tvec_base *new_base)
{
timer->base = (struct tvec_base *)((unsigned long)(new_base) |
tbase_get_deferrable(timer->base));
}
/*
* Note that all tvec_bases are 2 byte aligned and lower bit of
* base in timer_list is guaranteed to be zero. Use the LSB to
* indicate whether the timer is deferrable.
*
* A deferrable timer will work normally when the system is busy, but
* will not cause a CPU to come out of idle just to service it; instead,
* the timer will be serviced when the CPU eventually wakes up with a
* subsequent non-deferrable timer.
*/
#define TBASE_DEFERRABLE_FLAG (0x1)
刚看这段代码时看得一头雾水,竟然绕个弯来判断定时器是否是可延迟的。它的原理是这样的:tvec_bases这个数据结构是2个字节对齐的,而timer_list结构体的base字段就是指向tvec_base的地址,所以base的值肯定是2的倍数,即最低位为0。搞不明白为什么要这样设计,增加代码复杂性,难道仅仅是为了省个保存标志位的内存?
#define TBASE_MAKE_DEFERRED(ptr) ((struct tvec_base *) \
((unsigned char *)(ptr) + TBASE_DEFERRABLE_FLAG))
这个宏定义的作用是将原来的地址加1,即如果原来timer_list的base字段的地址为0x11223344,调用timer_set_deferrable(struct timer_list *timer)之后timer->base的值变为0x11223345。
static unsigned long round_jiffies_common(unsigned long j, int cpu,
bool force_up)
{
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.
* But never round down if @force_up is set.
*/
if (rem < HZ/4 && !force_up) /* round down */