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


時間子係統12_clockevent設備注冊

//	向係統注冊時鍾事件設備
//	函數任務:
//		1.添加設備到clockevent_devices鏈表
//		2.通知tick device管理機製有clockevent設備注冊
1.1 void clockevents_register_device(struct clock_event_device *dev)
{
	unsigned long flags;

	raw_spin_lock_irqsave(&clockevents_lock, flags);
	//將設備添加到clockevent_devices鏈表
	list_add(&dev->list, &clockevent_devices);
	//向係統通知有新設備注冊
	clockevents_do_notify(CLOCK_EVT_NOTIFY_ADD, dev);
	raw_spin_unlock_irqrestore(&clockevents_lock, flags);
}

//	clockevent設備注冊處理函數
//	調用路徑:tick_notify->CLOCK_EVT_NOTIFY_ADD
//	函數任務:
//		1.確認設備服務於本cpu
//		2.如果設備非本cpu的local device
//			2.1 設置cpu的irq親和性
//			2.2 如果本cpu已經綁定一個local device,並且二者服務的cpu相同,則忽略
//		3.如果本cpu已有local device,選擇兩者中更好的
//			3.1 優先選擇支持單觸發的
//			3.2 然後選擇高rating的
//		4.如果本cpu使用的clockevent設備為廣播設備,直接關閉
//		5.更新cpu的tick device為新tick device
//		6.啟動tick device
//		7.異步通知周期時鍾關於時鍾事件設備的改變

//	注:如果不能設置為本cpu tick device的事件源,考慮其是否可以作為廣播設備。
1.2 static int tick_check_new_device(struct clock_event_device *newdev)
{
	struct clock_event_device *curdev;
	struct tick_device *td;
	int cpu, ret = NOTIFY_OK;
	unsigned long flags;

	raw_spin_lock_irqsave(&tick_device_lock, flags);
	//檢查設備是否服務於本cpu
	cpu = smp_processor_id();
	if (!cpumask_test_cpu(cpu, newdev->cpumask))
		goto out_bc;
	//當前cpu的tick_device
	td = &per_cpu(tick_cpu_device, cpu);
	curdev = td->evtdev;
	//檢查是否為本cpu的local device
	//	如果設備隻服務於本cpu,則為local device
	if (!cpumask_equal(newdev->cpumask, cpumask_of(cpu))) {

		//非local device,並且不能設置irq 親和性,忽略
		if (!irq_can_set_affinity(newdev->irq))
			goto out_bc;
		//如果本cpu已經綁定有一個local device,則忽略
		if (curdev && cpumask_equal(curdev->cpumask, cpumask_of(cpu)))
			goto out_bc;
	}
	//本cpu已有一個tick device
	if (curdev) {
		//選擇兩者中最好的tick device
		//優先選擇 支持單觸發模式
		if ((curdev->features & CLOCK_EVT_FEAT_ONESHOT) &&
		    !(newdev->features & CLOCK_EVT_FEAT_ONESHOT))
			goto out_bc;
		//然後選擇 高rating
		if (curdev->rating >= newdev->rating)
			goto out_bc;
	}

	//如果當前使用的clockevent設備為廣播設備,直接關閉
	if (tick_is_broadcast_device(curdev)) {
		clockevents_shutdown(curdev);
		curdev = NULL;
	}
	//設置cpu的tick device為新tick device
	clockevents_exchange_device(curdev, newdev);
	//啟動tick device
	tick_setup_device(td, newdev, cpu, cpumask_of(cpu));
	//異步通知周期時鍾關於時鍾事件設備的改變
	if (newdev->features & CLOCK_EVT_FEAT_ONESHOT)
		tick_oneshot_notify();

	raw_spin_unlock_irqrestore(&tick_device_lock, flags);
	return NOTIFY_STOP;

out_bc:
	//嚐試設置clockevent設備為tick device
	if (tick_check_broadcast_device(newdev))
		ret = NOTIFY_STOP;
	raw_spin_unlock_irqrestore(&tick_device_lock, flags);

	return ret;
}

