linux时间子系统 - clockevents和tick_device

在驱动中,clock设备在注册的时候会被注册进clockevent,每个clockevent会转化为tick device,然后会进行一次选择,最后成为某种类型的tick device(包括oneshot/periodic/broadcast)

1. clockevents


1.1 clockevent设备

1.1.1 clockevent结构体

clockevent设备用一下结构体表示
( include/linux/clockchips.h )

struct clock_event_device {
    void            (*event_handler)(struct clock_event_device *);-------------------中断到来时调用的函数
    int         (*set_next_event)(unsigned long evt, struct clock_event_device *);---设置下一次中断时间的函数
    int         (*set_next_ktime)(ktime_t expires, struct clock_event_device *);-----设置下一次中断时间的函数
    ktime_t         next_event;------------------------------------------------------下一次时间
    u64         max_delta_ns;
    u64         min_delta_ns;
    u32         mult;
    u32         shift;
    enum clock_event_state  state_use_accessors;
    unsigned int        features;
    unsigned long       retries;

    int         (*set_state_periodic)(struct clock_event_device *);
    int         (*set_state_oneshot)(struct clock_event_device *);
    int         (*set_state_oneshot_stopped)(struct clock_event_device *);
    int         (*set_state_shutdown)(struct clock_event_device *);
    int         (*tick_resume)(struct clock_event_device *);

    void            (*broadcast)(const struct cpumask *mask);-----------------------广播函数
    void            (*suspend)(struct clock_event_device *);
    void            (*resume)(struct clock_event_device *);
    unsigned long       min_delta_ticks;
    unsigned long       max_delta_ticks;

    const char      *name;
    int         rating;
    int         irq;
    int         bound_on;
    const struct cpumask    *cpumask;
    struct list_head    list;
    struct module       *owner;
} ____cacheline_aligned;

1.1.2 clockevent状态

一个clockevent可以被配置成某种状态

enum clock_event_state {
    CLOCK_EVT_STATE_DETACHED,----------------分离状态
    CLOCK_EVT_STATE_SHUTDOWN,----------------关闭状态
    CLOCK_EVT_STATE_PERIODIC,----------------周期性模式
    CLOCK_EVT_STATE_ONESHOT,-----------------单步走模式
    CLOCK_EVT_STATE_ONESHOT_STOPPED,---------单步走模式关闭
};

1.2 clockevent list

每个被注册为clockevent的设备都会加入到链表clockevent_devices中

( kernel/time/clockevents.c )

static LIST_HEAD(clockevent_devices);

1.3 clockevent注册函数

extern void clockevents_config_and_register(struct clock_event_device *dev,
                        u32 freq, unsigned long min_delta,
                        unsigned long max_delta);

1.4 clockevent注册过程

在clocksource的注册过程中,会进行clockevent的注册,下面以arm global timer为例来介绍注册过程,调用函数如下图:

这里写图片描述

对clockevent初始化完成后,进入tick device设备的创建过程,调用函数是tick_check_new_device,下节进行介绍

2. tick设备(tick device)


2.1 tick device结构体及类别

tick设备用以下结构体来表示:

struct tick_device {
    struct clock_event_device *evtdev;
    enum tick_device_mode mode;
};

tick device的模式:

enum tick_device_mode {
    TICKDEV_MODE_PERIODIC,
    TICKDEV_MODE_ONESHOT,
};

2.2 tick device创建

2.2.1 tick_check_new_device

tick device创建调用函数tick_check_new_device
(kernel/time/tick-internal.h)

