linux时间子系统 - clocksource/timekeeper

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 mode\n",
                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 %s\n", 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


datecontentlinux
2016.12.11clocksourcelinux 4.6.3
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值