概述
linux时间子系统中有一个很重要的记录时间的模块就是timekeeper,而timekeeper需要底层clock硬件设备的支持,每一个设备用结构体clocksource来表示,注册进系统的每一个clocksource会凭借优先级最终被选择成为timekeeper的时钟源。
1. clocksource的注册
1.1 clocksource结构体
struct clocksource {
cycle_t (*read)(struct clocksource *cs);
cycle_t mask;
u32 mult;
u32 shift;
u64 max_idle_ns;
u32 maxadj;
#ifdef CONFIG_ARCH_CLOCKSOURCE_DATA
struct arch_clocksource_data archdata;
#endif
u64 max_cycles;
const char *name;
struct list_head list;
int rating;----------------------------------------clock设备的优先级
int (*enable)(struct clocksource *cs);-------------clock设备使能
void (*disable)(struct clocksource *cs);-----------clock设备关闭
unsigned long flags;-------------------------------clock源的标志
void (*suspend)(struct clocksource *cs);-----------PM调用函数suspend
void (*resume)(struct clocksource *cs);------------PM调用函数resume
/* private: */
#ifdef CONFIG_CLOCKSOURCE_WATCHDOG
/* Watchdog related data, used by the framework */
struct list_head wd_list;
cycle_t cs_last;
cycle_t wd_last;
#endif
struct module *owner;
};
1.2 clocksource_list
每一个注册进系统的clock设备都会加入一个全局链表clocksource_list
(kernel/time/clocksource.c)
static LIST_HEAD(clocksource_list);
但是timekeeper只会选取其中最合适的一个作为主clock,用一下变量来表示
static struct clocksource *curr_clocksource;
1.3 注册函数
clocksource的注册函数有如下几个:
(include/linux/clocksource.h)
static inline int __clocksource_register(struct clocksource *cs)
{
return __clocksource_register_scale(cs, 1, 0);
}
static inline int clocksource_register_hz(struct clocksource *cs, u32 hz)
{
return __clocksource_register_scale(cs, 1, hz);
}
static inline int clocksource_register_khz(struct clocksource *cs, u32 khz)
{
1.4 clocksource注册过程
clocksource设备的注册可以当作一个驱动,所以一般clock设备的文件都在dricers/clocksource/目录下,下面以arm global timer为例来介绍注册过程。
clocksource设备的初始化接口由宏定义:
CLOCKSOURCE_OF_DECLARE(arm_gt, "arm,cortex-a9-global-timer",
global_timer_of_register);
global的初始化函数为global_timer_of_register,下图是注册的函数调用关系图:
1.4.1 把clock设备加入clocksource_list
函数clocksource_enqueue实现这个过程
static void clocksource_enqueue(struct clocksource *cs)
{
struct list_head *entry = &clocksource_list;
struct clocksource *tmp;
list_for_each_entry(tmp, &clocksource_list, list)
/* Keep track of the place, where to insert */
if (tmp->rating >= cs->rating)
entry = &tmp->list;--------------得到最合适插入的地方
list_add(&cs->list, entry);--------------加入clocksource_list
}
1.4.2 选取最合适的curr_clocksource
函数__clocksource_select实现这个过程:
static void __clocksource_select(bool skipcur)
{
bool oneshot = tick_oneshot_mode_active();
struct clocksource *best, *cs;
/* Find the best suitable clocksource */
best = clocksource_find_best(oneshot, skipcur);
if (!best)
return;--------------------------------------------没有找到合适的就直接返回
/* Check for the override clocksource. */
list_for_each_entry(cs, &clocksource_list, list) {
if (skipcur && cs == curr_clocksource)
continue;
if (strcmp(cs->name, override_name) != 0)
continue;
/*
* Check to make sure we don't switch to a non-highres
* capable clocksource if the tick code is in oneshot
* mode (highres or nohz)
*/
if (!(cs->flags & CLOCK_SOURCE_VALID_FOR_HRES) && oneshot) {
/* Override clocksource cannot be used. */
pr_warn("Override clocksource %s is not HRT compatible - cannot switch while in HRT/NOHZ moden",
cs->name);
override_name[0] = 0;
} else
/* Override clocksource can be used. */
best = cs;-------------------------------------根据上面各个条件的过滤,最后得到最好的clocksource
break;
}
if (curr_clocksource != best && !timekeeping_notify(best)) {
pr_info("Switched to clocksource %sn", best->name);
curr_clocksource = best;--------------------------把clocksource给curr_clocksource
}
}
2. 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;
struct timespec64 raw_time;
/* The following members are for timekeeping internal use */
cycle_t cycle_interval;
u64 xtime_interval;
s64 xtime_remainder;
u32 raw_interval;
/* The ntp_tick_length() value currently being used.
* This cached copy ensures we consistently apply the tick
* length for an entire tick, as ntp_tick_length may change
* mid-tick, and we don't want to apply that new value to
* the tick in progress.
*/
u64 ntp_tick;
/* Difference between accumulated time and NTP time in ntp
* shifted nano seconds. */
s64 ntp_error;
u32 ntp_error_shift;
u32 ntp_err_mult;
#ifdef CONFIG_DEBUG_TIMEKEEPING
long last_warning;
/*
* These simple flag variables are managed
* without locks, which is racy, but they are
* ok since we don't really care about being
* super precise about how many events were
* seen, just that a problem was observed.
*/
int underflow_seen;
int overflow_seen;
#endif
};
TODO
Change Log
date | content | linux |
---|---|---|
2016.12.11 | clocksource | linux 4.6.3 |
最后
以上就是缓慢烤鸡为你收集整理的linux时间子系统 - clocksource/timekeeper1. clocksource的注册1.1 clocksource结构体2. timekeeperChange Log的全部内容,希望文章能够帮你解决linux时间子系统 - clocksource/timekeeper1. clocksource的注册1.1 clocksource结构体2. timekeeperChange Log所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复