Chinaunix首页 | 论坛 | 博客
  • 博客访问: 327125
  • 博文数量: 100
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 665
  • 用 户 组: 普通用户
  • 注册时间: 2015-02-02 12:43
文章分类

全部博文(100)

文章存档

2015年(100)

我的朋友

分类: LINUX

2015-05-18 11:02:59

//	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);
}
阅读(1817) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~