时间子系统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;
}
时间: 2024-09-28 12:05:59

时间子系统12_clockevent设备注册的相关文章

时间子系统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设备状态 /

Linux时间子系统之八:动态时钟框架(CONFIG_NO_HZ、tickless)【转】

转自:http://blog.csdn.net/droidphone/article/details/8112948 版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[-] 数据结构 低分辨率下的动态时钟 1  切换至动态时钟模式 2  低分辨率动态时钟下的事件中断处理函数 3  动态时钟停止周期tick时钟事件 3  动态时钟重新开启周期tick时钟事件 高精度模式下的动态时钟 动态时钟对中断的影响   在前面章节的讨论中,我们一直基于一个假设:Linux中的时钟事件都是由

虚拟化-Xenbus设备注册实现过程

问题描述 Xenbus设备注册实现过程 我想要实现自定义一个虚拟设备,注册到Xenbus上,过程包括将设备写入Xenstore,将设备注册到Xenbus,将设备驱动注册到Xenbus,实现前后端驱动连接. 在追踪代码的工程中,始终无法理解Xenbus是如何实现设备注册的?是Xenbus检测到Xenstore上存在该设备就去自动执行注册函数吗?还是在设备文件中执行注册? 非常感谢您的指导! 解决方案 驱动的注册过程和设备的注册过程(转载)设备驱动注册过程PowerPC平台,norflash及nan

Linux时间子系统之七:定时器的应用--msleep(),hrtimer_nanosleep()【转】

  转自:http://blog.csdn.net/droidphone/article/details/8104433 我们已经在前面几章介绍了低分辨率定时器和高精度定时器的实现原理,内核为了方便其它子系统,在时间子系统中提供了一些用于延时或调度的API,例如msleep,hrtimer_nanosleep等等,这些API基于低分辨率定时器或高精度定时器来实现,本章的内容就是讨论这些方便.好用的API是如何利用定时器系统来完成所需的功能的. /*************************

I.MX6 sdio 设备注册及识别

/************************************************************************* * I.MX6 sdio 设备注册及识别 * 说明: * 大多数时候如果驱动都识别不到硬件设备,这就很艰难了,能识别到设备, * 后续才有得发挥. * * 2016-6-12 深圳 南山平山村 曾剑锋 ************************************************************************/

时间子系统17_hard lockup机制

// 使能hard lockup探测 // 调用路径:watchdog_enable->watchdog_nmi_enable // 函数任务: // 1.初始化hard lockup检测事件 // 2.hard lockup阈值为10s // 2.向performance monitoring子系统注册hard lockup检测事件 // 3.使能hard lockup检测事件 // 注: // performance monitoring,x86中的硬件设备,当cpu clock经过了指定个

网络子系统5_设备队列规则

// 1.设备描述符与队列规则相关的字段: // 1.1 dev->tx_queue_len 指示驱动程序规则队列的队列长度,在注册设备时使用,通知核心是否为设备提供队列规则机制. // 1.1.1 不适用队列规则=0 // 1.1.2 使用队列规则>0 // 1.2 dev->qdisc,执行设备传输时,qdisc_run,dev_queue_xmit始终通过该字段获取设备当前使用的队列规则. // 1.3 dev->qdisc_sleep, 保存设备具备传输能力时,使用的设备队

时间子系统10_hpet时钟初始化

// 时钟mult :mult/2^shift = ns/cyc // 参考:http://www.bluezd.info/archives/reg_clock_event_device_1 // x86平台初始化 // 注:arch/x86/kernel/x86_init.c 1.1 struct x86_init_ops x86_init __initdata = { ... //时钟初始化 .timers = { .setup_percpu_clockev = setup_boot_API

Linux时间子系统之一:clock source(时钟源)【转】

转自:http://blog.csdn.net/droidphone/article/details/7975694 clock source用于为linux内核提供一个时间基线,如果你用linux的date命令获取当前时间,内核会读取当前的clock source,转换并返回合适的时间单位给用户空间.在硬件层,它通常实现为一个由固定时钟频率驱动的计数器,计数器只能单调地增加,直到溢出为止.时钟源是内核计时的基础,系统启动时,内核通过硬件RTC获得当前时间,在这以后,在大多数情况下,内核通过选定