// 设置clockevent周期处理函数 // 函数参数: // broadcast,指示此设备是否为全局广播设备 // 调用路径:tick_setup_periodic->tick_set_periodic_handler 1.1 void tick_set_periodic_handler(struct clock_event_device *dev, int broadcast) { if (!broadcast) dev->event_handler = tick_handle_periodic; else dev->event_handler = tick_handle_periodic_broadcast; } // clockevent周期处理函数(非广播设备) // 函数任务: // 1.执行周期任务 // 2.如果设备为单触发模式 // 2.1 重编程下一次事件到期时间 2.1 void tick_handle_periodic(struct clock_event_device *dev) { int cpu = smp_processor_id(); ktime_t next; //执行do_timer更新全局事件,更新进程时间 tick_periodic(cpu); //周期模式不需要手动设置下次到期时间,直接退出 if (dev->mode != CLOCK_EVT_MODE_ONESHOT) return; //计算下次到期时间 next = ktime_add(dev->next_event, tick_period); for (;;) { //重编程设备事件到期 if (!clockevents_program_event(dev, next, ktime_get())) return; //重新编程设备失败,说明已经经过一个tick周期,此时执行tick周期任务 if (timekeeping_valid_for_hres()) tick_periodic(cpu); //更新下次到期时间 next = ktime_add(next, tick_period); } } // 周期处理函数 // 函数任务: // 1.如果本cpu负责更新全局时间 // 1.1 执行do_timer // 2.更新进程运行时间 // 2.1 通知调度器更新其虚拟时钟 // 2.2 更新进程cpu上执行时间 // 调用路径:tick_handle_periodic->tick_periodic 2.2 static void tick_periodic(int cpu) { //本cpu负责更新全局时间 if (tick_do_timer_cpu == cpu) { write_seqlock(&xtime_lock); //计算下个周期 tick_next_period = ktime_add(tick_next_period, tick_period); //执行do_timer do_timer(1); write_sequnlock(&xtime_lock); } //更新进程时间 update_process_times(user_mode(get_irq_regs())); } // 更新全局时间 // 函数任务: // 1.更新jiffies // 2.更新墙上时间 // 3.cpu间负载均衡 // 调用路径:tick_periodic->do_timer 2.3 void do_timer(unsigned long ticks) { jiffies_64 += ticks; update_wall_time(); calc_global_load(); } // clockevent周期处理函数(广播设备) // 函数任务: // 1.执行本cpu的事件处理函数 // 2.通过ipi通知代理的cpu,执行时间处理函数 // 3.重新编程下次事件的到期时间 3.1 static void tick_handle_periodic_broadcast(struct clock_event_device *dev) { ktime_t next; //执行事件处理函数 tick_do_periodic_broadcast(); //重编程下次事件的到期时间 if (dev->mode == CLOCK_EVT_MODE_PERIODIC) return; for (next = dev->next_event; ;) { next = ktime_add(next, tick_period); if (!clockevents_program_event(dev, next, ktime_get())) return; tick_do_periodic_broadcast(); } } // 执行事件处理函数 // 函数任务: // 1.通过tick_broadcast_mask掩码获取代理的cpu // 2.执行事件处理函数 // 调用路径:tick_handle_periodic_broadcast->tick_do_periodic_broadcast 3.2 static void tick_do_periodic_broadcast(void) { raw_spin_lock(&tick_broadcast_lock); //通过tick_broadcast_mask掩码获取代理的cpu cpumask_and(to_cpumask(tmpmask), cpu_online_mask, tick_get_broadcast_mask()); //对代理的cpu执行事件处理函数 tick_do_broadcast(to_cpumask(tmpmask)); raw_spin_unlock(&tick_broadcast_lock); } // 执行事件处理函数 // 函数任务: // 1.执行本cpu的事件处理函数 // 2.通过ipi通知代理的cpu,执行事件处理函数 // 调用路径:tick_handle_periodic_broadcast->...->tick_do_broadcast 3.3 static void tick_do_broadcast(struct cpumask *mask) { int cpu = smp_processor_id(); struct tick_device *td; //检查当前cpu是否在掩码 if (cpumask_test_cpu(cpu, mask)) { //从掩码中清除本cpu cpumask_clear_cpu(cpu, mask); td = &per_cpu(tick_cpu_device, cpu); //执行事件处理函数 td->evtdev->event_handler(td->evtdev); } //检查是否有其他cpu需要被代理 if (!cpumask_empty(mask)) { //通过ipi通知其他cpu执行时钟事件处理函数 td = &per_cpu(tick_cpu_device, cpumask_first(mask)); td->evtdev->broadcast(mask); } } // x86下lapic,clockevent->broadcast函数 // 函数任务: // 通过ipi通知目标cpu执行事件处理函数 3.4 static void lapic_timer_broadcast(const struct cpumask *mask) { #ifdef CONFIG_SMP //所有需要通知的cpu均在mask掩码中 apic->send_IPI_mask(mask, LOCAL_TIMER_VECTOR); #endif }