1. 前言
由“linux cpufreq framework(3)_cpufreq core”的描述可知,cpufreq policy负责设定cpu调频的一个大致范围,而cpu的具体运行频率,则需要由相应的cufreq governor决定(可自行调节频率的CPU除外,后面会再详细介绍)。那到底什么是cpufreq governor?它的运行机制是什么?这就是本文要描述的内容。
2. cpufreq governor的实现
2.1 struct cpufreq_governor
kernel通过struct cpufreq_governor抽象cpufreq governor,如下:
struct cpufreq_governor {
// 调速器名称,最大长度为 CPUFREQ_NAME_LEN
char name[CPUFREQ_NAME_LEN];
// 初始化调速器函数指针
int (*init)(struct cpufreq_policy *policy);
// 退出调速器函数指针
void (*exit)(struct cpufreq_policy *policy);
// 启动调速器函数指针
int (*start)(struct cpufreq_policy *policy);
// 停止调速器函数指针
void (*stop)(struct cpufreq_policy *policy);
// 更新调速器限制函数指针
void (*limits)(struct cpufreq_policy *policy);
// 显示设置频率信息函数指针
ssize_t (*show_setspeed)(struct cpufreq_policy *policy, char *buf);
// 设置频率函数指针
int (*store_setspeed)(struct cpufreq_policy *policy, unsigned int freq);
// 调速器链表节点
struct list_head governor_list;
// 调速器所属模块
struct module *owner;
// 标志位
u8 flags;
};
好的,以下是带中文注释的代码:
C
struct cpufreq_governor {
// 调速器名称,最大长度为 CPUFREQ_NAME_LEN
char name[CPUFREQ_NAME_LEN];
// 初始化调速器函数指针
int (*init)(struct cpufreq_policy *policy);
// 退出调速器函数指针
void (*exit)(struct cpufreq_policy *policy);
// 启动调速器函数指针
int (*start)(struct cpufreq_policy *policy);
// 停止调速器函数指针
void (*stop)(struct cpufreq_policy *policy);
// 更新调速器限制函数指针
void (*limits)(struct cpufreq_policy *policy);
// 显示设置频率信息函数指针
ssize_t (*show_setspeed)(struct cpufreq_policy *policy, char *buf);
// 设置频率函数指针
int (*store_setspeed)(struct cpufreq_policy *policy, unsigned int freq);
// 调速器链表节点
struct list_head governor_list;
// 调速器所属模块
struct module *owner;
// 标志位
u8 flags;
};
请谨慎使用代码。
分析:
struct cpufreq_governor 結構体定义了 CPU 频率调速器的相关信息和函数指针。
name: 用於識別调速器的名称。
init: 初始化调速器,在将调速器应用于 CPU 之前执行。
exit: 退出调速器,在不再使用调速器时执行。
start: 启动调速器,使其开始控制 CPU 频率。
stop: 停止调速器,使其不再控制 CPU 频率。
limits: 更新调速器可用的频率范围。
show_setspeed: 显示当前设置的 CPU 频率信息。
store_setspeed: 设置 CPU 频率。
governor_list: 调速器链表节点,用于将调速器添加到链表中。
owner: 调速器所属的模块。
flags: 调速器相关的标志位,用于控制調速器的行为。
这个结构体定义了 CPU 频率调速器的接口,它允许不同的调速器实现不同的算法和策略来控制 CPU 频率。
2.3 governor register
所有governor都是通过cpufreq_register_governor注册到kernel中的,该接口比较简单,查找是否有相同名称的governor已经注册,如果没有,将这个governor挂到全局的链表即可,如下:
int cpufreq_register_governor(struct cpufreq_governor *governor)
{
int err;
// 检查参数有效性
if (!governor)
return -EINVAL;
// 如果 CPU 频率控制已经被禁用,则返回错误
if (cpufreq_disabled())
return -ENODEV;
// 锁住 cpufreq_governor_mutex 互斥锁
mutex_lock(&cpufreq_governor_mutex);
// 检查调速器是否已经注册
err = -EBUSY;
if (!find_governor(governor->name)) {
// 如果调速器未注册,则将其添加到链表中
err = 0;
list_add(&governor->governor_list, &cpufreq_governor_list);
}
// 解锁 cpufreq_governor_mutex 互斥锁
mutex_unlock(&cpufreq_governor_mutex);
// 返回结果
return err;
}
EXPORT_SYMBOL_GPL(cpufreq_register_governor);
3 governor相关的调用流程
3.1 启动流程
“linux cpufreq framework(3)_cpufreq core”中介绍过,添加cpufreq设备时,会调用cpufreq_init_policy,该接口的主要功能是为当前的cpufreq policy分配并启动一个cpufreq governor,如下:
static int cpufreq_init_policy(struct cpufreq_policy *policy)
{
struct cpufreq_governor *gov = NULL;
unsigned int pol = CPUFREQ_POLICY_UNKNOWN;
int ret;
// 检查是否支持的目标频率
if (has_target()) {
// 从历史记录中恢复调速器
gov = get_governor(policy->last_governor);
if (gov) {
pr_debug("Restoring governor %s for cpu %d\n",
gov->name, policy->cpu);
} else {
gov = get_governor(default_governor);
}
// 如果找不到调速器,使用默认调速器
if (!gov) {
gov = cpufreq_default_governor();
__module_get(gov->owner);
}
} else {
// 从历史记录或默认值中选择策略
if (policy->last_policy) {
pol = policy->last_policy;
} else {
pol = cpufreq_parse_policy(default_governor);
if (pol == CPUFREQ_POLICY_UNKNOWN)
pol = policy->policy;
}
// 验证策略有效性
if (pol != CPUFREQ_POLICY_PERFORMANCE &&
pol != CPUFREQ_POLICY_POWERSAVE)
return -ENODATA;
}
// 设置策略和调速器
ret = cpufreq_set_policy(policy, gov, pol);
// 释放引用计数
if (gov)
module_put(gov->owner);
return ret;
}
/**
* cpufreq_set_policy - 修改 cpufreq 策略参数
* @policy: 要修改的策略对象
* @new_gov: 新的策略调速器指针
* @new_pol: 策略值(针对带有内置调速器的驱动程序)
*
* 调用 cpufreq 驱动程序的 ->verify() 回调函数来对要设置的频率限制进行合理性检查,
* 使用验证后的限制值更新 @policy,并在驱动程序有 ->setpolicy() 回调函数的情况下调用它,
* 否则对 @policy 执行调速器更新。
* 调速器更新包括:
* - 如果 @new_gov 指向与 @policy 中相同的对象,则运行当前调速器的 ->limits() 回调函数。
* - 否则,用 @new_gov 替换 @policy 的调速器。
*
* 该函数不会更新 @policy 的 cpuinfo 部分。
*/
static int cpufreq_set_policy(struct cpufreq_policy *policy,
struct cpufreq_governor *new_gov,
unsigned int new_pol)
{
struct cpufreq_policy_data new_data; // 新策略数据
struct cpufreq_governor *old_gov; // 旧调速器指针
int ret;
// 复制部分策略数据到新的结构体中
memcpy(&new_data.cpuinfo, &policy->cpuinfo, sizeof(policy->cpuinfo));
new_data.freq_table = policy->freq_table;
new_data.cpu = policy->cpu;
// 从 PM QoS 框架获取最小和最大频率限制
new_data.min = freq_qos_read_value(&policy->constraints, FREQ_QOS_MIN);
new_data.max = freq_qos_read_value(&policy->constraints, FREQ_QOS_MAX);
// 打印调试信息
pr_debug("setting new policy for CPU %u: %u - %u kHz\n",
new_data.cpu, new_data.min, new_data.max);
// 调用驱动程序的 verify 回调函数来验证频率限制并确保 min <= max
ret = cpufreq_driver->verify(&new_data);
if (ret)
return ret;
// 将新的最小和最大频率限制写入策略对象
policy->min = new_data.min;
policy->max = new_data.max;
policy->min = __resolve_freq(policy, policy->min, CPUFREQ_RELATION_L);
policy->max = __resolve_freq(policy, policy->max, CPUFREQ_RELATION_H);
trace_cpu_frequency_limits(policy);
// 清除缓存的目标频率
policy->cached_target_freq = UINT_MAX;
// 打印调试信息
pr_debug("new min and max freqs are %u - %u kHz\n",
policy->min, policy->max);
// 如果驱动程序有 setpolicy 回调函数,则调用它来设置频率范围
if (cpufreq_driver->setpolicy) {
policy->policy = new_pol;
pr_debug("setting range\n");
return cpufreq_driver->setpolicy(policy);
}
// 如果新调速器与当前调速器相同,则只需更新调速器的限制
if (new_gov == policy->governor) {
pr_debug("governor limits update\n");
cpufreq_governor_limits(policy);
return 0;
}
// 打印调试信息
pr_debug("governor switch\n");
// 保存旧的调速器指针
old_gov = policy->governor;
// 停止旧的调速器
if (old_gov) {
cpufreq_stop_governor(policy);
cpufreq_exit_governor(policy);
}
// 启动新的调速
3.2 调频流程
前面已经多次提到基于cpufreq governor的调频思路,这里再总结一下:
1)有两种类型的cpu:一种只需要给定调频范围,cpu会在该范围内自行确定运行频率;另一种需要软件指定具体的运行频率。
2)对第一种cpu,cpufreq policy中会指定频率范围policy->{min, max},之后通过setpolicy接口,使其生效即可。
3)对第二种cpu,cpufreq policy在指定频率范围的同时,会指明使用的governor。governor在启动后,会动态的(例如启动一个timer,监测系统运行情况,并根据负荷调整频率),或者静态的(直接设置为某一个合适的频率值),设定cpu运行频率。
kernel document对这个过程有详细的解释,如下:
https://www.kernel.org/doc/Documentation/cpu-freq/governors.txt
4 常用的governor介绍
最后,我们介绍一下kernel中常见的cpufreq governor。
1)Performance
性能优先的governor,直接将cpu频率设置为policy->{min,max}中的最大值。
2)Powersave
功耗优先的governor,直接将cpu频率设置为policy->{min,max}中的最小值。
3)Userspace
由用户空间程序通过scaling_setspeed文件修改频率。
4)Ondemand
根据CPU的当前使用率,动态的调节CPU频率。
5)Conservative
类似Ondemand,不过频率调节的会平滑一下,不会忽然调整为最大值,又忽然调整为最小值。