//	綁定tick device到clockevnet設備
//	函數任務:
//		1.tick device未綁定過clockevent
//			1.1 選擇cpu負責更新全局時間
//			1.2 以周期模式啟動
//		2.tick device綁定過clockevnet
//			2.1 暫時設置其eventhandler=noop,保留下次到期時間
//		3.綁定tick device到新clockevent
//		4.clockevent並不僅服務於本cpu,設置irq親和本cpu
//		5.如果設備沒有正常工作,將設備加入廣播組,然後退出
//		6.否則更新clockevent的事件處理函數
//			6.1 首次綁定,以周期模式啟動
//			6.2 否則按照之前的模式啟動
//	調用路徑:tick_check_new_device->tick_setup_device

//	注:
//		1.由負責更新全局時間的cpu,初始化tick_next_period,tick_period
//			tick_next_period, 周期時鍾下次到期的時間
//			tick_period,周期時鍾的周期,1HZ經曆的納秒
1.2 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;
	//tick device第一次創建,沒有綁定clockevent
	if (!td->evtdev) {
		//如果沒有cpu負責do_timer,由本cpu負責
		if (tick_do_timer_cpu == TICK_DO_TIMER_BOOT) {
			tick_do_timer_cpu = cpu;
			tick_next_period = ktime_get();
			tick_period = ktime_set(0, NSEC_PER_SEC / HZ);
		}
		//以周期模式啟動
		td->mode = TICKDEV_MODE_PERIODIC;
	} else {
		//tick device非第一次創建,此次為更新clockevent
		//更新event處理函數為noop
		handler = td->evtdev->event_handler;
		next_event = td->evtdev->next_event;
		td->evtdev->event_handler = clockevents_handle_noop;
	}
	//更新tick device的clockevent
	td->evtdev = newdev;
	//clockevent並不僅服務於本cpu,設置irq與本cpu親和
	if (!cpumask_equal(newdev->cpumask, cpumask))
		irq_set_affinity(newdev->irq, cpumask);

	//如果設備沒有正常工作,將設備加入廣播組,然後退出
	if (tick_device_uses_broadcast(newdev, cpu))
		return;
	//設置clockevent的eventhandler
	if (td->mode == TICKDEV_MODE_PERIODIC)
	{
		//以周期模式啟動
		tick_setup_periodic(newdev, 0);
	}	
	else
	{
		//以單觸發模式啟動
		tick_setup_oneshot(newdev, handler, next_event);
	}
		
} 

//	周期模式啟動設備
//	調用路徑:tick_check_new_device->tick_setup_device->tick_setup_periodic
//	函數任務:
//		1.設置設備周期處理函數
//		2.根據設備支持的模式,激活設備
//			2.1 如果設備支持周期模式,並且為激活單觸發模式
//				2.1.1 設置設備當前為周期模式,退出
//				2.1.2 由周期處理函數執行周期任務
//			2.2 否則,通過單觸發模式模擬周期運行
//				2.2.1 獲取下次tick到期的時間
//				2.2.2 編程設備下一次到期時間
//				2.2.3 由周期處理函數執行周期任務,更新下次到期時間
1.3 void tick_setup_periodic(struct clock_event_device *dev, int broadcast)
{
	//設置clockevent的周期處理函數
	tick_set_periodic_handler(dev, broadcast);

	//設備不能正常啟動,則有可能broadcast代其運行
	if (!tick_device_is_functional(dev))
		return;
	//如果設備支持周期模式,並且沒有激活單觸發模式
	if ((dev->features & CLOCK_EVT_FEAT_PERIODIC) &&
	    !tick_broadcast_oneshot_active()) 
	{
		//設置設備為周期模式
		clockevents_set_mode(dev, CLOCK_EVT_MODE_PERIODIC);
	} else 
	{

		unsigned long seq;
		ktime_t next;
		//獲取下次tick到期的時間
		do {
			seq = read_seqbegin(&xtime_lock);
			next = tick_next_period;
		} while (read_seqretry(&xtime_lock, seq));
		//設置設備為單觸發模式
		clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT);
		//設置設備下一次到期時間
		for (;;) {
			if (!clockevents_program_event(dev, next, ktime_get()))
				return;
			next = ktime_add(next, tick_period);
		}
	}
}
//	單觸發模式啟動設備
//	調用路徑:tick_check_new_device->tick_setup_device->tick_setup_oneshot
//	函數任務:
//		1.設置事件處理函數
//		2.設置設備當前為單觸發模式
//		3.更新設備事件的到期時間
1.4 void tick_setup_oneshot(struct clock_event_device *newdev,
			void (*handler)(struct clock_event_device *),
			ktime_t next_event)
{
	//設置事件處理函數
	newdev->event_handler = handler;
	clockevents_set_mode(newdev, CLOCK_EVT_MODE_ONESHOT);
	//更新設備事件到期時間
	tick_dev_program_event(newdev, next_event, 1);
}

