时间子系统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