linux cpufreq framework(4)_cpufreq governor

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,不过频率调节的会平滑一下,不会忽然调整为最大值,又忽然调整为最小值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值