//	設置設備為廣播設備
//	函數任務:
//		1.成為廣播設備的條件
//			1.1 設備不受電源狀態的影響
//			1.2 設備的rating高於當前使用的廣播設備
//		2.綁定此設備到全局廣播設備
//		3.如果有設備被代理
//			3.1 開啟廣播設備
2.1 int tick_check_broadcast_device(struct clock_event_device *dev)
{
	//檢查是否可以作為廣播設備
	if ((tick_broadcast_device.evtdev &&
	     tick_broadcast_device.evtdev->rating >= dev->rating) ||
	     (dev->features & CLOCK_EVT_FEAT_C3STOP))
		return 0;

	clockevents_exchange_device(NULL, dev);
	tick_broadcast_device.evtdev = dev;
	//有設備需要被廣播設備代理
	if (!cpumask_empty(tick_get_broadcast_mask()))
	{
		//啟動廣播設備
		tick_broadcast_start_periodic(dev);
	}
	return 1;
}
//	開啟廣播設備
//	函數任務:
//		1.設置廣播設備的事件處理函數
//		2.編程啟動廣播設備
//	調用路徑:tick_check_new_device->tick_broadcast_start_periodic
2.2 static void tick_broadcast_start_periodic(struct clock_event_device *bc)
{
	if (bc)
		tick_setup_periodic(bc, 1);
}

//	將cpu加入廣播組
//	函數任務:
//		1.如果cpu的tick device沒有正常工作
//			1.1 設置tick device的事件處理函數為周期事件處理函數
//			1.2 將設備加入廣播組
//			1.3 啟動廣播設備
//		2.否則,如果設備不受省電模式C3的影響
//			2.1 將cpu從廣播組中刪除
3.1 int tick_device_uses_broadcast(struct clock_event_device *dev, int cpu)
{
	unsigned long flags;
	int ret = 0;

	raw_spin_lock_irqsave(&tick_broadcast_lock, flags);

	if (!tick_device_is_functional(dev)) {
		dev->event_handler = tick_handle_periodic;
		cpumask_set_cpu(cpu, tick_get_broadcast_mask());
		tick_broadcast_start_periodic(tick_broadcast_device.evtdev);
		ret = 1;
	} else {
		//如果本設備不受省電模式C3的影響
		if (!(dev->features & CLOCK_EVT_FEAT_C3STOP)) {
			int cpu = smp_processor_id();
			//將cpu從廣播組中刪除
			cpumask_clear_cpu(cpu, tick_get_broadcast_mask());
			tick_broadcast_clear_oneshot(cpu);
		}
	}
	raw_spin_unlock_irqrestore(&tick_broadcast_lock, flags);
	return ret;
}

最後更新:2017-04-03 14:54:38

  上一篇:go 手機衛士12-號碼歸屬地查詢
  下一篇:go 初學Scala(1): Scala實現Hash Join