Linux cpufreq子系统
目录
文章目录
前言
修订记录
| 日期 | 作者 | 版本 | 修改说明 |
|---|---|---|---|
| 2023.08.15 | 枫潇潇 | V1.0.0 | 初始版本 |
1 Linux cpufreq概述
Cpufreq framework的功能称作动态电压/频率调整(Dynamic Voltage/Frequency Scaling, DVFS)。通过调整CPU的电压和频率,可以在功耗和性能之间找到一个平衡点。在不需要高性能时,降低电压和频率,以降低功耗;在需要高性能时,提高电压和频率,以提高性能。要达到此目的,有两个关键点:
- 1)如何控制CPU core的电压和频率;
- 2)何时改变CPU core的电压和频率。
针对这两个关键点,CPU core有两种实现。
-
实现1:CPU core根据自身的负荷,自动调整电压和频率,不需要OS级别的软件参与。
这种实现,软件复杂度非常低,通常情况下,只需要告诉CPU core电压和频率的调整范围(通过频率表示,scaling_min_freq和scaling_max_freq,也称作policy),CPU core即可自行调整。因此:
关键点1,由CPU core自行处理;
关键点2,OS需要根据大致的应用场景(例如,是高性能场景,还是低性能场景),设定一个频率范围,改变时机,由CPU core自行决定。
注1:由于软件参与度小,该实现的省电效率可能较低 -
实现2:CPU core不参与任何的逻辑动作,由OS软件根据系统运行情况,调整电压和频率。
这种实现,几乎完全由软件掌控DVFS行为:
关键点1,基于clock framework和regulator framework提供的接口,控制CPU core的频率和电压;
关键点2,根据应用场景,手动(用户发起,例如省电模式)或者自动(软件自动调整,例如HMP)的调整。
注2:对关键点2来说,如果调整比较频繁,则需要CPU core在不同频率之间转换的速度足够快,后面会详细介绍。
为了实现上述功能需求,cpufreq framework抽象出cpufreq driver、cpufreq policy(策略)、cpufreq governor等多个软件实体。
2 软件架构

