阅读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