// 向系统注册时钟事件设备 // 函数任务: // 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