- 注册cpufreq_driver 时设置 cpufreq_policy .
/*
* 问渠哪得清如许, 唯有源头活水来. 那么我们就从
* 这一个源头来看下 struct cpufreq_polcy 是怎么从上游源头流经到下游的.
* 在流动途中接纳了什么 ? 最终形成了什么 ?
*
* if (cpu_online(cpu))
* ret = cpufreq_online()
*
* 很显然顾名思义. cpufreq作用的对象是处于online状态的cpu. 之所以加上
* 这个 if 判断 是考虑到 cpuhotplug 机制的影响. 当然如果cpuhp状态跃迁
* 到online上来了,那么cpufreq_online 是会被再次调用的. 这是cpufreq_driver_register()
* 的职责. 看下面.
* cpufreq_register_driver(...)
* {
* .......
* ret = cpuhp_setup_state_nocalls_cpuslocked(CPUHP_AP_ONLINE_DYN,
* "cpufreq:online",
* cpuhp_cpufreq_online, // 在这里.
* cpuhp_cpufreq_offline);
* }
* ok, 现在我们知道 处于online状态的cpu会执行下面的操作. 且如果小于online状态的cpu再次处于
* online状态时,该函数会再次被调用. 这是cpuhp所包含的一个动作. 你体会到了cpuhp 设计的精妙了吗 ? 😄
*/
static int cpufreq_online(unsigned int cpu)
{
/*
* 下面给这个policy赋值有两个地方. 这涉及到了BP和AP.
* 要弄懂这个policy 你需要先明白 cpufreq_policy 与 cpus的关系.
* 一般来说. 同一个簇下的cpu是由同一个clock提供时钟输入、同一路regulator电源供电.
* 所以 只需要一个cpu管理policy 就可以了. 哈哈😄,不允许藩镇割据各自为政 . 画了一个图. 看下面
*
* for_each_cpu(j, policy->related_cpus) {
* per_cpu(cpufreq_cpu_data, j) = policy; // 在这里.
*
* 真正的策略是policy持有的 governor,还没有走到那里, 到了再说.
*/
struct cpufreq_policy *policy;
/*
* 获取该cpu上的per-cpu变量cpufreq_cpu_data. 它指向该cpu对应的 struct cpufreq_policy
* 作者意图很明显, 就是想看看该cpu 是否有一个policy来管理它.
* 因为
* 每个处于online状态的cpu都会被遍历,对于每一个cpu都执行cpufreq_online.
* cpu 为 0时, 该policy 显然为 NULL. new_policy =true
* 继续下面的逻辑:
* if (new_policy) {
* for_each_cpu(j, policy->related_cpus) {
* per_cpu(cpufreq_cpu_data, j) = policy; // 在这里
* so. cpu0新创建的cpufreq_policy来管理cluster中的所有cpu. AP们感觉岁月静好, 只不过都是BP在负重前行了.
*/
policy = per_cpu(cpufreq_cpu_data, cpu);
if (policy) {
} else {
/*
* 用bool来区分是cpu0还是cpux的执行逻辑.
*/
new_policy = true;
/*
* ok, 创建policy吧. 底层调用kzalloc(), 然后赋值初始化. 玩套路吗大哥 !
*/
policy = cpufreq_policy_alloc(cpu);
if (!policy)
return -ENOMEM;
}
}
二 : mechanism for registering utilization update callbacks
/*
* 核心来了. DL 、RT 、CFS 都会执行到该函数.
* 注意, 它是per-cpu类型的. 它也肯定要被设置为per-cpu的. cpufreq 把per-cpu机制发挥的淋漓尽致 !
* 它就是调度器与schedutil governor 之间的桥梁 ! 直接看下面, 体会一下原汁原味的意思 :
* This function is called by the scheduler on the CPU whose utilization is being updated.
* 懂了吧 ! 😄 知道了它的意思, 我看看它的参数.
*
* 1. struct rq *rq : 不用多说,通用的运行队列.
* 2. flags : 为啥要update. 通常传入 0.
*
* 为什么要传入一个通用的queue, 😄 想一下如果你传入一个cfs_rq, 它只能代表cfs啊. 传入的目的显而易见
* static inline int cpu_of(struct rq *rq)
* {
* #ifdef CONFIG_SMP
* return rq->cpu; // 在这里.
* #else
* return 0;
* #endif
* }
*/
static inline void cpufreq_update_util(struct rq *rq, unsigned int flags)
{
/*
* 牛13的设计. 里面只包含一个func. 即cpu使用率发生变化的时候的回调函数.
* ok, 现在问题来了, 该callback 是在什么时候被设置进去的.
* 别慌 哈哈😄, 它是在启动schedutil governor 的时候设置的. 这也是合理的时机.
*/
struct update_util_data *data;
/*
* ok, 根据cpuid 取出per_cpu变量了.自然的就执行data持有的func了.
* 那我们今天就聊聊 Kernel中第二大特性 RCU.
*/
data = rcu_dereference_sched(*per_cpu_ptr(&cpufreq_update_util_data,
cpu_of(rq)));
/*
* 通路不复杂, 终于再这里落地了. 通知schedutil governor了.
* governor 是否下发调频给cpufreq_driver, 还是需要做判断的, 总不能DL task 入队一次就实际调频一次吧.
* 具体再func() 中, 一会我们就看.
*/
if (data)
data->func(data, rq_clock(rq), flags);
}
/*
* 启动 schedutil governor !
* ok, 看启动的时机.
*/
static int sugov_init(struct cpufreq_policy *policy)
{
}