一、cpufreq的背景
随着技术的发展,当前soc中的cpu主频一般都超过了1Ghz,而cpu的主频越高,其消耗的功耗也越大,这主要体现在以下两个方面:
(1)cpu的运行频率越高,则晶体管在单位时间内的开关次数就越多,与其相关的动态功耗也越高。
(2)为了保证数字电路的逻辑正确,cpu运行频率越高,则其所需的供电电压也越高
但是系统在负载较轻时可能并不需要工作在最高主频上,特别是对于手机等可移动设备,这种性能浪费会直接影响其续航能力。为此若能根据系统的负载情况,动态调整cpu频率和电压,就能减少这部分功率消耗。cpufreq就是内核为支持这种能力而定义的一套管理框架。
二、cpufreq总体架构
cpufreq主要功能为动态调节cpu的频率和电压,为了达到这一目标,该框架需要考虑以下几个问题:
(1)确定每个cpu的频率调节范围,包括硬件支持的最大频率、最小频率,以及软件可调节的最大频率和最小频率等。在cpufreq中它可通过policy来管理
(2)在当前负载下,确定cpu应该工作于哪个频率,如简单地使cpu工作在最高频率以提供最高性能,或者使其工作在最低频率以降低功耗。但系统的负载是动态变化的,因此cpufreq还支持通过特定算法,在负载变化时动态调节cpu的频率。它可通过其governor组件实现
(3)确定cpu频率调节的周期及触发时机,以使其既不对系统有太大影响,又能较快地跟踪系统的负载变化。它是通过内核调度器和cpufreq模块共同完成的
(4)当待调节频率确定后,如何调用频率设置接口,实现实际的频率调节工作。它是通过其driver组件实现的
除此以外,cpufreq还包含cpufreq stats,cpufreq qos,cpufreq notifier等辅助模块,其主要功能如下:
(1)cpufreq stats:用于搜集cpufreq的一些统计数据,如cpu在每个频点下的运行时间,总的切频次数等。
(2)cpufreq qos:该模块用于在cpufreq频率限制值发生改变时,向cpufreq模块发送一个通知,使其能及时调整到新的值
(3)cpufreq notifier:那些对cpu频率切换,或policy对应governor发生改变感兴趣的模块,可以向cpufreq注册一个通知。当以上事件发生时,cpufreq将会向其发送相关通知
最后由于cpu的工作电压越高,其功耗也越大,因此我们希望cpu在切换到某一频率时,其电压也相应地切换到其可稳定工作的最低电压。为了得到电压和频率的关系,一般soc都会提供一些频点组合,cpufreq只会在这些规定的频点中切换。这些系统预置的频点叫做operation performance points(opp),它可通过如下例所示的dts文件配置:
cpu0: cpu@0 {
...
operating-points-v2 = <&cluster0_opp>;
…
};
cpu1: cpu@1 {
...
operating-points-v2 = <&cluster0_opp>;
...
};
cluster0_opp: opp_table0 {
compatible = "operating-points-v2";
opp-shared;
opp00 {
opp-hz = /bits/ 64 <533000000>;
opp-microvolt = <700000>;
clock-latency-ns = <300000>;
};
opp01 {
opp-hz = /bits/ 64 <999000000>;
opp-microvolt = <800000>;
clock-latency-ns = <300000>;
};
opp02 {
opp-hz = /bits/ 64 <1402000000>;
opp-microvolt = <900000>;
clock-latency-ns = <300000>;
};
opp03 {
opp-hz = /bits/ 64 <1709000000>;
opp-microvolt = <1000000>;
clock-latency-ns = <300000>;
};
opp04 {
opp-hz = /bits/ 64 <1844000000>;
opp-microvolt = <1100000>;
clock-latency-ns = <300000>;
};
};
内核抽象出了一个opp组件,用于管理这些电压和频率组合。当cpufreq执行频率切换时,则从opp表中选择一个最合适的频率电压值,然后通过opp接口,调用clk和regulator操作函数,完成实际的调频调压操作。综上所述,内核cpufreq模块的总体架构如下:
三、主要数据结构
3.1 cufreq_policy结构
cpufreq_policy结构包含了调频策略相关的频率范围等参数。它们包括其对应的调频策略、最大最小频率限制值,以及cpufreq qos参数等。以下为其结构体定义:
struct cpufreq_policy {
cpumask_var_t cpus;
cpumask_var_t related_cpus;
cpumask_var_t real_cpus; (1)
…
unsigned int cpu; (2)
struct clk *clk;
struct cpufreq_cpuinfo cpuinfo; (3)
unsigned int min;
unsigned int max;
unsigned int cur; (4)
unsigned int suspend_freq; (5)
unsigned int policy; (6)
unsigned int last_policy;
struct cpufreq_governor *governor; (7)
void *governor_data;
char last_governor[CPUFREQ_NAME_LEN];
struct work_struct update;
struct freq_constraints constraints;
struct freq_qos_request *min_freq_req;
struct freq_qos_request *max_freq_req; (8)
struct cpufreq_frequency_table *freq_table;
enum cpufreq_table_sorting freq_table_sorted; (9)
struct list_head policy_list; (10)
…
struct cpufreq_stats *stats; (11)
void *driver_data; (12)
struct thermal_cooling_device *cdev; (13)
struct notifier_block nb_min;
struct notifier_block nb_max; (14)
};
(1)cpus、related_cpus和real_cpus都用于表示使用该policy的cpu mask,它们的区别在于cpu的运行状态不同。其中cpus只包含当前处于online状态的cpu,related_cpus包含online和offline状态的cpu,而real_cpus则包含present状态的cpu
(2)cpufreq中每一种policy都通过一个cpu进行管理,它们可以是以上cpus掩码中的任意一个。由于内核支持cpu hotplug,因此若该cpu被下线以后,则需要重新选择一个cpu作为管理该policy的cpu
(3)cpuinfo结构体定义如下,它用于定义硬件支持的cpu最高、最低频率,以及切频的延迟时间。该频率值在系统运行过程中不会改变
struct cpufreq_cpuinfo {
unsigned int max_freq;
unsigned int min_freq;
unsigned int transition_latency;
}
(4)min、max表示当前cpufreq支持切换的最低和最高频率,该值可以与cpuinfo中规定的硬件频率范围不同,且不能超过该范围。当cpufreq qos改变频率范围时,这两个值就可能在系统运行过程中发生改变。而cur表示cpu的当前运行频率
(5)在suspend流程中需要被设置为该频率
(6)内核支持两种调频方式,一种是通过governor调频,它通过target_index或target回调执行调频操作。另一种是通过setpolicy回调调频,这时其调频策略将由policy的值确定,其取值可为 CPUFREQ_POLICY_POWERSAVE和CPUFREQ_POLICY_PERFORMANCE
(7)若使用governor调频方式,则该成员用于指定控制调频策略的governor
(8)这几个成员用于cpufreq qos特性,主要用于修改该policy支持的最低、最高频率
(9)freq_table用于保存该policy支持的所有频点,这些频点数据是在cpufreq驱动初始化时,从cpu对应的opp表中读取的。而freq_table_sorted用于指定这些频点的排序方式
(10)它用于将该policy挂到全局的policy链表中
(11)该结构体用于保存policy的统计数据
(12)用于保存驱动的私有数据
(13)由于cpu调频可以改变系统的功耗,因此thermal模块可通过调频方式为系统降温。这种方式下,cpufreq就作为thermal模块的降温设备
(14)这两个通知将被注册到qos的通知链中,当cpufreq的最大、最小频率发生改变时,qos模块将分别调用这两个通知
3.2 cpufreq_governor结构体
cpufreq governor的主要功能是根据其相应的控制策略,为cpu选择一个合适的运行频率,该流程实际上是由governor的start接口向调度子系统注册一个回调函数,并由调度子系统触发调频操作时执行的。
cpufreq_governor结构体主要提供了一组回调函数,用于控制特定governor的相关流程:
struct cpufreq_governor {
char name[CPUFREQ_NAME_LEN]; (1)
int (*init)(struct cpufreq_policy *policy); (2)
void (*exit)(struct cpufreq_policy *policy); (3)
int (*start)(struct cpufreq_policy *policy); (4)
void (*stop)(struct cpufreq_policy *policy); (5)
void (*limits)(struct cpufreq_policy *policy); (6)
ssize_t (*show_setspeed) (struct cpufreq_policy *policy,
char *buf);
int (*store_setspeed) (struct cpufreq_policy *policy,
unsigned int freq); (7)
struct list_head governor_list; (8)
struct module *owner;
u8 flags; (9)
}
(1)该governor的名字
(2)初始化一个governor时的回调函数
(3)退出一个governor时的回调函数
(4)启动一个governor的回调函数
(5)停止一个governor的回调函数
(6)若一个policy的最高或最低频率值发生改变,会调用本函数,用于判断cpu当前运行频率是否已不在新的频率范围之内。如果是的话,就将其当前频率设置到新频率范围之内
(7)show_setspeed和store_setspeed分别为sysfs用于读取和修改频率的接口
(8)用于将该governor挂到全局governor链表的节点
(9)用于保存该governor的标志
3.3 cpufreq_driver结构
cpufreq_governor会根据governor特定的策略,计算得到cpu待设置频率对应的频点,而频率设置流程需要cpufreq 驱动完成。cpufreq驱动主要包含一组回调函数,而特定驱动函数只需要实现这些回