对下,cpufreq framework基于cpu subsystem driver、OPP、clock framework、regulator framework等模块,提供对CPU core频率和电压的控制。这一部分主要由cpufreq driver实现。
对上,cpufreq framework会通过cpufreq core、cpufreq governors、cpufreq stats等模块,以sysfs的形式,向用户空间提供cpu frequency的查询、控制等接口。同时,在频率改变的时候,通过notifier通知关心的driver。
内部,cpufreq framework包括cpufreq core、cpufreq driver、cpufreq governors、cpufreq stats等模块。
3 软件模块的功能
3.1 Cpufreq core
cpufreq core是cpufreq framework的核心模块,和kernel其它framework类似,它主要实现三类功能:
对上,以sysfs的形式向用户空间提供统一的接口,以notifier的形式向其它driver提供频率变化的通知;
对下,提供CPU core频率和电压控制的驱动框架,方便底层driver的开发;同时,提供governor框架,用于实现不同的频率调整机制;
内部,封装各种逻辑,实现所需功能。这些逻辑主要围绕struct cpufreq_driver、struct cpufreq_policy和struct cpufreq_governor三个数据结构进行,下面会详细分析。
3.1.1 struct cpufreq_drive
struct cpufreq_driver {
struct module *owner;
char name[CPUFREQ_NAME_LEN]; //名字唯一
u8 flags;
/* needed by all drivers */
int (*init) (struct cpufreq_policy *policy); //必须实现,填充policy内容
int (*verify) (struct cpufreq_policy *policy); //必须实现,验证policy的内容是否符合硬件要求
/* define one out of two */
用于设置CPU core动态频率调整的范围(即policy)
int (*setpolicy) (struct cpufreq_policy *policy);
//设定CPU指定频率,接口为旧接口
int (*target) (struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation);
/* should be defined, if possible */
unsigned int (*get) (unsigned int cpu);
/* optional */
unsigned int (*getavg) (struct cpufreq_policy *policy,
unsigned int cpu);
int (*bios_limit) (int cpu, unsigned int *limit);
int (*exit) (struct cpufreq_policy *policy);
int (*suspend) (struct cpufreq_policy *policy);
int (*resume) (struct cpufreq_policy *policy);
struct freq_attr **attr;
};
相关API:
int cpufreq_register_driver(struct cpufreq_driver *driver_data);
int cpufreq_unregister_driver(struct cpufreq_driver *driver_data);
const char *cpufreq_get_current_driver(void);
void *cpufreq_get_driver_data(void);
3.1.2 struct cpufreq_cpuinfo
struct cpufreq_cpuinfo {
unsigned int max_freq;
unsigned int min_freq;
/* in 10^(-9) s = nanoseconds */
unsigned int transition_latency;
};
cpuinfo,该cpu调频相关的固定信息,包括最大频率、最小频率、切换延迟,其中最大频率、最小频率可以通过frequency table推导得出
3.1.3 struct cpufreq_policy
struct cpufreq_policy {
cpumask_var_t cpus; /* CPUs requiring sw coordination */
cpumask_var_t related_cpus; /* CPUs with any coordination */
unsigned int shared_type; /* ANY or ALL affected CPUs
should set cpufreq */
unsigned int cpu; /* cpu nr of registered CPU */
struct cpufreq_cpuinfo cpuinfo;/* see above */
unsigned int min; /* in kHz */
unsigned int max; /* in kHz */
unsigned int cur; /* in kHz, only needed if cpufreq * governors are used */
unsigned int policy; /* see above */
//不能自动调频的CPU,需要governor设置具体的频率
struct cpufreq_governor *governor; /* see below */
struct work_struct update; /* if update_policy() needs to be * called, but you're in IRQ context */
struct cpufreq_real_policy user_policy;
struct kobject kobj;
struct completion kobj_unregister;
};
3.1.4 struct cpufreq_governor
struct cpufreq_governor {
char name[CPUFREQ_NAME_LEN];
//用于governor状态切换的回调函数
int (*governor) (struct cpufreq_policy *policy, unsigned int event);
//用于提供 sysfs setspeed attribute文件的回调函数
ssize_t (*show_setspeed) (struct cpufreq_policy *policy, char *buf);
int (*store_setspeed) (struct cpufreq_policy *policy, unsigned int freq);
//该governor所能容忍的最大频率切换延迟
unsigned int max_transition_latency; /* HW must be able to switch to
next freq faster than this value in nano secs or we
will fallback to performance governor */
struct list_head governor_list;
struct module *owner;
};
3.2 cpufreq drivers
3.2.1 cpufreq driver编写步骤
Cpufreq driver主要完成平台的相关的CPU频率/电压的控制,他在cpufreq framework中是非常简单的模块,编写步骤如下:
- 1)平台相关的初始化动作,包括CPU core的clock/regulator获取、初始化等。
- 2)生成frequency table,即CPU core所支持的频率/电压列表。并在初始化时将该table保存在policy中。
- 3)定义一个struct cpufreq_driver变量,填充必要的字段,并根据平台的特性,实现其中的回调函数。
- 4)调用cpufreq_register_driver将driver注册到cpufreq framework中。
- 5)cpufreq core会在CPU设备添加时,调用driver的init接口。driver需要在该接口中初始化struct cpufreq_policy变量。
- 6)系统运行过程中,cpufreq core会根据实际情况,调用driver的setpolicy或者target/target_index等接口,设置CPU的调频策略或者频率值。
- 7)系统suspend的时中,会将CPU的频率设置为指定的值,或者调用driver的suspend回调函数;系统resume时,调用driver的resume回调函数。
3.2.2 cpufreq driver有关的API即功能分析
3.2.2.1 frequency table
frequency table是CPU core可以正确运行的一组频率/电压组合,一般情况下,会在项目启动的初期,通过“try频点”的方法,确定出稳定性、通用性都符合要求的频点。
frequency table之所以存在的一个思考点是:table是频率和电压之间的一个一一对应的组合,因此cpufreq framework只需要关心频率,所有的策略都称做“调频”策略。而cpufreq driver可以在“调频”的同时,通过table取出和频率对应的电压,进行修改CPU core电压,实现“调压”的功能。
/* Special Values of .frequency field */
#define CPUFREQ_ENTRY_INVALID ~0u
#define CPUFREQ_TABLE_END ~1u
/* Special Values of .flags field */
#define CPUFREQ_BOOST_FREQ (1 << 0)
struct cpufreq_frequency_table {
unsigned int flags;
unsigned int driver_data; /* driver specific data, not used by core */
unsigned int frequency; /* kHz - doesn't need to be in ascending order */
};
flags,现在只有一个----CPUFREQ_BOOST_FREQ,表示这个频率值是一个boost频率。
3.2.2.2 struct cpufreq_drive
struct cpufreq_driver {
char name[CPUFREQ_NAME_LEN];
u8 flags;
void *driver_data;
/* needed by all drivers */
int (*init)(struct cpufreq_policy *policy);
int (*verify)(struct cpufreq_policy *policy);
/* define one out of two */
int (*setpolicy)(struct cpufreq_policy *policy);
/*
* On failure, should always restore frequency to policy->restore_freq
* (i.e. old freq).
*/
int (*target)(struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation); /* Deprecated */
int (*target_index)(struct cpufreq_policy *policy,
unsigned int index);
unsigned int (*fast_switch)(struct cpufreq_policy *policy,
unsigned int target_freq);
/*
* Caches and returns the lowest driver-supported frequency greater than
* or equal to the target frequency, subject to any driver limitations.
* Does not set the frequency. Only to be implemented for d
Linux cpufreq子系统全面解析

本文详细介绍Linux cpufreq子系统,其功能是动态电压/频率调整,可在功耗和性能间平衡。阐述了软件架构、各模块功能,如cpufreq core、cpufreq drivers等,还介绍了调频策略及常见问题解答,如查看频率表、切换模式等。
最低0.47元/天 解锁文章
943

被折叠的 条评论
为什么被折叠?



