时间子系统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设备状态
// 调用路径: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);
}
最后更新:2017-04-03 14:54:29