時間子係統2_tick device管理機製
// 1.每個cpu都具有一個tick_device,維護周期時鍾。
// 2.tick_device依賴一個clockevent設備,提供周期事件。
// 3.cpu電源狀態的改變會影響tick_device,通過tick_notifier監聽電源狀態。
// 4.全局廣播設備接管進入省電模式cpu的周期時間維護。
// 4.broadcast_mask保存開啟廣播模式的cpu, broadcast_oneshot_mask保存進入省電模式的cpu。
// 監聽clockevent設備狀態
// 調用路徑:start_kernel->tick_init
1.1 void __init tick_init(void)
{
clockevents_register_notifier(&tick_notifier);
}
// 監聽clockevent設備,cpu的狀態變化
1.2 static int tick_notify(struct notifier_block *nb, unsigned long reason,
void *dev)
{
switch (reason) {
//新eventclock設備注冊
case CLOCK_EVT_NOTIFY_ADD:
return tick_check_new_device(dev);
//cpu開啟\關閉廣播模式
case CLOCK_EVT_NOTIFY_BROADCAST_ON:
case CLOCK_EVT_NOTIFY_BROADCAST_OFF:
case CLOCK_EVT_NOTIFY_BROADCAST_FORCE:
tick_broadcast_on_off(reason, dev);
break;
//cpu進入\離開廣播模式
case CLOCK_EVT_NOTIFY_BROADCAST_ENTER:
case CLOCK_EVT_NOTIFY_BROADCAST_EXIT:
tick_broadcast_oneshot_control(reason);
break;
//選擇do_timer的cpu
case CLOCK_EVT_NOTIFY_CPU_DYING:
tick_handover_do_timer(dev);
break;
//停用dead cpu的clockevent
case CLOCK_EVT_NOTIFY_CPU_DEAD:
tick_shutdown_broadcast_oneshot(dev);
tick_shutdown_broadcast(dev);
tick_shutdown(dev);
break;
//掛起cpu的clockevent
case CLOCK_EVT_NOTIFY_SUSPEND:
tick_suspend();
tick_suspend_broadcast();
break;
//恢複cpu的clockevent
case CLOCK_EVT_NOTIFY_RESUME:
tick_resume();
break;
default:
break;
}
return NOTIFY_OK;
}
// 向clockevents_chain通知clockevents事件
// 函數主要任務:
// 1.通知clockevents_chain
// 2.當cpu offline時,清空clockevents_released鏈表,刪除與此cpu相關的clockevent
// 注:clockevents_released保存所有fail-to-add/replace-out的clockevent
// clockevent_devices保存所有有效的clockevent
1.3 void clockevents_notify(unsigned long reason, void *arg)
{
struct clock_event_device *dev, *tmp;
unsigned long flags;
int cpu;
raw_spin_lock_irqsave(&clockevents_lock, flags);
//封裝標準通知鏈操作raw_notifier_call_chain
clockevents_do_notify(reason, arg);
switch (reason) {
case CLOCK_EVT_NOTIFY_CPU_DEAD:
//clockevents_released用於保存所有fail-to-add/replace-out的clockevent
//當cpu offline後,清空此鏈表
list_for_each_entry_safe(dev, tmp, &clockevents_released, list)
list_del(&dev->list);
//刪除與此cpu相關的clockevent
cpu = *((int *)arg);
list_for_each_entry_safe(dev, tmp, &clockevent_devices, list) {
if (cpumask_test_cpu(cpu, dev->cpumask) &&
cpumask_weight(dev->cpumask) == 1 &&
!tick_is_broadcast_device(dev)) {
BUG_ON(dev->mode != CLOCK_EVT_MODE_UNUSED);
list_del(&dev->list);
}
}
break;
default:
break;
}
raw_spin_unlock_irqrestore(&clockevents_lock, flags);
}
// cpu開啟\關閉廣播模式
// 調用路徑:tick_notify->tick_broadcast_on_off
// 注:
// 1.加入、退出廣播組指針對online的cpu
2.1 void tick_broadcast_on_off(unsigned long reason, int *oncpu)
{
//忽略offline的cpu
if (!cpumask_test_cpu(*oncpu, cpu_online_mask))
printk(KERN_ERR "tick-broadcast: ignoring broadcast for "
"offline CPU #%d\n", *oncpu);
else
tick_do_broadcast_on_off(&reason);
}
// cpu開啟\關閉廣播模式
// 函數主要任務:
// 1.開啟廣播模式
// 1.1 將cpu加入廣播組bitmap中
// 1.2 如果廣播設備為周期觸發模式,關閉此clockevent
// 2.關閉廣播模式
// 2.1 從廣播組bitmap中刪除cpu
// 2.2 如果廣播設備為周期觸發模式,恢複其clockevent
// 3.如果廣播組為空
// 3.1 停止全局廣播設備
// 4.否則
// 4.1 設置全局廣播設備為單觸發模式或周期模式
// 注:
// 1.全局廣播設備tick_broadcast_device
// 2.每個cpu有各自的tick_device,選擇其中一個充當全局廣播設備
// 3.加入廣播組的clockevent需要關閉其周期觸發模式
2.2 static void tick_do_broadcast_on_off(unsigned long *reason)
{
struct clock_event_device *bc, *dev;
struct tick_device *td;
unsigned long flags;
int cpu, bc_stopped;
raw_spin_lock_irqsave(&tick_broadcast_lock, flags);
cpu = smp_processor_id();
//per cpu的tick device
td = &per_cpu(tick_cpu_device, cpu);
dev = td->evtdev;
//全局廣播設備
bc = tick_broadcast_device.evtdev;
//設備不受電源狀態的影響
if (!dev || !(dev->features & CLOCK_EVT_FEAT_C3STOP))
goto out;
//廣播組是否為空
bc_stopped = cpumask_empty(tick_get_broadcast_mask());
switch (*reason) {
//開啟cpu的廣播模式
case CLOCK_EVT_NOTIFY_BROADCAST_ON:
case CLOCK_EVT_NOTIFY_BROADCAST_FORCE:
//當前cpu不在廣播組中
if (!cpumask_test_cpu(cpu, tick_get_broadcast_mask())) {
//將cpu加入廣播組
cpumask_set_cpu(cpu, tick_get_broadcast_mask());
//關閉周期觸發模式的clockevent設備
if (tick_broadcast_device.mode ==
TICKDEV_MODE_PERIODIC)
clockevents_shutdown(dev);
}
break;
//關閉cpu的廣播模式
case CLOCK_EVT_NOTIFY_BROADCAST_OFF:
if (!tick_broadcast_force &&
//從廣播組中清除該設備
cpumask_test_cpu(cpu, tick_get_broadcast_mask())) {
cpumask_clear_cpu(cpu, tick_get_broadcast_mask());
//恢複該設備的周期觸發模式
if (tick_broadcast_device.mode ==
TICKDEV_MODE_PERIODIC)
tick_setup_periodic(dev, 0);
}
break;
}
//刪除了廣播組中最後一個設備
if (cpumask_empty(tick_get_broadcast_mask())) {
if (!bc_stopped)
{ //停止全局廣播設備
clockevents_shutdown(bc);
}
} else if (bc_stopped) {
if (tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC)
tick_broadcast_start_periodic(bc);
else
tick_broadcast_setup_oneshot(bc);
}
out:
raw_spin_unlock_irqrestore(&tick_broadcast_lock, flags);
}
// cpu進入\離開廣播模式
// 當cpu進入省電模式時,由全局廣播設備接管cpu的時間維護
// 調用路徑:tick_notify->tick_broadcast_oneshot_control
// 函數主要任務:
// 1.cpu進入省電模式,由全局廣播設備接管其時間維護
// 1.1 設置cpu關閉狀態,更新全局廣播設備的到期時間
// 2.cpu離開省電模式,全局廣播設備不再接管其時間維護
// 2.1 更新全局廣播設備的到期時間
// 注:
// cpu在進入省電模式時,進入廣播模式
// cpu在離開省電模式時,退出廣播模式
3.1 void tick_broadcast_oneshot_control(unsigned long reason)
{
struct clock_event_device *bc, *dev;
struct tick_device *td;
unsigned long flags;
int cpu;
raw_spin_lock_irqsave(&tick_broadcast_lock, flags);
//電源狀態變化不影響周期模式的全局廣播設備
if (tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC)
goto out;
bc = tick_broadcast_device.evtdev;
cpu = smp_processor_id();
td = &per_cpu(tick_cpu_device, cpu);
dev = td->evtdev;
//cpu的clockevent設備不受電源狀態的影響
if (!(dev->features & CLOCK_EVT_FEAT_C3STOP))
goto out;
//cpu進入省電模式,由全局廣播設備接管其tick device的任務
if (reason == CLOCK_EVT_NOTIFY_BROADCAST_ENTER) {
//設置cpu關閉狀態
if (!cpumask_test_cpu(cpu, tick_get_broadcast_oneshot_mask())) {
cpumask_set_cpu(cpu, tick_get_broadcast_oneshot_mask());
clockevents_set_mode(dev, CLOCK_EVT_MODE_SHUTDOWN);
//cpu下一個事件的到期時間小於全局廣播設備下一個事件的到期時間,設置cpu在下一個周期到期
if (dev->next_event.tv64 < bc->next_event.tv64)
tick_broadcast_set_event(dev->next_event, 1);
}
//cpu離開省電模式,全局廣播設備不再接管其tick device的任務
} else {
//清除cpu關閉狀態
if (cpumask_test_cpu(cpu, tick_get_broadcast_oneshot_mask())) {
cpumask_clear_cpu(cpu, tick_get_broadcast_oneshot_mask());
clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT);
//重新編程cpu的tick device在下一個周期到期
if (dev->next_event.tv64 != KTIME_MAX)
tick_program_event(dev->next_event, 1);
}
}
out:
raw_spin_unlock_irqrestore(&tick_broadcast_lock, flags);
}
// 選擇do_timer cpu
// 調用路徑:tick_notify->tick_handover_do_timer
4.1 static void tick_handover_do_timer(int *cpup)
{
//如果dying cpu負責do_timer,重新選取online cpu中第一個cpu負責
if (*cpup == tick_do_timer_cpu) {
int cpu = cpumask_first(cpu_online_mask);
tick_do_timer_cpu = (cpu < nr_cpu_ids) ? cpu :
TICK_DO_TIMER_NONE;
}
}
// dead cpu的clockevent的處理:
// 1.從oneshot掩碼中刪除cpu
// 2.從broadcast掩碼中刪除cpu
// 3.關閉clockevent設備
// 3.1 設置該cpu tick device的clockevent為null
// 3.2 將clockevent設備鏈入clockevents_released
// 從oneshot掩碼中刪除cpu
// 調用路徑:tick_notify->tick_shutdown_broadcast_oneshot
5.1 void tick_shutdown_broadcast_oneshot(unsigned int *cpup)
{
unsigned long flags;
unsigned int cpu = *cpup;
raw_spin_lock_irqsave(&tick_broadcast_lock, flags);
//從oneshot掩碼中刪除dead cpu
cpumask_clear_cpu(cpu, tick_get_broadcast_oneshot_mask());
raw_spin_unlock_irqrestore(&tick_broadcast_lock, flags);
}
// 從broadcast掩碼中刪除cpu
// 調用路徑:tick_notify->tick_shutdown_broadcast
5.2 void tick_shutdown_broadcast(unsigned int *cpup)
{
struct clock_event_device *bc;
unsigned long flags;
unsigned int cpu = *cpup;
raw_spin_lock_irqsave(&tick_broadcast_lock, flags);
//從broadcast掩碼中刪除dead cpu
bc = tick_broadcast_device.evtdev;
cpumask_clear_cpu(cpu, tick_get_broadcast_mask());
//廣播組為空,停止全局廣播廣播設備
if (tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC) {
if (bc && cpumask_empty(tick_get_broadcast_mask()))
clockevents_shutdown(bc);
}
raw_spin_unlock_irqrestore(&tick_broadcast_lock, flags);
}
// 關閉cpu的clockevent設備
// 調用路徑:tick_notify->tick_shutdown
5.3 static void tick_shutdown(unsigned int *cpup)
{
struct tick_device *td = &per_cpu(tick_cpu_device, *cpup);
struct clock_event_device *dev = td->evtdev;
unsigned long flags;
raw_spin_lock_irqsave(&tick_device_lock, flags);
td->mode = TICKDEV_MODE_PERIODIC;
//標記給定cpu的clockevent設備為不可用
if (dev) {
dev->mode = CLOCK_EVT_MODE_UNUSED;
//將clockevent鏈接到clockevents_released
clockevents_exchange_device(dev, NULL);
td->evtdev = NULL;
}
raw_spin_unlock_irqrestore(&tick_device_lock, flags);
}
// 掛起cpu的clockevent
// 調用路徑:tick_notify->tick_suspend
6.1 static void tick_suspend(void)
{
struct tick_device *td = &__get_cpu_var(tick_cpu_device);
unsigned long flags;
//關閉cpu tick device的clockevent
raw_spin_lock_irqsave(&tick_device_lock, flags);
clockevents_shutdown(td->evtdev);
raw_spin_unlock_irqrestore(&tick_device_lock, flags);
}
// 恢複cpu的clockevent
// 調用路徑:tick_notify->tick_resume
7.1 static void tick_resume(void)
{
struct tick_device *td = &__get_cpu_var(tick_cpu_device);
unsigned long flags;
//恢複廣播
int broadcast = tick_resume_broadcast();
raw_spin_lock_irqsave(&tick_device_lock, flags);
clockevents_set_mode(td->evtdev, CLOCK_EVT_MODE_RESUME);
//恢複clockevent的觸發模式
if (!broadcast) {
if (td->mode == TICKDEV_MODE_PERIODIC)
tick_setup_periodic(td->evtdev, 0);
else
tick_resume_oneshot();
}
raw_spin_unlock_irqrestore(&tick_device_lock, flags);
}
最後更新:2017-04-03 14:54:29