在驅動中,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的,這兩個函數的目的大致相同:
- 設置clock_event_device->event_handler
(對於periodic是tick_handle_periodic,對於送呢oneshot是hrtimer_interrupt) - 設置clock_event_device->state_use_accessors(CLOCK_EVT_STATE_PERIODIC或者CLOCK_EVT_STATE_ONESHOT)
- 如果是oneshot的,那麼clockevents_program_event設置下次中斷時間
3.change log
date | content | linux |
---|---|---|
2016.12.18 | clockevent和tick device基本介紹 | linux4.6.3 |