時間子係統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