时间维护层会收到Tick层的周期调用,每次调用的周期是由内核参数决定的。在此期间,时间维护层可以读取时钟源设备的周期数,从而感知时间的流逝。
目前时间维护层主要负责维护以下几种类型的时间:
为了维护以上各种时间,时间维护层使用了timekeeper结构体来记录各种数据:
struct timekeeper {
struct tk_read_base tkr_mono;
struct tk_read_base tkr_raw;
u64 xtime_sec;
unsigned long ktime_sec;
struct timespec64 wall_to_monotonic;
ktime_t offs_real;
ktime_t offs_boot;
ktime_t offs_tai;
s32 tai_offset;
unsigned int clock_was_set_seq;
u8 cs_was_changed_seq;
ktime_t next_leap_ktime;
u64 raw_sec;
struct timespec64 monotonic_to_boot;
u64 cycle_interval;
u64 xtime_interval;
s64 xtime_remainder;
u64 raw_interval;
u64 ntp_tick;
s64 ntp_error;
u32 ntp_error_shift;
u32 ntp_err_mult;
u32 skip_second_overflow;
#ifdef CONFIG_DEBUG_TIMEKEEPING
......
#endif
};
struct tk_read_base {
struct clocksource *clock;
u64 mask;
u64 cycle_last;
u32 mult;
u32 shift;
u64 xtime_nsec;
ktime_t base;
u64 base_real;
};
下面分场景介绍一下时间维护层的工作过程。
时间维持层的初始化函数是timekeeping_init:
void __init timekeeping_init(void)
{
struct timespec64 wall_time, boot_offset, wall_to_mono;
struct timekeeper *tk = &tk_core.timekeeper;
struct clocksource *clock;
unsigned long flags;
/* 尝试获得当前的实时时间 */
read_persistent_wall_and_boot_offset(&wall_time, &boot_offset);
if (timespec64_valid_settod(&wall_time) &&
timespec64_to_ns(&wall_time) > 0) {
persistent_clock_exists = true;
} else if (timespec64_to_ns(&wall_time) != 0) {
pr_warn("Persistent clock returned invalid value");
wall_time = (struct timespec64){0};
}
if (timespec64_compare(&wall_time, &boot_offset) < 0)
boot_offset = (struct timespec64){0};
/* 计算当前实时时间和单调时间之间的差值 */
wall_to_mono = timespec64_sub(boot_offset, wall_time);
/* 获得自旋锁并关中断 */
raw_spin_lock_irqsave(&timekeeper_lock, flags);
write_seqcount_begin(&tk_core.seq);
/* 初始化NTP层 */
ntp_init();
/* 获取默认时钟源设备 */
clock = clocksource_default_clock();
/* 打开默认时钟源设备 */
if (clock->enable)
clock->enable(clock);
/* 设置时钟源设备 */
tk_setup_internals(tk, clock);
/* 设置实时时间 */
tk_set_xtime(tk, &wall_time);
tk->raw_sec = 0;
/* 设置各种差值 */
tk_set_wall_to_mono(tk, wall_to_mono);
timekeeping_update(tk, TK_MIRROR | TK_CLOCK_WAS_SET);
write_seqcount_end(&tk_core.seq);
/* 释放自旋锁并开中断 */
raw_spin_unlock_irqrestore(&timekeeper_lock, flags);
}
该函数首先尝试获得系统当前的实时时间,不过不是什么系统都支持这个操作,大多数情况下读出来的值都是空的(全0)。
clocksource_default_clock函数返回的默认时钟源其实就是系统中定义的那个精度最差的基于jiffies的时钟源。在获得默认时钟源设备之后,timekeeping_init函数接着调用了tk_setup_internals函数,将该设备设置进时钟维护层:
static void tk_setup_internals(struct timekeeper *tk, struct clocksource *clock)
{
u64 interval;
u64 tmp, ntpinterval;
struct clocksource *old_clock;
++tk->cs_was_changed_seq;
old_clock = tk->tkr_mono.clock;
/* 设置记录单调时间的结构体变量 */
tk->tkr_mono.clock = clock;
tk->tkr_mono.mask = clock->mask;
/* 读取时钟源当前计数 */
tk->tkr_mono.cycle_last = tk_clock_read(&tk->tkr_mono);
/* 设置记录原始单调时间的结构体变量 */
tk->tkr_raw.clock = clock;
tk->tkr_raw.mask = clock->mask;
tk->tkr_raw.cycle_last = tk->tkr_mono.cycle_last;
/* NTP_INTERVAL_LENGTH表示一个NTP周期有多少纳秒 */
tmp = NTP_INTERVAL_LENGTH;
/* 计算一个NTP周期包含多少个时钟源周期数并赋值给cycle_interval */
tmp <<= clock->shift;
ntpinterval = tmp;
/* 四舍五入 */
tmp += clock->mult/2;
do_div(tmp, clock->mult);
if (tmp == 0)
tmp = 1;
interval = (u64) tmp;
tk->cycle_interval = interval;
/* 设置xtime_interval、xtime_remainder和raw_interval */
tk->xtime_interval = interval * clock->mult;
tk->xtime_remainder = ntpinterval - tk->xtime_interval;
tk->raw_interval = interval * clock->mult;
/* 如果新老时钟源设备的shift值不同则相应做出调整 */
if (old_clock) {
int shift_change = clock->shift - old_clock->shift;
if (shift_change < 0) {
tk->tkr_mono.xtime_nsec >>= -shift_change;
tk->tkr_raw.xtime_nsec >>= -shift_change;
} else {
tk->tkr_mono.xtime_nsec <<= shift_change;
tk->tkr_raw.xtime_nsec <<= shift_change;
}
}
/* 设置shift */
tk->tkr_mono.shift = clock->shift;
tk->tkr_raw.shift = clock->shift;
tk->ntp_error = 0;
/* 计算两层shift位之间的差值 */
tk->ntp_error_shift = NTP_SCALE_SHIFT - clock->shift;
tk->ntp_tick = ntpinterval << tk->ntp_error_shift;
/* 设置mult */
tk->tkr_mono.mult = clock->mult;
tk->tkr_raw.mult = clock->mult;
tk->ntp_err_mult = 0;
tk->skip_second_overflow = 0;
}
在timekeeper结构体里面凡是跟纳秒相关的变量,基本上都是真实值向左shift过的。因为纳秒数可以通过周期数由下面的公式转换过来:
所以cycle_interval和xtime_interval的转换关系是:
而xtime_remainder记录的是将时钟源设备的周期数转换成纳秒数所引入的精度损失。通过前面的代码分析,可以知道cycle_interval的计算公式是:
不过这里的除法是整数除法,肯定是有精度损失的,也就是:
这个精度是cycle_interval累积的,也就是时间维持层每检测到时钟源设备经过了cycle_interval周期,会将代表当前纳秒数位移值的变量xtime_nsec加上xtime_interval,但其实应该不止那么多。因此,就需要一个变量xtime_remainder来记录它们之间的精度差值,也就是:
在初始设置时,ntp_error被设置成了0,刚开始,累积错误当然是0。ntp_error_shift被设置成了NTP层的shift和时钟源设备shift之间的差值。ntp_tick其实最终被设置成了NTP_INTERVAL_LENGTH< 完成了时钟源设备的设置之后,timekeeping_init函数接着调用tk_set_wall_to_mono函数,通过前面计算好的wall_to_monotonic值,设置各种其它的差值: 完成初始设置之后,初始化函数timekeeping_init最后还调用了timekeeping_update函数用来更新时间维护层的数据,我们接着分析。 前面提到了,timekeeping_update函数用来更新时间维护层的数据。该函数的第二个参数是action动作,目前共定义了下面三个值: 该函数除了根据传入的action参数做出相应的操作外,主要就是调用tk_update_ktime_data函数: 该函数首先更新了代表单调时间的tkr_mono结构体中的base变量,基本上可以理解为单调时间的base=xtime_sec+wall_to_monotonic。这个很好理解,xtime_sec代表了当前实时时间的秒数,而wall_to_monotonic代表了墙上时间也就是实时时间与单调时间的差值,所以直接将它们相加之后再转换成纳秒数,就是单调时间的base值了。同时,该函数还更新了代表原始单调时间的tkr_raw结构体中的base变量,因为原始单调时间是不受NTP调整的,一直都是自顾自的累加,所以不需要通过实时时间转换,而直接就是用代表当前原始单调时间秒数的raw_sec转换成纳秒数就可以了。该函数还更新了代表单调时间当前秒数的ktime_sec,前面说了,单调时间其实是通过实时时间转过来的,那么就有可能当前的纳秒数加上转换偏移的纳秒数之后大于1秒(xtime_nsec >> shift + wall_to_monotonic.tv_nsec > NSEC_PER_SEC),这时候就需要将纳秒数累积到秒上去。 时间维护层通过调用函数update_wall_time来更新墙上时间: 这个函数就是调用了timekeeping_advance函数,传入的参数是timekeeping_adv_mode枚举变量: 共有两种模式: update_wall_time传递的是TK_ADV_TICK: 该函数首先读取了当前时钟源设备的周期数,然后将其与记录在tkr_mono结构体变量中的代表上一次周期数的cycle_last值相减,计算两者之间的差值。由于NO_HZ模式的存在,在处于空闲模式的时候,CPU是收不到任何Tick的,因此有可能这个差值会很大,如果一个周期一个周期的累加效率太低了,这里采取了取log然后用shift计算的方法。一共要累积多少个NTP周期可以用下面公式计算: 两边都取log2之后可以得到: 两边取2的次方后可以得到: 上面函数中的shift其实就是计算的最大的那个NTP的周期数,但是由于没有浮点单元,所以ilog2其实算出来的是整数,也就是最高位1所在的位数。得到最高位了,循环调用logarithmic_accumulation函数通过shift累积时间和周期数,如果当前shift位全都累积完了,就接着累积下一位,一直到最低位,也就是offset小于cycle_interval为止。由于对shift的位数有限制,所以最高位的shift有可能要累积多次,下面的每一位最多只会累积一次。 有了Tick数之后,计算过去了多少时间就很简单了,由于xtime_interval表示一个NTP周期包含多少shift后的纳秒数,所以经过了多长时间可以用下面公式计算: 这也就是在logarithmic_accumulation函数中要做的事: 如果offset小于位移后的周期数,则说明这一个shift位不需要累积,直接返回。可以看到,在累积纳秒数xtime_nsec的时候是直接用xtime_interval向左位移shift位计算的。累积完纳秒后,还需要调用accumulate_nsecs_to_secs函数,看是不是纳秒数已经超过一秒了,如果是的话还需要将纳秒数累积成秒数,并将减掉对应一秒的纳秒数: 在logarithmic_accumulation函数的最后,还会计算NTP时间和当前实时时间之间的差值,计算公式为: 这个误差是按照NTP周期累积的,所以最后还需要左移shift位。前面看到了,一般情况下ntp_tick的值并不会更改,就等于xtime_interval加上xtime_remainder,所以计算出来的误差应该是0。但管理员可以通过命令adjtimex或者是通过系统调用adjtimex对其进行调整。 在完成了时间的累积之后,timekeeping_advance函数会调用timekeeping_adjust函数对mult的值做出调整: 如果ntp_tick的值没有变的话,会将mult还原回原来的值,ntp_err_mult要么是0要么是1,在后面的timekeeping_apply_adjustment函数会将这个值加上,所以在这里还原的时候要减去ntp_err_mult。如果ntp_tick的值改变了的话,要重新计算mult的值。 加大mult的值则实时时间变快,而减小mult的值则实时时间变慢。所以,如果ntp_error的值大于0,说明当前时间晚于NTP时间,因此需要将mult的值加1进行“追赶”。 接着,该函数会调用timekeeping_apply_adjustment函数,针对新的mult值对timekeeper结构体中的其它变量进行修正: 该函数根据时钟源设备已经经过的周期数和要调整的mult的差值相应调整xtime_interval和xtime_nsec的值。可以看到,只更改了对应单调时间的结构体变量tkr_mono中的值,对于表示原始单调时间的结构体变量tkr_raw并没有做任何更改,这也符合它们的定义。 具体怎么调整呢,我们先来看xtime_interval,前面说了,xtime_interval和cycle_interval之间的关系是: 那么调整之后应该就变成了: 稍微变换之后就是: 而cycle_interval * mult_adj已经计算过了,就是局部变量interval的值,所以最终就是: 接着看xtime_nsec的计算,计算它的时候,其实有一个前提条件,就是不希望产生时间的跳变,也就是说改了mult和xtime_nsec的值后和不改它之前通过offset计算出来的时间是一样的。即: 稍微变换一下就可以得到: 而offset * mult_adj也已经在前面计算过了,存放在变量offset中,所以最后就是: 这又会引入一个新的问题,如果调用timekeeping_apply_adjustment的时候,刚好xtime_nsec的值非常小,小于offset的值,那么当减去offset之后就会造成下溢出,这就是在timekeeping_adjust函数最后所要做的处理。首先是将表示纳秒数的xtime_nsec值加上一秒(当然还要位移),然后将表示秒数的xtime_sec减去1,最后赋值skip_second_overflow为1,因为其实这秒已经在前面累积过了,所以要跳过。这样在后面再次调用accumulate_nsecs_to_secs函数的时候,会跳过这一秒。 更新时间都是首先累积到实时时间也就是墙上时间上的,其它的时间都是通过实时时间转换过来的。 在对时钟源层进行分析的时候,我们提到过其在__clocksource_select函数中选择最佳时钟源的时候,会调用时间维护层的timekeeping_notify函数: 该函数在“停机”的状态下调用change_clocksource函数切换时钟源: 该函数先调用timekeeping_forward_now函数,立即读取当前要被替换的老时钟源周期,并用其更新当前时间: 然后调用tk_setup_internals函数设置新的时钟源设备,最后调用timekeeping_update函数更新时间维护层的数据。static void tk_set_wall_to_mono(struct timekeeper *tk, struct timespec64 wtm)
{
struct timespec64 tmp;
/* 首先验证当前的offs_real是否等于-wall_to_monotonic */
set_normalized_timespec64(&tmp, -tk->wall_to_monotonic.tv_sec,
-tk->wall_to_monotonic.tv_nsec);
WARN_ON_ONCE(tk->offs_real != timespec64_to_ktime(tmp));
/* 更新wall_to_monotonic */
tk->wall_to_monotonic = wtm;
/* 根据新的wall_to_monotonic设置offs_real */
set_normalized_timespec64(&tmp, -wtm.tv_sec, -wtm.tv_nsec);
tk->offs_real = timespec64_to_ktime(tmp);
/* offs_tai等于offs_real加上tai_offset */
tk->offs_tai = ktime_add(tk->offs_real, ktime_set(tk->tai_offset, 0));
}
2)更新时间维护层的数据
#define TK_CLEAR_NTP (1 << 0)
#define TK_MIRROR (1 << 1)
#define TK_CLOCK_WAS_SET (1 << 2)
static void timekeeping_update(struct timekeeper *tk, unsigned int action)
{
/* 如果定义了TK_CLEAR_NTP动作则对NTP进行清理 */
if (action & TK_CLEAR_NTP) {
tk->ntp_error = 0;
ntp_clear();
}
/* 更新闰秒的状态 */
tk_update_leap_state(tk);
tk_update_ktime_data(tk);
/* 更新vdso中关于时间的数据 */
update_vsyscall(tk);
update_pvclock_gtod(tk, action & TK_CLOCK_WAS_SET);
/* base_real = base + offs_real */
tk->tkr_mono.base_real = tk->tkr_mono.base + tk->offs_real;
update_fast_timekeeper(&tk->tkr_mono, &tk_fast_mono);
update_fast_timekeeper(&tk->tkr_raw, &tk_fast_raw);
/* 如果定义了TK_CLOCK_WAS_SET动作则递增clock_was_set_seq */
if (action & TK_CLOCK_WAS_SET)
tk->clock_was_set_seq++;
/* 如果定义了TK_MIRROR动作则将主timekeeper结构体内容拷贝到影子timekeeper中 */
if (action & TK_MIRROR)
memcpy(&shadow_timekeeper, &tk_core.timekeeper,
sizeof(tk_core.timekeeper));
}
static inline void tk_update_ktime_data(struct timekeeper *tk)
{
u64 seconds;
u32 nsec;
/* 更新tkr_mono的base变量 */
seconds = (u64)(tk->xtime_sec + tk->wall_to_monotonic.tv_sec);
nsec = (u32) tk->wall_to_monotonic.tv_nsec;
tk->tkr_mono.base = ns_to_ktime(seconds * NSEC_PER_SEC + nsec);
/* 更新ktime_sec */
nsec += (u32)(tk->tkr_mono.xtime_nsec >> tk->tkr_mono.shift);
/* 是否需要累积到秒上 */
if (nsec >= NSEC_PER_SEC)
seconds++;
tk->ktime_sec = seconds;
/* 更新tkr_raw的base变量 */
tk->tkr_raw.base = ns_to_ktime(tk->raw_sec * NSEC_PER_SEC);
}
3)更新墙上时间
void update_wall_time(void)
{
timekeeping_advance(TK_ADV_TICK);
}
enum timekeeping_adv_mode {
TK_ADV_TICK,
TK_ADV_FREQ
};
static void timekeeping_advance(enum timekeeping_adv_mode mode)
{
/* 真的全局timekeeper */
struct timekeeper *real_tk = &tk_core.timekeeper;
/* 影子时间维护者 */
struct timekeeper *tk = &shadow_timekeeper;
u64 offset;
int shift = 0, maxshift;
unsigned int clock_set = 0;
unsigned long flags;
/* 获得自旋锁并关中断 */
raw_spin_lock_irqsave(&timekeeper_lock, flags);
/* 如果被挂起了就直接退出 */
if (unlikely(timekeeping_suspended))
goto out;
#ifdef CONFIG_ARCH_USES_GETTIMEOFFSET
......
#else
/* 计算当前时钟源周期数和上一次之间的差值 */
offset = clocksource_delta(tk_clock_read(&tk->tkr_mono),
tk->tkr_mono.cycle_last, tk->tkr_mono.mask);
/* 如果模式是TK_ADV_TICK且累积的时钟源周期数小于cycle_interval则什么都不做 */
if (offset < real_tk->cycle_interval && mode == TK_ADV_TICK)
goto out;
#endif
timekeeping_check_update(tk, offset);
/* 根据时钟源周期数的差值计算 */
shift = ilog2(offset) - ilog2(tk->cycle_interval);
shift = max(0, shift);
/* 最大shift是不造成tick_length溢出的shift减去1 */
maxshift = (64 - (ilog2(ntp_tick_length())+1)) - 1;
shift = min(shift, maxshift);
/* 一直循环处理到offset
static u64 logarithmic_accumulation(struct timekeeper *tk, u64 offset,
u32 shift, unsigned int *clock_set)
{
/* 计算一共过了多少周期数 */
u64 interval = tk->cycle_interval << shift;
u64 snsec_per_sec;
/* 如果offset小于位移后的周期数则返回 */
if (offset < interval)
return offset;
/* 将offset减去位移后的周期数 */
offset -= interval;
/* 将cycle_last加上位移后的周期数 */
tk->tkr_mono.cycle_last += interval;
tk->tkr_raw.cycle_last += interval;
/* 累积单调时间纳秒数 */
tk->tkr_mono.xtime_nsec += tk->xtime_interval << shift;
/* 累积纳秒数到实时时间秒数上 */
*clock_set |= accumulate_nsecs_to_secs(tk);
/* 累积原始单调时间 */
tk->tkr_raw.xtime_nsec += tk->raw_interval << shift;
snsec_per_sec = (u64)NSEC_PER_SEC << tk->tkr_raw.shift;
while (tk->tkr_raw.xtime_nsec >= snsec_per_sec) {
tk->tkr_raw.xtime_nsec -= snsec_per_sec;
tk->raw_sec++;
}
/* 累积NTP时间和当前实时时间之间的差值 */
tk->ntp_error += tk->ntp_tick << shift;
tk->ntp_error -= (tk->xtime_interval + tk->xtime_remainder) <<
(tk->ntp_error_shift + shift);
return offset;
}
static inline unsigned int accumulate_nsecs_to_secs(struct timekeeper *tk)
{
u64 nsecps = (u64)NSEC_PER_SEC << tk->tkr_mono.shift;
unsigned int clock_set = 0;
while (tk->tkr_mono.xtime_nsec >= nsecps) {
int leap;
/* 减掉对应1秒的纳秒数 */
tk->tkr_mono.xtime_nsec -= nsecps;
/* 将实时时间秒数加1 */
tk->xtime_sec++;
/* 如果这1秒已经累积过了则跳过下面的执行步骤 */
if (unlikely(tk->skip_second_overflow)) {
tk->skip_second_overflow = 0;
continue;
}
/* 处理闰秒的情况 */
leap = second_overflow(tk->xtime_sec);
if (unlikely(leap)) {
struct timespec64 ts;
tk->xtime_sec += leap;
ts.tv_sec = leap;
ts.tv_nsec = 0;
tk_set_wall_to_mono(tk,
timespec64_sub(tk->wall_to_monotonic, ts));
__timekeeping_set_tai_offset(tk, tk->tai_offset - leap);
clock_set = TK_CLOCK_WAS_SET;
}
}
return clock_set;
}
static void timekeeping_adjust(struct timekeeper *tk, s64 offset)
{
u32 mult;
/* 根据ntp_tick值的改变调整mult的值 */
if (likely(tk->ntp_tick == ntp_tick_length())) {
/* 如果ntp_tick值没变则还原回原来的mult */
mult = tk->tkr_mono.mult - tk->ntp_err_mult;
} else {
/* 如果ntp_tick值改变了则从新计算mult */
tk->ntp_tick = ntp_tick_length();
mult = div64_u64((tk->ntp_tick >> tk->ntp_error_shift) -
tk->xtime_remainder, tk->cycle_interval);
}
/* 如果ntp_error大于0说明实时时间晚于NTP时间,需要将mult加1,进行追赶。 */
tk->ntp_err_mult = tk->ntp_error > 0 ? 1 : 0;
mult += tk->ntp_err_mult;
/* 根据mult值的改变对timekeeper结构体中的相应变量进行调整 */
timekeeping_apply_adjustment(tk, offset, mult - tk->tkr_mono.mult);
/* 检查调整过后的mult的值和原始的mult值之间差值是否超过了最大限度(11%) */
if (unlikely(tk->tkr_mono.clock->maxadj &&
(abs(tk->tkr_mono.mult - tk->tkr_mono.clock->mult)
> tk->tkr_mono.clock->maxadj))) {
printk_once(KERN_WARNING
"Adjusting %s more than 11%% (%ld vs %ld)\n",
tk->tkr_mono.clock->name, (long)tk->tkr_mono.mult,
(long)tk->tkr_mono.clock->mult + tk->tkr_mono.clock->maxadj);
}
/* 处理调整过后xtime_nsec下溢出的情况 */
if (unlikely((s64)tk->tkr_mono.xtime_nsec < 0)) {
tk->tkr_mono.xtime_nsec += (u64)NSEC_PER_SEC <<
tk->tkr_mono.shift;
tk->xtime_sec--;
tk->skip_second_overflow = 1;
}
}
static __always_inline void timekeeping_apply_adjustment(struct timekeeper *tk,
s64 offset,
s32 mult_adj)
{
s64 interval = tk->cycle_interval;
/* 当前offset和interval中存的值刚好是mult_adj为1的情况 */
if (mult_adj == 0) {
/* 如果调整值为0说明不需要调整直接退出 */
return;
} else if (mult_adj == -1) {
/* 如果调整值为-1则直接将offset和interval取负 */
interval = -interval;
offset = -offset;
} else if (mult_adj != 1) {
/* 如果不为1则直接对offset和interval计算乘积 */
interval *= mult_adj;
offset *= mult_adj;
}
/* 检查调整过后是否会造成mult值的溢出 */
if ((mult_adj > 0) && (tk->tkr_mono.mult + mult_adj < mult_adj)) {
WARN_ON_ONCE(1);
return;
}
/* 调整timekeeping结构体参数 */
tk->tkr_mono.mult += mult_adj;
tk->xtime_interval += interval;
tk->tkr_mono.xtime_nsec -= offset;
}
4)切换时钟源
int timekeeping_notify(struct clocksource *clock)
{
struct timekeeper *tk = &tk_core.timekeeper;
/* 如果要替换的时钟源就是原来的则直接返回 */
if (tk->tkr_mono.clock == clock)
return 0;
/* 调用change_clocksource函数 */
stop_machine(change_clocksource, clock, NULL);
/* 通知Tick层时钟源设备已改变 */
tick_clock_notify();
return tk->tkr_mono.clock == clock ? 0 : -1;
}
static int change_clocksource(void *data)
{
struct timekeeper *tk = &tk_core.timekeeper;
struct clocksource *new, *old;
unsigned long flags;
new = (struct clocksource *) data;
/* 获得自旋锁并关中断 */
raw_spin_lock_irqsave(&timekeeper_lock, flags);
write_seqcount_begin(&tk_core.seq);
/* 立即更新时间 */
timekeeping_forward_now(tk);
/* 获得时钟源设备的模块 */
if (try_module_get(new->owner)) {
if (!new->enable || new->enable(new) == 0) {
old = tk->tkr_mono.clock;
/* 设置新的时钟源 */
tk_setup_internals(tk, new);
/* 关闭老的时钟源 */
if (old->disable)
old->disable(old);
module_put(old->owner);
} else {
module_put(new->owner);
}
}
/* 更新数据 */
timekeeping_update(tk, TK_CLEAR_NTP | TK_MIRROR | TK_CLOCK_WAS_SET);
write_seqcount_end(&tk_core.seq);
/* 释放自旋锁并开中断 */
raw_spin_unlock_irqrestore(&timekeeper_lock, flags);
return 0;
}
static void timekeeping_forward_now(struct timekeeper *tk)
{
u64 cycle_now, delta;
/* 读取时钟源设备当前周期数 */
cycle_now = tk_clock_read(&tk->tkr_mono);
/* 计算其与上一次更新之间的差值 */
delta = clocksource_delta(cycle_now, tk->tkr_mono.cycle_last, tk->tkr_mono.mask);
/* 更新cycle_last */
tk->tkr_mono.cycle_last = cycle_now;
tk->tkr_raw.cycle_last = cycle_now;
/* 计算纳秒数 */
tk->tkr_mono.xtime_nsec += delta * tk->tkr_mono.mult;
......
tk->tkr_raw.xtime_nsec += delta * tk->tkr_raw.mult;
......
tk_normalize_xtime(tk);
}