void tick_check_new_device(struct clock_event_device *newdev)
{
    struct clock_event_device *curdev;
    struct tick_device *td;
    int cpu;

    cpu = smp_processor_id();
    td = &per_cpu(tick_cpu_device, cpu);------------得到对应cpu的tick_cpu_device(全局tick device)
    curdev = td->evtdev;

    /* cpu local device ? */
    if (!tick_check_percpu(curdev, newdev, cpu))
        goto out_bc;------------------tick device不是per cpu的,goto

    /* Preference decision */
    if (!tick_check_preferred(curdev, newdev))
        goto out_bc;------------------优先级不高的,goto

    if (!try_module_get(newdev->owner))
        return;

    /*
     * Replace the eventually existing device by the new
     * device. If the current device is the broadcast device, do
     * not give it back to the clockevents layer !
     */
    if (tick_is_broadcast_device(curdev)) {
        clockevents_shutdown(curdev);
        curdev = NULL;
    }
    clockevents_exchange_device(curdev, newdev);
    tick_setup_device(td, newdev, cpu, cpumask_of(cpu));----正式创建特定的tick device
    if (newdev->features & CLOCK_EVT_FEAT_ONESHOT)
        tick_oneshot_notify();
    return;

out_bc:
    /*
     * Can the new device be used as a broadcast device ?
     */
    tick_install_broadcast_device(newdev);------------------创建broadcast tick device
}

2.2.2 tick_setup_device

static void tick_setup_device(struct tick_device *td,
                  struct clock_event_device *newdev, int cpu,
                  const struct cpumask *cpumask)
{
    ktime_t next_event;
    void (*handler)(struct clock_event_device *) = NULL;

    /*
     * First device setup ?
     */
    if (!td->evtdev) {-------第一次进行tick device设置时进入
        /*
         * If no cpu took the do_timer update, assign it to
         * this cpu:
         */
        if (tick_do_timer_cpu == TICK_DO_TIMER_BOOT) {
            if (!tick_nohz_full_cpu(cpu))
                tick_do_timer_cpu = cpu;
            else
                tick_do_timer_cpu = TICK_DO_TIMER_NONE;
            tick_next_period = ktime_get();
            tick_period = ktime_set(0, NSEC_PER_SEC / HZ);
        }

        /*
         * Startup in periodic mode first.
         */
        td->mode = TICKDEV_MODE_PERIODIC;
    } else {---------------非第一次进行tick device设置时进入
        handler = td->evtdev->event_handler;
        next_event = td->evtdev->next_event;
        td->evtdev->event_handler = clockevents_handle_noop;
    }

    td->evtdev = newdev;

    /*
     * When the device is not per cpu, pin the interrupt to the
     * current cpu:
     */
    if (!cpumask_equal(newdev->cpumask, cpumask))
        irq_set_affinity(newdev->irq, cpumask);

    /*
     * When global broadcasting is active, check if the current
     * device is registered as a placeholder for broadcast mode.
     * This allows us to handle this x86 misfeature in a generic
     * way. This function also returns !=0 when we keep the
     * current active broadcast state for this CPU.
     */
    if (tick_device_uses_broadcast(newdev, cpu))
        return;

    if (td->mode == TICKDEV_MODE_PERIODIC)
        tick_setup_periodic(newdev, 0);--------------------创建periodic tick-device 
    else
        tick_setup_oneshot(newdev, handler, next_event);---创建oneshot  tick-device
}

2.3 periodic & oneshot tick-device

periodic和oneshot tick-device的区别在于,periodic的设备是周期性的,不用每次设置下一次中断的时间,但是one shot的设备是需要每次中断到来时,要设置下一次中断的时间的;由于这个原因,one shot的设备要更灵活或者说“精度”更高,所以一般说periodic设备是低精度设备,oneshot设备用于高精度设备。

在clock设备第一次注册的时候,一般都默认设置tick-device为periodic的,不过在后边的运行过程中,会对tick-device设备做出一些改变并设置为oneshot,这要看clock设备的支持了。

tick_setup_periodic和tick_setup_oneshot是分别设置clockevent为periodic和oneshot的,这两个函数的目的大致相同:

  1. 设置clock_event_device->event_handler
    (对于periodic是tick_handle_periodic,对于送呢oneshot是hrtimer_interrupt)
  2. 设置clock_event_device->state_use_accessors(CLOCK_EVT_STATE_PERIODIC或者CLOCK_EVT_STATE_ONESHOT)
  3. 如果是oneshot的,那么clockevents_program_event设置下次中断时间

3.change log


date content linux
2016.12.18 clockevent和tick device基本介绍 linux4.6.3
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章