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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章