time keeping 机制中为什么忽视 85ns 的延迟
- tk_update_ktime_data
原来在这里:
- sysvec_apic_timer_interrupt
- instr_sysvec_apic_timer_interrupt
- irq_enter_rcu
- tick_irq_enter
- tick_nohz_irq_enter
- tick_nohz_update_jiffies
- update_wall_time
- timekeeping_advance
- timekeeping_update_from_shadow
- tk_update_ktime_data
- timekeeping_update_from_shadow
- timekeeping_advance
- update_wall_time
- tick_nohz_update_jiffies
- tick_nohz_irq_enter
- tick_irq_enter
- irq_enter_rcu
- instr_sysvec_apic_timer_interrupt
- timekeeping_advance : 实际上,在这里每次都有 85ns 的差距
- clocksource_delta
- logarithmic_accumulation : 这里是关键
- accumulate_nsecs_to_secs
- timekeeping_update_from_shadow
认为问题的关键在:
/*
* With NO_HZ we may have to accumulate many cycle_intervals
* (think "ticks") worth of time at once. To do this efficiently,
* we calculate the largest doubling multiple of cycle_intervals
* that is smaller than the offset. We then accumulate that
* chunk in one go, and then try to consume the next smaller
* doubled multiple.
*/
shift = ilog2(offset) - ilog2(tk->cycle_interval); // tk->cycle_interval = 2995200
shift = max(0, shift);
/* Bound shift to one less than what overflows tick_length */
maxshift = (64 - (ilog2(ntp_tick_length())+1)) - 1;
shift = min(shift, maxshift);
while (offset >= tk->cycle_interval) {
// cycle_interval 的单位是 khz ,而 offset 是 cycle
// 可以理解 tk->cycle_interval 为,多少个 cycle 作为一个 interval 来统计一次
offset = logarithmic_accumulation(tk, offset, shift, &clock_set);
if (offset < tk->cycle_interval<<shift)
shift--;
}
$ p tk->tkr_raw.shift
$9 = 24
$ p tk->tkr_raw.mult
$10 = 5601368
$ p tk->raw_interval
$12 = 16777217433600
$ p tk->cycle_interval
$15 = 2995200
raw_interval 就是用的 mult * interval
In [1]: 5601368 * 2995200
Out[1]: 16777217433600
tk->raw_interval = interval * clock->mult;
关键在于这里:
/* Accumulate raw time */
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++;
}
此外还有这里
static inline void tk_normalize_xtime(struct timekeeper *tk)
{
while (tk->tkr_mono.xtime_nsec >= ((u64)NSEC_PER_SEC << tk->tkr_mono.shift)) {
tk->tkr_mono.xtime_nsec -= (u64)NSEC_PER_SEC << tk->tkr_mono.shift;
tk->xtime_sec++;
}
while (tk->tkr_raw.xtime_nsec >= ((u64)NSEC_PER_SEC << tk->tkr_raw.shift)) {
tk->tkr_raw.xtime_nsec -= (u64)NSEC_PER_SEC << tk->tkr_raw.shift;
tk->raw_sec++;
}
}
以及这里
static void tk_xtime_add(struct timekeeper *tk, const struct timespec64 *ts)
{
tk->xtime_sec += ts->tv_sec;
tk->tkr_mono.xtime_nsec += (u64)ts->tv_nsec << tk->tkr_mono.shift;
tk_normalize_xtime(tk);
}
基于原则就是,当和 xtime_nsec ,尽量使用 mult * cycle 才可以
以及在 accumulate_nsecs_to_secs 中
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;
总之,就是任何 shift 和 hz 的时候都是需要小心的。
但是 timekeeping_apply_adjustment 中会去调整 interval
tk->xtime_interval += interval;
但是还好,tk_setup_internals 中,这里有很多
tk->cycle_interval = interval;
/* Go back from cycles -> shifted ns */
tk->xtime_interval = interval * clock->mult;
tk->xtime_remainder = ntpinterval - tk->xtime_interval;
tk->raw_interval = interval * clock->mult;
还好,不是去调整 raw_interval 。
xtime_remainder 是用于解决这个问题的吗?
tk->xtime_interval = interval * clock->mult;
tk->xtime_remainder = ntpinterval - tk->xtime_interval;
tk->raw_interval = interval * clock->mult;
本站所有文章转发 CSDN 将按侵权追究法律责任,其它情况随意。