閱讀671 返回首頁    go 阿裏雲 go 技術社區[雲棲]


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

  上一篇:go ubuntu下查找某個軟件的安裝位置,配置文件
  下一篇:go 時間子係統6_高分辨率定時器框架初始化