作者簡介:大Q在某半導(dǎo)體公司任職,主要從事硬件、芯片、驅(qū)動、軟件,整體方案及架構(gòu)的功耗設(shè)計。喜歡研究linux,liteos等系統(tǒng)的功耗設(shè)計思路,以及業(yè)界如何結(jié)合實際場景進行軟硬件方案設(shè)計。
DVFS全稱Dynamic Voltage and Frequency Scaling,本章主要講解CPU的DVFS。
Linux 的CPU調(diào)頻調(diào)壓由cpufreq完成,cpufreq需要拆成兩個詞看cpu-freq,通過字面意思可知,cpufreq和CPU及頻率有關(guān)。隨著半導(dǎo)體工藝的演進,芯片性能越來越強,軟件迭代對CPU性能的需求也越來越大,CPU頻率也越來越高。如果一直讓CPU運行在最高頻率下,功耗、發(fā)熱等問題也會隨之而來,本章我們講解CPU調(diào)頻調(diào)壓的設(shè)計與實現(xiàn)。
13.1 Linux cpufreq的設(shè)計與實現(xiàn)
本節(jié),我們主要聚焦對Linux 內(nèi)核cpufreq的實現(xiàn)機制進行分析,包括配置、主要結(jié)構(gòu)體、主要函數(shù)、工作時序等。
13.1.1 架構(gòu)設(shè)計概覽
DVFS在低功耗軟件棧中的位置如圖13-1所示,屬于非睡眠形式的動態(tài)功耗控制方式。
圖13-1 DVFS在低功耗軟件棧中的位置
13.1.2 模塊功能詳解
Linux的cpufreq框架用一句話概括就是基于軟硬件約束通過一定的策略完成CPU頻率的調(diào)整。這里軟硬件配置如頻率范圍、CPU個數(shù)等由cpufreq驅(qū)動初始化時通過相應(yīng)流程配置,主要由policy模塊承載,同時,policy模塊也管理governor和driver的聯(lián)系等事項。策略主要指governor模塊即基于什么調(diào)頻,kernel默認的governor有performance、powersave、conservative、ondemand、userspace以及目前默認應(yīng)用的schedutil,最后完成CPU頻率配置的模塊就是driver了,頻率配置可以調(diào)用CLK模塊接口完成,也可自行根據(jù)芯片配置流程完成,如果clk實現(xiàn)較好,建議通過CLK模塊完成。
調(diào)頻調(diào)壓均支持的CPU(其他模塊有同樣約束)一般有如下配置約束:升頻時,如果需要升壓,需要先完成電壓配置(記起regulator模塊沒)并等待電壓穩(wěn)定后再進行頻率配置,降壓時,需要先降低頻率,然后再配置降壓。這是由硬件決定的,先頻率高于額定電壓時,相當于CPU在超頻運行,不一定會出問題,出問題也是千奇八怪,難以定位,還是按照芯片約束來吧。
另外,cpufreq框架還支持notify機制,在頻率發(fā)生變化前后調(diào)用通知,對于需要感知CPU頻率變化的模塊,收到通知事件時,可以進行對應(yīng)配置。
13.1.3 配置信息解析
Linux cpufreq依賴如下特性宏:
CONFIG_CPU_FREQ=y
CONFIG_CPU_FREQ_GOV_ATTR_SET=y
CONFIG_CPU_FREQ_GOV_COMMON=y
CONFIG_CPU_FREQ_STAT=y
CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE=y
CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE=y
CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL=y
CONFIG_CPUFREQ_DT=y
CONFIG_CPUFREQ_DT_PLATDEV=y
cpufreq的實現(xiàn)主要在如下文件中:drivers/cpufreq/cpufreq.c、cpufreq-dt.c、cpufreq-dt-platdev.c、cpufreq_governor.c、cpufreq_ondemand.c、cpufreq_stats.c、freq_table.c、cpufreq_governor_attr_set.c等
13.1.4 主要數(shù)據(jù)類型
Linux cpufreq和內(nèi)核絕大多數(shù)框架一樣,提供了通用機制,便于驅(qū)動開發(fā)人員專注驅(qū)動開發(fā),但我們主要了解其核心思路,便于在非Linux系統(tǒng)中實現(xiàn)并應(yīng)用自己的CPU調(diào)頻模塊。
1)cpufreq框架的core主要完成sysfs接口封裝、driver/governor/policy邏輯串聯(lián)及driver驅(qū)動接口封裝。
2)governor模塊主要封裝governor統(tǒng)一接口,提供governor注冊。
3)policy模塊,具體也不能說是個模塊,但cpufreq driver與governor的關(guān)聯(lián)、管理都離不開policy模塊,閱讀源碼時會感覺policy有點混亂哪里都有它的身影,又有點語焉不詳,其實不妨礙實現(xiàn)自己的cpufreq。
4)driver主要功能就是完成最終的頻率調(diào)整(電壓調(diào)整,如果支持的話)。
其他模塊這就不細述了,下面我們結(jié)合源碼了解各模塊功能及cpufreq是如何工作的。
1 cpufreq_policy結(jié)構(gòu)體
struct cpufreq_policy 是cpufreq core提供的非常重要的結(jié)構(gòu)體,下面講解主要成員含義:
cpus及related_cpus表示當前policy管理的cpu,cpus是當前處于online狀態(tài)的,related_cpus表示所有的的包含online/offline的。
CPU表示當前管理policy的cpu id,若多個CPU共用一個policy,只需要一個CPU進行管理即可,在該CPU下線時,還需要更新管理CPU。
clk 表示當前policy使用的clk句柄。
cpuinfo表示CPU設(shè)計的最大最小頻率。
min/max/cur表示當前policy支持的最大最小及當前頻率。
Governor/governor_data表示當前policy使用的governor及其私有數(shù)據(jù)。
freq_table當前CPU支持的頻率表。
driver_data表示driver的私有數(shù)據(jù)。
結(jié)構(gòu)體定義如下:
struct cpufreq_policy {
cpumask_var_t cpus; /* 只有Online的?CPUs才使用*/
cpumask_var_t related_cpus; /* Online + Offline CPUs */
unsigned int cpu; ???/* 使用這個策略的cpu,必須是online的*/
struct clk *clk;
struct cpufreq_cpuinfo cpuinfo;/* see above */
unsigned int min; ???/* in kHz */
unsigned int max; ???/* in kHz */
unsigned int cur; ???/* in kHz, 只有在cpufreq governors 被使用時踩需要?*/
unsigned int policy; /* see above */
struct cpufreq_governor *governor; /* see below */
void *governor_data;
struct cpufreq_frequency_table *freq_table;
void *driver_data;
};
通過對policy結(jié)構(gòu)體主要成員的介紹,我們知道policy主要用于配置CPU的調(diào)頻約束以及governor的管理。
2 governor相關(guān)數(shù)據(jù)結(jié)構(gòu)
governor鏈表,用于存放所有注冊的governor節(jié)點。
static LIST_HEAD(cpufreq_governor_list);
接下來介紹下governor的主要結(jié)構(gòu)體struct cpufreq_governor ,主要給出governor唯一名字及API回調(diào)。
#define CPUFREQ_DBS_GOVERNOR_INITIALIZER(_name_)
{
.name = _name_,
.flags = CPUFREQ_GOV_DYNAMIC_SWITCHING,
.owner = THIS_MODULE,
.init = cpufreq_dbs_governor_init,
.exit = cpufreq_dbs_governor_exit,
.start = cpufreq_dbs_governor_start,
.stop = cpufreq_dbs_governor_stop,
.limits = cpufreq_dbs_governor_limits,
}
struct cpufreq_governor {
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;
};
/* Governor flags */
/* For governors which change frequency dynamically by themselves */
#define CPUFREQ_GOV_DYNAMIC_SWITCHING ?BIT(0)
/* For governors wanting the target frequency to be set exactly */
#define CPUFREQ_GOV_STRICT_TARGET BIT(1)
governor模塊提供了一個統(tǒng)一初始化宏用于對其變量進行初始化,如宏CPUFREQ_DBS_GOVERNOR_INITIALIZE的定義實現(xiàn),實際使用的結(jié)構(gòu)體為struct cpufreq_governor,governor模塊是為了屏蔽各種governor和cpufreq關(guān)聯(lián)而實現(xiàn)的。結(jié)構(gòu)體相關(guān)成員變量含義如下所示:
name:當前初始化governor的名字,如“ondemand”、“conservative”等。
init/exit等:governor初始化、注銷或切換時cpufreq core調(diào)用的流程。
governor_list:各種governor注冊時掛接鏈表,現(xiàn)在已不怎么使用了。
flags:當前governor策略,見上述注釋。
governor模塊還有一個核心結(jié)構(gòu)體struct dbs_governor,其定義如下:
struct dbs_governor {
struct cpufreq_governor gov;
struct kobj_type kobj_type;
struct dbs_data *gdbs_data;
unsigned int (*gov_dbs_update)(struct cpufreq_policy *policy);
struct policy_dbs_info *(*alloc)(void);
void (*free)(struct policy_dbs_info *policy_dbs);
int (*init)(struct dbs_data *dbs_data);
void (*exit)(struct dbs_data *dbs_data);
void (*start)(struct cpufreq_policy *policy);
};
gov:上文中CPUFREQ_DBS_GOVERNOR_INITIALIZER部分。
gdbs_data:當前governor調(diào)頻約束,如負載閾值,采樣周期配置等。
gov_dbs_update:governor更新負載及觸發(fā)頻率配置的回調(diào),governor的精髓在這里,后續(xù)講解。
3 driver相關(guān)數(shù)據(jù)結(jié)構(gòu)
cpufreq_driver類型的變量定義:
static struct cpufreq_driver *cpufreq_driver;
core提供的driver結(jié)構(gòu)體,用于core對默認driver的關(guān)聯(lián)及管理。
struct cpufreq_driver {
char name[CPUFREQ_NAME_LEN];
u16 flags;
void *driver_data;
/*所有的驅(qū)動都會使用?*/
int (*init)(struct cpufreq_policy *policy);
int (*verify)(struct cpufreq_policy_data *policy);
int (*setpolicy)(struct cpufreq_policy *policy);
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);
/*緩存并返回驅(qū)動程序支持的最低頻率大于或等于目標頻率。并不設(shè)置頻率,只有target()才會設(shè)置頻率。*/
unsigned int (*resolve_freq)(struct cpufreq_policy *policy,
unsigned int target_freq);
/*僅適用于未設(shè)置target_index()和CPUFREQ_ASYNC_NOTIFICATION的驅(qū)動程序。Get_intermediate應(yīng)該返回一個穩(wěn)定的中間頻率在跳轉(zhuǎn)到對應(yīng)'index'的頻率之前,target_intermediate()應(yīng)該將CPU設(shè)置為該頻率。core將負責發(fā)送通知,而驅(qū)動程序不必在target_intermediate()或者 target_index()中處理它們。驅(qū)動程序可以從get_intermediate()返回'0',以防他們不希望切換到某個目標頻率的中間頻率。在這種情況下,core將直接調(diào)用->target_index()。?*/
unsigned int (*get_intermediate)(struct cpufreq_policy *policy,
unsigned int index);
int (*target_intermediate)(struct cpufreq_policy *policy,
unsigned int index);
unsigned int (*get)(unsigned int cpu);
/* 更新策略限值(policy limits).?*/
void (*update_limits)(unsigned int cpu);
int (*bios_limit)(int cpu, unsigned int *limit);
int (*online)(struct cpufreq_policy *policy);
int (*offline)(struct cpufreq_policy *policy);
int (*exit)(struct cpufreq_policy *policy);
void (*stop_cpu)(struct cpufreq_policy *policy);
int (*suspend)(struct cpufreq_policy *policy);
int (*resume)(struct cpufreq_policy *policy);
void (*ready)(struct cpufreq_policy *policy);
struct freq_attr **attr;
bool boost_enabled;
int (*set_boost)(struct cpufreq_policy *policy, int state);
};
主要成員屬性見加粗部分。
name:驅(qū)動唯一名字。
flags:用于cpufreq部分功能控制,詳見cpufreq.h,注釋較清晰。
Init:driver注冊時,由core調(diào)用的初始化接口,一般主要用于頻率表的創(chuàng)建及policy的填充。
verify:主要對policy內(nèi)配置及CPU頻率約束進行驗證,保證policy在CPU硬件約束范圍之內(nèi)。
target/target_index:driver實現(xiàn)最終調(diào)頻的接口,內(nèi)部可以自行實現(xiàn)或調(diào)用CLK接口。
suspend/resume:系統(tǒng)DPM時回調(diào)接口。
編輯:黃飛
評論
查看更多