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); }