作者簡介:大Q在某半導體公司任職,主要從事硬件、芯片、驅動、軟件,整體方案及架構的功耗設計。喜歡研究linux,liteos等系統的功耗設計思路,以及業界如何結合實際場景進行軟硬件方案設計。
DVFS全稱Dynamic Voltage and Frequency Scaling,本章主要講解CPU的DVFS。
Linux 的CPU調頻調壓由cpufreq完成,cpufreq需要拆成兩個詞看cpu-freq,通過字面意思可知,cpufreq和CPU及頻率有關。隨著半導體工藝的演進,芯片性能越來越強,軟件迭代對CPU性能的需求也越來越大,CPU頻率也越來越高。如果一直讓CPU運行在最高頻率下,功耗、發熱等問題也會隨之而來,本章我們講解CPU調頻調壓的設計與實現。
13.1 Linux cpufreq的設計與實現
本節,我們主要聚焦對Linux 內核cpufreq的實現機制進行分析,包括配置、主要結構體、主要函數、工作時序等。
13.1.1 架構設計概覽
DVFS在低功耗軟件棧中的位置如圖13-1所示,屬于非睡眠形式的動態功耗控制方式。
圖13-1 DVFS在低功耗軟件棧中的位置
13.1.2 模塊功能詳解
Linux的cpufreq框架用一句話概括就是基于軟硬件約束通過一定的策略完成CPU頻率的調整。這里軟硬件配置如頻率范圍、CPU個數等由cpufreq驅動初始化時通過相應流程配置,主要由policy模塊承載,同時,policy模塊也管理governor和driver的聯系等事項。策略主要指governor模塊即基于什么調頻,kernel默認的governor有performance、powersave、conservative、ondemand、userspace以及目前默認應用的schedutil,最后完成CPU頻率配置的模塊就是driver了,頻率配置可以調用CLK模塊接口完成,也可自行根據芯片配置流程完成,如果clk實現較好,建議通過CLK模塊完成。
調頻調壓均支持的CPU(其他模塊有同樣約束)一般有如下配置約束:升頻時,如果需要升壓,需要先完成電壓配置(記起regulator模塊沒)并等待電壓穩定后再進行頻率配置,降壓時,需要先降低頻率,然后再配置降壓。這是由硬件決定的,先頻率高于額定電壓時,相當于CPU在超頻運行,不一定會出問題,出問題也是千奇八怪,難以定位,還是按照芯片約束來吧。
另外,cpufreq框架還支持notify機制,在頻率發生變化前后調用通知,對于需要感知CPU頻率變化的模塊,收到通知事件時,可以進行對應配置。
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的實現主要在如下文件中: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 主要數據類型
Linux cpufreq和內核絕大多數框架一樣,提供了通用機制,便于驅動開發人員專注驅動開發,但我們主要了解其核心思路,便于在非Linux系統中實現并應用自己的CPU調頻模塊。
1)cpufreq框架的core主要完成sysfs接口封裝、driver/governor/policy邏輯串聯及driver驅動接口封裝。
2)governor模塊主要封裝governor統一接口,提供governor注冊。
3)policy模塊,具體也不能說是個模塊,但cpufreq driver與governor的關聯、管理都離不開policy模塊,閱讀源碼時會感覺policy有點混亂哪里都有它的身影,又有點語焉不詳,其實不妨礙實現自己的cpufreq。
4)driver主要功能就是完成最終的頻率調整(電壓調整,如果支持的話)。
其他模塊這就不細述了,下面我們結合源碼了解各模塊功能及cpufreq是如何工作的。
1 cpufreq_policy結構體
struct cpufreq_policy 是cpufreq core提供的非常重要的結構體,下面講解主要成員含義:
cpus及related_cpus表示當前policy管理的cpu,cpus是當前處于online狀態的,related_cpus表示所有的的包含online/offline的。
CPU表示當前管理policy的cpu id,若多個CPU共用一個policy,只需要一個CPU進行管理即可,在該CPU下線時,還需要更新管理CPU。
clk 表示當前policy使用的clk句柄。
cpuinfo表示CPU設計的最大最小頻率。
min/max/cur表示當前policy支持的最大最小及當前頻率。
Governor/governor_data表示當前policy使用的governor及其私有數據。
freq_table當前CPU支持的頻率表。
driver_data表示driver的私有數據。
結構體定義如下:
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結構體主要成員的介紹,我們知道policy主要用于配置CPU的調頻約束以及governor的管理。
2 governor相關數據結構
governor鏈表,用于存放所有注冊的governor節點。
static LIST_HEAD(cpufreq_governor_list);
接下來介紹下governor的主要結構體struct cpufreq_governor ,主要給出governor唯一名字及API回調。
#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模塊提供了一個統一初始化宏用于對其變量進行初始化,如宏CPUFREQ_DBS_GOVERNOR_INITIALIZE的定義實現,實際使用的結構體為struct cpufreq_governor,governor模塊是為了屏蔽各種governor和cpufreq關聯而實現的。結構體相關成員變量含義如下所示:
name:當前初始化governor的名字,如“ondemand”、“conservative”等。
init/exit等:governor初始化、注銷或切換時cpufreq core調用的流程。
governor_list:各種governor注冊時掛接鏈表,現在已不怎么使用了。
flags:當前governor策略,見上述注釋。
governor模塊還有一個核心結構體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調頻約束,如負載閾值,采樣周期配置等。
gov_dbs_update:governor更新負載及觸發頻率配置的回調,governor的精髓在這里,后續講解。
3 driver相關數據結構
cpufreq_driver類型的變量定義:
static struct cpufreq_driver *cpufreq_driver;
core提供的driver結構體,用于core對默認driver的關聯及管理。
struct cpufreq_driver {
char name[CPUFREQ_NAME_LEN];
u16 flags;
void *driver_data;
/*所有的驅動都會使用?*/
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);
/*緩存并返回驅動程序支持的最低頻率大于或等于目標頻率。并不設置頻率,只有target()才會設置頻率。*/
unsigned int (*resolve_freq)(struct cpufreq_policy *policy,
unsigned int target_freq);
/*僅適用于未設置target_index()和CPUFREQ_ASYNC_NOTIFICATION的驅動程序。Get_intermediate應該返回一個穩定的中間頻率在跳轉到對應'index'的頻率之前,target_intermediate()應該將CPU設置為該頻率。core將負責發送通知,而驅動程序不必在target_intermediate()或者 target_index()中處理它們。驅動程序可以從get_intermediate()返回'0',以防他們不希望切換到某個目標頻率的中間頻率。在這種情況下,core將直接調用->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:驅動唯一名字。
flags:用于cpufreq部分功能控制,詳見cpufreq.h,注釋較清晰。
Init:driver注冊時,由core調用的初始化接口,一般主要用于頻率表的創建及policy的填充。
verify:主要對policy內配置及CPU頻率約束進行驗證,保證policy在CPU硬件約束范圍之內。
target/target_index:driver實現最終調頻的接口,內部可以自行實現或調用CLK接口。
suspend/resume:系統DPM時回調接口。
編輯:黃飛
評論
查看更多