国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 開(kāi)發(fā)設(shè)計(jì) > 正文

Linux內(nèi)核架構(gòu):動(dòng)態(tài)頻率調(diào)節(jié)系統(tǒng)CPUFreq

2019-11-09 18:30:02
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

概述

linux中,內(nèi)核提供了一套框架模型來(lái)完成CPU頻率的動(dòng)態(tài)調(diào)節(jié),以達(dá)到省電的目的。 這就是所謂的CPUFreq系統(tǒng)。


1. sysfs接口

我們先從CPUFreq提供的sysfs接口入手,直觀地看看它提供了那些功能。以下是我的電腦輸出的結(jié)果:

droidphone@990:~$ cd /sys/devices/system/cpudroidphone@990:/sys/devices/system/cpu$ lscpu0 cpu3 cpu6 cpuidle offline power releasecpu1 cpu4 cpu7 kernel_max online PResent ueventcpu2 cpu5 cpufreq modalias possible probe

所有與CPUFreq相關(guān)的sysfs接口都位于:/sys/devices/system/cpu下面,我們可以看到,8個(gè)cpu分別建立了一個(gè)自己的目錄,從cpu0到cpu7,我們?cè)倏纯磑ffline和online以及present的內(nèi)容:

droidphone@990:/sys/devices/system/cpu$ cat online0-7droidphone@990:/sys/devices/system/cpu$ cat offline8-15droidphone@990:/sys/devices/system/cpu$ cat present0-7droidphone@990:/sys/devices/system/cpu$

online代表目前正在工作的cpu,輸出顯示編號(hào)為0-7這8個(gè)cpu在工作,offline代表目前被關(guān)掉的cpu,present則表示主板上已經(jīng)安裝的cpu,由輸出可以看到,我的主板可以安裝16個(gè)cpu(因?yàn)閕ntel的超線程技術(shù),其實(shí)物理上只是8個(gè)),第8-15號(hào)cpu處于關(guān)閉狀態(tài)(實(shí)際上不存在,因?yàn)閜resent只有0-7)。

接著往下看:

droidphone@990:/sys/devices/system/cpu/cpu0$ lscache cpuidle microcode power thermal_throttle ueventcpufreq crash_notes node0 subsystem topologydroidphone@990:/sys/devices/system/cpu/cpu0$ cd cpufreq/droidphone@990:/sys/devices/system/cpu/cpu0/cpufreq$ lsaffected_cpus related_cpus scaling_max_freqbios_limit scaling_available_frequencies scaling_min_freqcpuinfo_cur_freq scaling_available_governors scaling_setspeedcpuinfo_max_freq scaling_cur_freq statscpuinfo_min_freq scaling_drivercpuinfo_transition_latency scaling_governordroidphone@990:/sys/devices/system/cpu/cpu0/cpufreq$

在我的電腦上,部分的值如下: cpuinfo_cur_freq: 1600000

cpuinfo_max_freq: 3401000

cpuinfo_min_freq: 1600000

scaling_cur_freq: 1600000

scaling_max_freq: 3401000

scaling_min_freq: 1600000 所以,我的cpu0的最低運(yùn)行頻率是1.6GHz,最高是3.4GHz,目前正在運(yùn)行的頻率是1.6GHz,前綴cpuinfo代表的是cpu硬件上支持的頻率,而scaling前綴代表的是可以通過(guò)CPUFreq系統(tǒng)用軟件進(jìn)行調(diào)節(jié)時(shí)所支持的頻率。cpuinfo_cur_freq代表通過(guò)硬件實(shí)際上讀到的頻率值,而scaling_cur_freq則是軟件當(dāng)前的設(shè)置值,多數(shù)情況下這兩個(gè)值是一致的,但是也有可能因?yàn)橛布脑颍形⑿〉牟町悺caling_available_frequencies會(huì)輸出當(dāng)前軟件支持的頻率值,看看我的cpu支持那些頻率:

droidphone@990:/sys/devices/system/cpu/cpu0/cpufreq$ cat scaling_available_frequencies 3401000 3400000 3000000 2800000 2600000 2400000 2200000 2000000 1800000 1600000 droidphone@990:/sys/devices/system/cpu/cpu0/cpufreq$

Oh,從1.6GHz到3.4GHz,一共支持10擋的頻率可供選擇。scaling_available_governors則會(huì)輸出當(dāng)前可供選擇的頻率調(diào)節(jié)策略:

conservative ondemand userspace powersave performance

一共有5中策略供我們選擇,那么當(dāng)前系統(tǒng)選用那種策略?讓我們看看:

dong@dong-990:/sys/devices/system/cpu/cpu0/cpufreq$ cat scaling_governorondemand

OK,我的系統(tǒng)當(dāng)前選擇ondemand這種策略,這種策略的主要思想是:只要cpu的負(fù)載超過(guò)某一個(gè)閥值,cpu的頻率會(huì)立刻提升至最高,然后再根據(jù)實(shí)際情況降到合適的水平。詳細(xì)的情況我們留在后面的章節(jié)中討論。scaling_driver則會(huì)輸出當(dāng)前使用哪一個(gè)驅(qū)動(dòng)來(lái)設(shè)置cpu的工作頻率。 當(dāng)我們選擇userspace作為我們的調(diào)頻governor時(shí),我們可以通過(guò)scaling_setspeed手工設(shè)置需要的頻率。powersave則簡(jiǎn)單地使用最低的工作頻率進(jìn)行運(yùn)行,而performance則一直選擇最高的頻率進(jìn)行運(yùn)行。


2. 軟件架構(gòu)

通過(guò)上一節(jié)的介紹,我們可以大致梳理出CPUFreq系統(tǒng)的構(gòu)成和工作方式。首先,CPU的硬件特性決定了這個(gè)CPU的最高和最低工作頻率,所有的頻率調(diào)整數(shù)值都必須在這個(gè)范圍內(nèi),它們用cpuinfo_xxx_freq來(lái)表示。然后,我們可以在這個(gè)范圍內(nèi)再次定義出一個(gè)軟件的調(diào)節(jié)范圍,它們用scaling_xxx_freq來(lái)表示,同時(shí),根據(jù)具體的硬件平臺(tái)的不同,我們還需要提供一個(gè)頻率表,這個(gè)頻率表規(guī)定了cpu可以工作的頻率值,當(dāng)然這些頻率值必須要在cpuinfo_xxx_freq的范圍內(nèi)。有了這些頻率信息,CPUFreq系統(tǒng)就可以根據(jù)當(dāng)前cpu的負(fù)載輕重狀況,合理地從頻率表中選擇一個(gè)合適的頻率供cpu使用,已達(dá)到節(jié)能的目的。至于如何選擇頻率表中的頻率,這個(gè)要由不同的governor來(lái)實(shí)現(xiàn),目前的內(nèi)核版本提供了5種governor供我們選擇。選擇好適當(dāng)?shù)念l率以后,具體的頻率調(diào)節(jié)工作就交由scaling_driver來(lái)完成。CPUFreq系統(tǒng)把一些公共的邏輯和接口代碼抽象出來(lái),這些代碼與平臺(tái)無(wú)關(guān),也與具體的調(diào)頻策略無(wú)關(guān),內(nèi)核的文檔把它稱為CPUFreq Core(/Documents/cpufreq/core.txt)。另外一部分,與實(shí)際的調(diào)頻策略相關(guān)的部分被稱作cpufreq_policy,cpufreq_policy又是由頻率信息和具體的governor組成,governor才是具體策略的實(shí)現(xiàn)者,當(dāng)然governor需要我們提供必要的頻率信息,governor的實(shí)現(xiàn)最好能做到平臺(tái)無(wú)關(guān),與平臺(tái)相關(guān)的代碼用cpufreq_driver表述,它完成實(shí)際的頻率調(diào)節(jié)工作。最后,如果其他內(nèi)核模塊需要在頻率調(diào)節(jié)的過(guò)程中得到通知消息,則可以通過(guò)cpufreq notifiers來(lái)完成。由此,我們可以總結(jié)出CPUFreq系統(tǒng)的軟件結(jié)構(gòu)如下:

3. cpufreq_policy

一種調(diào)頻策略的各種限制條件的組合稱之為policy,代碼中用cpufreq_policy這一數(shù)據(jù)結(jié)構(gòu)來(lái)表示:

struct cpufreq_policy { cpumask_var_t cpus; cpumask_var_t related_cpus; unsigned int shared_type; unsigned int cpu; unsigned int last_cpu; struct cpufreq_cpuinfo cpuinfo; unsigned int min; /* in kHz */ unsigned int max; /* in kHz */ unsigned int cur; unsigned int policy; struct cpufreq_governor *governor; void *governor_data; struct work_struct update; struct cpufreq_real_policy user_policy; struct kobject kobj; struct completion kobj_unregister;};

其中的各個(gè)字段的解釋如下:

cpus和related_cpus 這兩個(gè)都是cpumask_var_t變量,cpus表示的是這一policy控制之下的所有還出于online狀態(tài)的cpu,而related_cpus則是online和offline兩者的合集。主要是用于多個(gè)cpu使用同一種policy的情況,實(shí)際上,我們平常見(jiàn)到的大多數(shù)系統(tǒng)中都是這種情況:所有的cpu同時(shí)使用同一種policy。我們需要related_cpus變量指出這個(gè)policy所管理的所有cpu編號(hào)。cpu和last_cpu 雖然一種policy可以同時(shí)用于多個(gè)cpu,但是通常一種policy只會(huì)由其中的一個(gè)cpu進(jìn)行管理,cpu變量用于記錄用于管理該policy的cpu編號(hào),而last_cpu則是上一次管理該policy的cpu編號(hào)(因?yàn)楣芾韕olicy的cpu可能會(huì)被plug out,這時(shí)候就要把管理工作遷移到另一個(gè)cpu上)。cpuinfo 保存cpu硬件所能支持的最大和最小的頻率以及切換延遲信息。min/max/cur 該policy下的可使用的最小頻率,最大頻率和當(dāng)前頻率。policy 該變量可以取以下兩個(gè)值:CPUFREQ_POLICY_POWERSAVE和CPUFREQ_POLICY_PERFORMANCE,該變量只有當(dāng)調(diào)頻驅(qū)動(dòng)支持setpolicy回調(diào)函數(shù)的時(shí)候有效,這時(shí)候由驅(qū)動(dòng)根據(jù)policy變量的值來(lái)決定系統(tǒng)的工作頻率或狀態(tài)。如果調(diào)頻驅(qū)動(dòng)(cpufreq_driver)支持target回調(diào),則頻率由相應(yīng)的governor來(lái)決定。governor和governor_data 指向該policy當(dāng)前使用的cpufreq_governor結(jié)構(gòu)和它的上下文數(shù)據(jù)。governor是實(shí)現(xiàn)該policy的關(guān)鍵所在,調(diào)頻策略的邏輯由governor實(shí)現(xiàn)。update 有時(shí)在中斷上下文中需要更新policy,需要利用該工作隊(duì)列把實(shí)際的工作移到稍后的進(jìn)程上下文中執(zhí)行。user_policy 有時(shí)候因?yàn)樘厥獾脑蛐枰薷膒olicy的參數(shù),比如溫度過(guò)高時(shí),最大可允許的運(yùn)行頻率可能會(huì)被降低,為了在適當(dāng)?shù)臅r(shí)候恢復(fù)原有的運(yùn)行參數(shù),需要使用user_policy保存原始的參數(shù)(min,max,policy,governor)。kobj 該policy在sysfs中對(duì)應(yīng)的kobj的對(duì)象。

4. cpufreq_governor

所謂的governor,我把它翻譯成:調(diào)節(jié)器。governor負(fù)責(zé)檢測(cè)cpu的使用狀況,從而在可用的范圍中選擇一個(gè)合適的頻率,代碼中它用cpufreq_governor結(jié)構(gòu)來(lái)表示:

struct cpufreq_governor { char name[CPUFREQ_NAME_LEN]; int initialized; int (*governor) (struct cpufreq_policy *policy, unsigned int event); ssize_t (*show_setspeed) (struct cpufreq_policy *policy, char *buf); int (*store_setspeed) (struct cpufreq_policy *policy, unsigned int freq); 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;};

其中的各個(gè)字段的解釋如下:

name 該governor的名字。initialized 初始化標(biāo)志。governor 指向一個(gè)回調(diào)函數(shù),CPUFreq Core會(huì)在不同的階段調(diào)用該回調(diào)函數(shù),用于該governor的啟動(dòng)、停止、初始化、退出動(dòng)作。list_head 所有注冊(cè)的governor都會(huì)利用該字段鏈接在一個(gè)全局鏈表中,以供系統(tǒng)查詢和使用。

5. cpufreq_driver

上一節(jié)提到的gonvernor只是負(fù)責(zé)計(jì)算并提出合適的頻率,但是頻率的設(shè)定工作是平臺(tái)相關(guān)的,這需要cpufreq_driver驅(qū)動(dòng)來(lái)完成,cpufreq_driver的結(jié)構(gòu)如下:

struct cpufreq_driver { struct module *owner; char name[CPUFREQ_NAME_LEN]; u8 flags; bool have_governor_per_policy; /* 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); 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;};

相關(guān)的字段的意義解釋如下:

name 該頻率驅(qū)動(dòng)的名字。init 回調(diào)函數(shù),該回調(diào)函數(shù)必須實(shí)現(xiàn),CPUFreq Core會(huì)通過(guò)該回調(diào)函數(shù)對(duì)該驅(qū)動(dòng)進(jìn)行必要的初始化工作。verify 回調(diào)函數(shù),該回調(diào)函數(shù)必須實(shí)現(xiàn),CPUFreq Core會(huì)通過(guò)該回調(diào)函數(shù)檢查policy的參數(shù)是否被驅(qū)動(dòng)支持。setpolicy/target 回調(diào)函數(shù),驅(qū)動(dòng)必須實(shí)現(xiàn)這兩個(gè)函數(shù)中的其中一個(gè),如果不支持通過(guò)governor選擇合適的運(yùn)行頻率,則實(shí)現(xiàn)setpolicy回調(diào)函數(shù),這樣系統(tǒng)只能支持CPUFREQ_POLICY_POWERSAVE和CPUFREQ_POLICY_PERFORMANCE這兩種工作策略。反之,實(shí)現(xiàn)target回調(diào)函數(shù),通過(guò)target回調(diào)設(shè)定 - - governor所需要的頻率。get 回調(diào)函數(shù),用于獲取cpu當(dāng)前的工作頻率。getavg 回調(diào)函數(shù),用于獲取cpu當(dāng)前的平均工作頻率。

6. cpufreq notifiers

CPUFreq的通知系統(tǒng)使用了內(nèi)核的標(biāo)準(zhǔn)通知接口。它對(duì)外提供了兩個(gè)通知事件:policy通知和transition通知。

policy通知用于通知其它模塊cpu的policy需要改變,每次policy改變時(shí),該通知鏈上的回調(diào)將會(huì)用不同的事件參數(shù)被調(diào)用3次,分別是:

CPUFREQ_ADJUST 只要有需要,所有的被通知者可以在此時(shí)修改policy的限制信息,比如溫控系統(tǒng)可能會(huì)修改在大允許運(yùn)行的頻率。CPUFREQ_INCOMPATIBLE 只是為了避免硬件錯(cuò)誤的情況下,可以在該通知中修改policy的限制信息。CPUFREQ_NOTIFY 真正切換policy前,該通知會(huì)發(fā)往所有的被通知者。 transition通知鏈用于在驅(qū)動(dòng)實(shí)施調(diào)整cpu的頻率時(shí),用于通知相關(guān)的注冊(cè)者。每次調(diào)整頻率時(shí),該通知會(huì)發(fā)出兩次通知事件:CPUFREQ_PRECHANGE 調(diào)整前的通知。CPUFREQ_POSTCHANGE 完成調(diào)整后的通知。 當(dāng)檢測(cè)到因系統(tǒng)進(jìn)入suspend而造成頻率被改變時(shí),以下通知消息會(huì)被發(fā)出:CPUFREQ_RESUMECHANGE

核心(core)架構(gòu)與API

上面我們大致地講解了一下CPUFreq在用戶空間的sysfs接口和它的幾個(gè)重要的數(shù)據(jù)結(jié)構(gòu),同時(shí)也提到,CPUFreq子系統(tǒng)把一些公共的代碼邏輯組織在一起,構(gòu)成了CPUFreq的核心部分,這些公共邏輯向CPUFreq和其它內(nèi)核模塊提供了必要的API,像cpufreq_governor、cpufreq_driver等模塊通過(guò)這些API來(lái)完成一個(gè)完整的CPUFreq體系。這一節(jié)我們就來(lái)討論一下核心架構(gòu)的代碼架構(gòu)以及如何使用這些公共的API接口。

核心部分的代碼都在:/drivers/cpufreq/cpufreq.c中,本系列文章我使用的內(nèi)核版本是3.10.0.


1. CPUFreq子系統(tǒng)的初始化

先看看具體的代碼:

static int __init cpufreq_core_init(void){ int cpu; if (cpufreq_disabled()) return -ENODEV; for_each_possible_cpu(cpu) { per_cpu(cpufreq_policy_cpu, cpu) = -1; init_rwsem(&per_cpu(cpu_policy_rwsem, cpu)); } cpufreq_global_kobject = kobject_create_and_add("cpufreq", &cpu_subsys.dev_root->kobj); BUG_ON(!cpufreq_global_kobject); register_syscore_ops(&cpufreq_syscore_ops); return 0;}core_initcall(cpufreq_core_init);

可見(jiàn),在系統(tǒng)的啟動(dòng)階段,經(jīng)由initcall機(jī)制,cpufreq_core_init被調(diào)用,由它來(lái)完成核心部分的初始化工作,其中: cpufreq_policy_cpu 是一個(gè)per_cpu變量,在smp的系統(tǒng)下,每個(gè)cpu可以有自己獨(dú)立的調(diào)頻policy,也可以所有的cpu都是用一種policy,這時(shí)候就有可能出現(xiàn)其中一個(gè)cpu管理著某個(gè)policy,而其它c(diǎn)pu因?yàn)橐彩褂猛粋€(gè)policy,這些cpu的policy的就交由那個(gè)管理cpu代管,這個(gè)per_cpu變量就是用來(lái)記錄各個(gè)cpu的policy實(shí)際上是由那個(gè)cpu進(jìn)行管理的。初始化時(shí)都被初始化為-1了,代表現(xiàn)在還沒(méi)有開(kāi)始進(jìn)行policy的管理。

接下來(lái)的kobject_create_and_add函數(shù)在/sys/devices/system/cpu這個(gè)節(jié)點(diǎn)下建立了一個(gè)cpufreq節(jié)點(diǎn),該節(jié)點(diǎn)的下面以后會(huì)用來(lái)放置當(dāng)前governor的一些配置參數(shù)。參數(shù)cpu_subsys是內(nèi)核的一個(gè)全局變量,是由更早期的初始化時(shí)初始化的,代碼在drivers/base/cpu.c中:

struct bus_type cpu_subsys = { .name = "cpu", .dev_name = "cpu",};EXPORT_SYMBOL_GPL(cpu_subsys);void __init cpu_dev_init(void){ if (subsys_system_register(&cpu_subsys, cpu_root_attr_groups)) panic("Failed to register CPU subsystem"); cpu_dev_register_generic();}

這將會(huì)建立一根cpu總線,總線下掛著系統(tǒng)中所有的cpu,cpu總線設(shè)備的根目錄就位于:/sys/devices/system/cpu,同時(shí),/sys/bus下也會(huì)出現(xiàn)一個(gè)cpu的總線節(jié)點(diǎn)。cpu總線設(shè)備的根目錄下會(huì)依次出現(xiàn)cpu0,cpu1,…… cpux節(jié)點(diǎn),每個(gè)cpu對(duì)應(yīng)其中的一個(gè)設(shè)備節(jié)點(diǎn)。CPUFreq子系統(tǒng)利用這個(gè)cpu_subsys來(lái)獲取系統(tǒng)中的cpu設(shè)備,并在這些cpu設(shè)備下面建立相應(yīng)的cpufreq對(duì)象,這個(gè)我們?cè)诤竺嬖儆懻摗?這樣看來(lái),cpufreq子系統(tǒng)的初始化其實(shí)沒(méi)有做什么重要的事情,只是初始化了幾個(gè)per_cpu變量和建立了一個(gè)cpufreq文件節(jié)點(diǎn)。下圖是初始化過(guò)程的序列圖:

圖 1.1 核心層初始化

2. 注冊(cè)cpufreq_governor

系統(tǒng)中可以同時(shí)存在多個(gè)governor策略,一個(gè)policy通過(guò)cpufreq_policy結(jié)構(gòu)中的governor指針和某個(gè)governor相關(guān)聯(lián)。要想一個(gè)governor被policy使用,首先要把該governor注冊(cè)到cpufreq的核心中,我們可以通過(guò)核心層提供的API來(lái)完成注冊(cè):

int cpufreq_register_governor(struct cpufreq_governor *governor){ int err; ...... governor->initialized = 0; err = -EBUSY; if (__find_governor(governor->name) == NULL) { err = 0; list_add(&governor->governor_list, &cpufreq_governor_list); } ...... return err;}

核心層定義了一個(gè)全局鏈表變量:cpufreq_governor_list,注冊(cè)函數(shù)首先根據(jù)governor的名稱,通過(guò)__find_governor()函數(shù)查找該governor是否已經(jīng)被注冊(cè)過(guò),如果沒(méi)有被注冊(cè)過(guò),則把代表該governor的結(jié)構(gòu)體添加到cpufreq_governor_list鏈表中。在上一篇中我們提到,目前的內(nèi)核版本提供了5種governor供我們使用,我們可以通過(guò)內(nèi)核的配置項(xiàng)來(lái)選擇需要編譯的governor,同時(shí)需要指定一個(gè)默認(rèn)的governor。在cpufreq.h中,將會(huì)根據(jù)配置項(xiàng)的選擇,把CPUFREQ_DEFAULT_GOVERNOR宏指向默認(rèn)governor結(jié)構(gòu)體變量的地址,在注冊(cè)cpufreq_driver的階段需要使用這個(gè)宏來(lái)設(shè)定系統(tǒng)默認(rèn)使用的governor。


3. 注冊(cè)一個(gè)cpufreq_driver驅(qū)動(dòng)

與governor不同,系統(tǒng)中只會(huì)存在一個(gè)cpufreq_driver驅(qū)動(dòng),根據(jù)上一篇Linux動(dòng)態(tài)頻率調(diào)節(jié)系統(tǒng)CPUFreq之一:概述的介紹,cpufreq_driver是平臺(tái)相關(guān)的,負(fù)責(zé)最終實(shí)施頻率的調(diào)整動(dòng)作,而選擇工作頻率的策略是由governor完成的。所以,系統(tǒng)中只需要注冊(cè)一個(gè)cpufreq_driver即可,它只負(fù)責(zé)知道如何控制該平臺(tái)的時(shí)鐘系統(tǒng),從而設(shè)定由governor確定的工作頻率。注冊(cè)cpufreq_driver驅(qū)動(dòng)會(huì)觸發(fā)cpufreq核心的一系列額外的初始化動(dòng)作,第一節(jié)所說(shuō)的核心初始化工作非常簡(jiǎn)單,實(shí)際上,更多的初始化動(dòng)作在注冊(cè)cpufreq_driver階段完成。核心提供了一個(gè)API:cpufreq_register_driver來(lái)完成注冊(cè)工作。下面我們分析一下這個(gè)函數(shù)的工作過(guò)程:

int cpufreq_register_driver(struct cpufreq_driver *driver_data){ ...... if (cpufreq_disabled()) return -ENODEV; if (!driver_data || !driver_data->verify || !driver_data->init || ((!driver_data->setpolicy) && (!driver_data->target))) return -EINVAL;

該API只有一個(gè)參數(shù):一個(gè)cpufreq_driver指針,driver_data,該結(jié)構(gòu)事先在驅(qū)動(dòng)的代碼中定義,調(diào)用該API時(shí)作為參數(shù)傳入。函數(shù)先判斷系統(tǒng)目前是否禁止了調(diào)頻功能,然后檢查cpufreq_driver的幾個(gè)回調(diào)函數(shù)是否被實(shí)現(xiàn),由代碼可以看出,verify和init回調(diào)函數(shù)必須要實(shí)現(xiàn),而setpolicy和target回調(diào)則至少要被實(shí)現(xiàn)其中的一個(gè)。這幾個(gè)回調(diào)的作用請(qǐng)參考本系列的第一篇文章。接下來(lái):

write_lock_irqsave(&cpufreq_driver_lock, flags); if (cpufreq_driver) { write_unlock_irqrestore(&cpufreq_driver_lock, flags); return -EBUSY; } cpufreq_driver = driver_data; write_unlock_irqrestore(&cpufreq_driver_lock, flags);

檢查全局變量cpufreq_driver是否已經(jīng)被賦值,如果沒(méi)有,則傳入的參數(shù)被賦值給全局變量cpufreq_driver,從而保證了系統(tǒng)中只會(huì)注冊(cè)一個(gè)cpufreq_driver驅(qū)動(dòng)。然后:

ret = subsys_interface_register(&cpufreq_interface); ...... ...... register_hotcpu_notifier(&cpufreq_cpu_notifier);

通過(guò)subsys_interface_register給每一個(gè)cpu建立一個(gè)cpufreq_policy,最后注冊(cè)cpu hot plug通知,以便在cpu hot plug的時(shí)候,能夠動(dòng)態(tài)地處理各個(gè)cpu policy之間的關(guān)系(比如遷移負(fù)責(zé)管理的cpu等等)。這里要重點(diǎn)討論一下subsys_interface_register的過(guò)程,回到第一節(jié)的內(nèi)容,我們知道初始化階段,cpu_subsys被建立,從而每個(gè)cpu都會(huì)在cpu總線設(shè)備下建立一個(gè)屬于自己的設(shè)備:sys/devices/system/cpu/cpux。subsys_interface_register負(fù)責(zé)在cpu_subsys子系統(tǒng)的子設(shè)備下面注冊(cè)公共的接口。我們看看參數(shù)cpufreq_interface的定義:

static struct subsys_interface cpufreq_interface = { .name = "cpufreq", .subsys = &cpu_subsys, .add_dev = cpufreq_add_dev, .remove_dev = cpufreq_remove_dev,};

subsys_interface_register函數(shù)的代碼我就不再展開(kāi)了,它的大致作用就是:遍歷子系統(tǒng)下面的每一個(gè)子設(shè)備,然后用這個(gè)子設(shè)備作為參數(shù),調(diào)用cpufrq_interface結(jié)構(gòu)的add_dev回調(diào)函數(shù),這里的回調(diào)函數(shù)被指向了cpufreq_add_dev,它的具體工作方式我們?cè)谙乱还?jié)中討論。 driver注冊(cè)完成后,驅(qū)動(dòng)被保存在全局變量cpufreq_driver中,供核心層使用,同時(shí),每個(gè)cpu也會(huì)建立自己的policy策略,governor也開(kāi)始工作,實(shí)時(shí)地監(jiān)控著cpu的負(fù)載并計(jì)算合適的工作頻率,然后通過(guò)driver調(diào)整真正的工作頻率。下圖是cpufreq_driver注冊(cè)過(guò)程的序列圖:

圖 3.1 cpufreq_driver的注冊(cè)過(guò)程


4. 為每個(gè)cpu建立頻率調(diào)整策略(policy)

為每個(gè)cpu建立頻率調(diào)整策略實(shí)在注冊(cè)cpufreq_driver階段的subsys_interface_registe函數(shù)中完成的,上一節(jié)已經(jīng)提到,該函數(shù)最終會(huì)調(diào)用cpufreq_add_dev回調(diào)函數(shù),現(xiàn)在展開(kāi)這個(gè)函數(shù)分析一下:

因?yàn)閟ubsys_interface_registe會(huì)枚舉各個(gè)cpu設(shè)備,不管該cpu處于offline還是online狀態(tài),cpufreq_add_dev都會(huì)被調(diào)用,所以函數(shù)的一開(kāi)始,判斷如果cpu處于offline狀態(tài),直接返回。

static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif){ ...... if (cpu_is_offline(cpu)) return 0;

如果是smp系統(tǒng),本cpu的policy可能和其他cpu共同使用同一個(gè)policy,并委托另一個(gè)叫做管理cpu的cpu進(jìn)行管理,下面的代碼判斷這種情況,如果已經(jīng)委托別的cpu管理,則直接返回,核心層定義了另一個(gè)per_cpu變量:cpufreq_cpu_data,用來(lái)保存各個(gè)cpu所使用的cpufreq_policy結(jié)構(gòu)的指針,cpufreq_cpu_get函數(shù)實(shí)際上就是通過(guò)這個(gè)per_cpu變量,獲取該指針,如果該指針?lè)?,代表該cpu已經(jīng)建立好了它自身的policy(可能是在他之前的管理cpu建立policy期間一并建立的)。

policy = cpufreq_cpu_get(cpu); if (unlikely(policy)) { cpufreq_cpu_put(policy); return 0; }

因?yàn)閏pu hot plug期間,cpufreq_add_dev也會(huì)被調(diào)用,下面的代碼片段檢測(cè)該cpu之前是否被hot-unpluged過(guò),如果是,找到其中一個(gè)相關(guān)的cpu(這些相關(guān)的cpu都委托給同一個(gè)托管它c(diǎn)pu進(jìn)行管理,調(diào)用cpufreq_add_policy_cpu函數(shù),該函數(shù)只是簡(jiǎn)單地建立一個(gè)cpufreq鏈接,鏈接到管理cpu的cpufreq節(jié)點(diǎn)。

for_each_online_cpu(sibling) { struct cpufreq_policy *cp = per_cpu(cpufreq_cpu_data, sibling); if (cp && cpumask_test_cpu(cpu, cp->related_cpus)) { read_unlock_irqrestore(&cpufreq_driver_lock, flags); return cpufreq_add_policy_cpu(cpu, sibling, dev); } }

當(dāng)這是系統(tǒng)初始化階段第一次調(diào)用cpufreq_add_dev時(shí)(subsys_interface_register枚舉到的第一個(gè)cpu,通常就是cpu0),cpufreq_cpu_data應(yīng)該為NULL,所以我們要為這樣的cpu分配一個(gè)cpufreq_policy結(jié)構(gòu),并初始化該policy所管理的cpu,包括online的cpus字段和online+offline的cpu_related字段,并把自己設(shè)置為這個(gè)policy的管理cpu,使用默認(rèn)governor初始化policy->governor字段,同時(shí)吧自己加入到online的cpus字段中:

policy = kzalloc(sizeof(struct cpufreq_policy), GFP_KERNEL); if (!policy) goto nomem_out; if (!alloc_cpumask_var(&policy->cpus, GFP_KERNEL)) goto err_free_policy; if (!zalloc_cpumask_var(&policy->related_cpus, GFP_KERNEL)) goto err_free_cpumask; policy->cpu = cpu; policy->governor = CPUFREQ_DEFAULT_GOVERNOR; cpumask_copy(policy->cpus, cpumask_of(cpu)); /* Initially set CPU itself as the policy_cpu */ per_cpu(cpufreq_policy_cpu, cpu) = cpu;

接下來(lái)初始化一個(gè)供kobject系統(tǒng)注銷時(shí)使用的同步變量,初始化一個(gè)workqueue,某些時(shí)候不能馬上執(zhí)行對(duì)該policy的更新操作,可以使用該workqueue來(lái)延遲執(zhí)行。

init_completion(&policy->kobj_unregister); INIT_WORK(&policy->update, handle_update);

接著,調(diào)用cpufreq_driver的init回調(diào),進(jìn)一步初始化該policy:

ret = cpufreq_driver->init(policy); if (ret) { pr_debug("initialization failed/n"); goto err_set_policy_cpu; }

在上述驅(qū)動(dòng)的初始化內(nèi)部,應(yīng)該完成以下工作:

設(shè)定該cpu的最大和最小工作頻率設(shè)定該policy的最大和最小工作頻率設(shè)定該policy可供調(diào)節(jié)的頻率檔位設(shè)定cpu調(diào)節(jié)頻率時(shí)的延遲時(shí)間特性該policy可以管理的cpu個(gè)數(shù)(policy->cpus) 繼續(xù): /* related cpus should atleast have policy->cpus */ cpumask_or(policy->related_cpus, policy->related_cpus, policy->cpus);

注釋已經(jīng)寫的很清楚了,把online的cpu加到代表online+offline的related字段中。接著,剔除offline的cpu:

cpumask_and(policy->cpus, policy->cpus, cpu_online_mask);

然后,發(fā)出CPUFREQ_START通知:

blocking_notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_START, policy);

如果是hot-plug加入的cpu,找出它上次使用的governor:

#ifdef CONFIG_HOTPLUG_CPU gov = __find_governor(per_cpu(cpufreq_cpu_governor, cpu)); if (gov) { policy->governor = gov; pr_debug("Restoring governor %s for cpu %d/n", policy->governor->name, cpu); }#endif

最后,建立cpu設(shè)備下的sysfs文件節(jié)點(diǎn):cpufreq,它的完整路徑是:/sys/devices/system/cpu/cpux/cpufreq,同時(shí),在他的下面,相應(yīng)的sysfs節(jié)點(diǎn)也同時(shí)被建立,節(jié)點(diǎn)的內(nèi)容請(qǐng)參考本系列的第一篇文章:Linux動(dòng)態(tài)頻率調(diào)節(jié)系統(tǒng)CPUFreq之一:概述:

ret = cpufreq_add_dev_interface(cpu, policy, dev);

至此,一個(gè)cpu的policy建立完成,它的頻率限制條件、使用的governor策略,sysfs文件節(jié)點(diǎn)都已經(jīng)建立完成。需要注意點(diǎn)是,系統(tǒng)中有多少個(gè)cpu,cpufreq_add_dev函數(shù)就會(huì)被調(diào)用多少次,最后,每個(gè)cpu都會(huì)建立自己的policy,當(dāng)然,也有可能只有部分cpu建立了真正的policy,而其它c(diǎn)pu則委托這些cpu進(jìn)行policy的管理,關(guān)于這一點(diǎn),一開(kāi)始讀代碼的時(shí)候可能有點(diǎn)困擾,為了搞清楚他們之間的關(guān)系,我們?cè)俑隿pufreq_add_dev_interface函數(shù)看看:

static int cpufreq_add_dev_interface(unsigned int cpu, struct cpufreq_policy *policy, struct device *dev){ ...... /* prepare interface data */ ret = kobject_init_and_add(&policy->kobj, &ktype_cpufreq, &dev->kobj, "cpufreq"); ...... /* set up files for this cpu device */ drv_attr = cpufreq_driver->attr; while ((drv_attr) && (*drv_attr)) { ret = sysfs_create_file(&policy->kobj, &((*drv_attr)->attr)); if (ret) goto err_out_kobj_put; drv_attr++; }

函數(shù)的一開(kāi)始,建立cpufreq文件節(jié)點(diǎn),然后在它的下面再建立一系列節(jié)點(diǎn),用戶可以通過(guò)這些文件節(jié)點(diǎn)控制該policy的一些參數(shù)。這不是我們的重點(diǎn),我們看下面這一段代碼:

for_each_cpu(j, policy->cpus) { per_cpu(cpufreq_cpu_data, j) = policy; per_cpu(cpufreq_policy_cpu, j) = policy->cpu; }

前面的代碼已經(jīng)設(shè)定了該policy所管理的online cpu:policy->cpus,通過(guò)兩個(gè)per_cpu變量,這里把每個(gè)online cpu的policy都設(shè)置為了本cpu(管理cpu)的policy,并且把所有online的cpu的管理cpu也指定為了本cpu。接下來(lái),cpufreq_add_dev_symlink被調(diào)用,所有policy->cpus指定的cpu會(huì)建立一個(gè)cpufreq鏈接,指向本cpu(管理cpu)的真實(shí)cpufreq節(jié)點(diǎn):

ret = cpufreq_add_dev_symlink(cpu, policy);

注意,假如這時(shí)的cpu是cpu0,也就是說(shuō),其它c(diǎn)pu的cpufreq_add_dev還沒(méi)有被調(diào)用,但是在cpufreq_cpu_data中,與之對(duì)應(yīng)的policy指針已經(jīng)被賦值為cpu0所對(duì)應(yīng)的policy,這樣,回到cpufreq_add_dev函數(shù)的開(kāi)頭部分,當(dāng)接下其它被認(rèn)為使用cpu0托管他們的policy的cpu也會(huì)進(jìn)入cpufreq_add_dev函數(shù),但是,因?yàn)閏pufreq_cpu_data中對(duì)應(yīng)的policy已經(jīng)在cpu0的建立階段被賦值,所以這些cpu他們不會(huì)走完所有的流程,在函數(shù)的開(kāi)頭的判斷部分,判斷cpufreq_cpu_data中cpu對(duì)應(yīng)的policy已經(jīng)被賦值,就直接返回了。 接著往下看cpufreq_add_dev_interface的代碼:

memcpy(&new_policy, policy, sizeof(struct cpufreq_policy)); /* assure that the starting sequence is run in __cpufreq_set_policy */ policy->governor = NULL; /* set default policy */ ret = __cpufreq_set_policy(policy, &new_policy); policy->user_policy.policy = policy->policy; policy->user_policy.governor = policy->governor;

通過(guò)__cpufreq_set_policy函數(shù),最終使得該policy正式生效。到這里,每個(gè)cpu的policy已經(jīng)建立完畢,并正式開(kāi)始工作。關(guān)于__cpufreq_set_policy的代碼這里就不展開(kāi)了,我只給出它的序列圖:

圖 4.1 設(shè)置一個(gè)cpufreq_policy


5. 其它API

cpufreq的核心層除了提供上面幾節(jié)討論的注冊(cè)governor,注冊(cè)cpufreq_driver等API外,還提供了其他一些輔助的API,以方便其它模塊的使用。

int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list);int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int list);

以上兩個(gè)API用于注冊(cè)和注銷cpufreq系統(tǒng)的通知消息,第二個(gè)參數(shù)可以選擇通知的類型,可以有以下兩種類型:

CPUFREQ_TRANSITION_NOTIFIER 收到頻率變更通知CPUFREQ_POLICY_NOTIFIER 收到policy更新通知int cpufreq_driver_target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation);int __cpufreq_driver_target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation);

以上兩個(gè)API用來(lái)設(shè)置cpu的工作頻率,區(qū)別在于cpufreq_driver_target是帶鎖的版本,而__cpufreq_driver_target是不帶鎖的版本,如果確定是在governor的上下文中,使用不帶鎖的版本,否則需要使用帶鎖的版本。

void cpufreq_verify_within_limits(struct cpufreq_policy *policy, unsigned int min, unsigned int max);

這個(gè)API用來(lái)檢查并重新設(shè)定policy的最大和最小頻率。

int cpufreq_update_policy(unsigned int cpu);

這個(gè)API用來(lái)觸發(fā)cpufreq核心進(jìn)行policy的更新操作。

governor

在上一篇文章中,介紹了cpufreq的core層,core提供了cpufreq系統(tǒng)的初始化,公共數(shù)據(jù)結(jié)構(gòu)的建立以及對(duì)cpufreq中其它子部件提供注冊(cè)功能。core的最核心功能是對(duì)policy的管理,一個(gè)policy通過(guò)cpufreq_policy結(jié)構(gòu)中的governor字段,和某個(gè)governor相關(guān)聯(lián),本章的內(nèi)容正是要對(duì)governor進(jìn)行討論。 通過(guò)前面兩篇文章的介紹,我們知道,governor的作用是:檢測(cè)系統(tǒng)的負(fù)載狀況,然后根據(jù)當(dāng)前的負(fù)載,選擇出某個(gè)可供使用的工作頻率,然后把該工作頻率傳遞給cpufreq_driver,完成頻率的動(dòng)態(tài)調(diào)節(jié)。內(nèi)核默認(rèn)提供了5種governor供我們使用,在之前的內(nèi)核版本中,每種governor幾乎是獨(dú)立的代碼,它們各自用自己的方式實(shí)現(xiàn)對(duì)系統(tǒng)的負(fù)載進(jìn)行監(jiān)測(cè),很多時(shí)候,檢測(cè)的邏輯其實(shí)是很相似的,各個(gè)governor最大的不同之處其實(shí)是根據(jù)檢測(cè)的結(jié)果,選擇合適頻率的策略。所以,為了減少代碼的重復(fù),在我現(xiàn)在分析的內(nèi)核版本中(3.10.0),一些公共的邏輯代碼被單獨(dú)抽象出來(lái),單獨(dú)用一個(gè)文件來(lái)實(shí)現(xiàn):/drivers/cpufreq/cpufreq_governor.c,而各個(gè)具體的governor則分別有自己的代碼文件,如:cpufreq_ondemand.c,cpufreq_performance.c。下面我們先從公共部分討論。


1. 數(shù)據(jù)結(jié)構(gòu)

cpu_dbs_common_info 該結(jié)構(gòu)把對(duì)計(jì)算cpu負(fù)載需要使用到的一些輔助變量整合在了一起,通常,每個(gè)cpu都需要一個(gè)cpu_dbs_common_info結(jié)構(gòu)體,該結(jié)構(gòu)體中的成員會(huì)在governor的生命周期期間進(jìn)行傳遞,以用于統(tǒng)計(jì)當(dāng)前cpu的負(fù)載,它的定義如下:

/* Per cpu structures */struct cpu_dbs_common_info { int cpu; u64 prev_cpu_idle; u64 prev_cpu_wall; u64 prev_cpu_nice; struct cpufreq_policy *cur_policy; struct delayed_work work; struct mutex timer_mutex; ktime_t time_stamp;};cpu 與該結(jié)構(gòu)體相關(guān)聯(lián)的cpu編號(hào)。prev_cpu_idle 上一次統(tǒng)計(jì)時(shí)刻該cpu停留在idle狀態(tài)的總時(shí)間。prev_cpu_wall 上一次統(tǒng)計(jì)時(shí)刻對(duì)應(yīng)的總工作時(shí)間。cur_policy 指向該cpu所使用的cpufreq_policy結(jié)構(gòu)。work 工作隊(duì)列,該工作隊(duì)列會(huì)被定期地觸發(fā),然后定期地進(jìn)行負(fù)載的更新和統(tǒng)計(jì)工作。 dbs縮寫,實(shí)際是:demand based switching,通常,因?yàn)閏pu_dbs_common_info只包含了經(jīng)過(guò)抽象后的公共部分,所以,各個(gè)governor會(huì)自己定義的一個(gè)包含cpu_dbs_common_info的自定義結(jié)構(gòu),例如對(duì)于ondemand,他會(huì)定義:struct od_cpu_dbs_info_s { struct cpu_dbs_common_info cdbs; struct cpufreq_frequency_table *freq_table; unsigned int freq_lo; unsigned int freq_lo_jiffies; unsigned int freq_hi_jiffies; unsigned int rate_mult; unsigned int sample_type:1;};

而對(duì)于Conservative,他的定義如下:

struct cs_cpu_dbs_info_s { struct cpu_dbs_common_info cdbs; unsigned int down_skip; unsigned int requested_freq; unsigned int enable:1;};

把它理解為類似于C++語(yǔ)言的基類和子類的概念就是了。

common_dbs_data 各個(gè)獨(dú)立的governor,需要和governor的公共層交互,需要實(shí)現(xiàn)一套公共的接口,這個(gè)接口由common_dbs_data結(jié)構(gòu)來(lái)提供:

struct common_dbs_data { /* Common across governors */ #define GOV_ONDEMAND 0 #define GOV_CONSERVATIVE 1 int governor; struct attribute_group *attr_group_gov_sys; /* one governor - system */ struct attribute_group *attr_group_gov_pol; /* one governor - policy */ /* Common data for platforms that don't set have_governor_per_policy */ struct dbs_data *gdbs_data; struct cpu_dbs_common_info *(*get_cpu_cdbs)(int cpu); void *(*get_cpu_dbs_info_s)(int cpu); void (*gov_dbs_timer)(struct work_struct *work); void (*gov_check_cpu)(int cpu, unsigned int load); int (*init)(struct dbs_data *dbs_data); void (*exit)(struct dbs_data *dbs_data); /* Governor specific ops, see below */ void *gov_ops;};

主要的字段意義如下:

governor 因?yàn)閛ndemand和conservative的實(shí)現(xiàn)部分有很多相似的地方,用該字段做一區(qū)分,可以設(shè)置為GOV_ONDEMAND或GOV_CONSERVATIVE的其中之一。attr_group_gov_sys 該公共的sysfs屬性組。attr_group_gov_pol 各policy使用的屬性組,有時(shí)候多個(gè)policy會(huì)使用同一個(gè)governor算法。gdbs_data 通常,當(dāng)沒(méi)有設(shè)置have_governor_per_policy時(shí),表示所有的policy使用了同一種 - - governor,該字段指向該governor的dbs_data結(jié)構(gòu)。get_cpu_cdbs 回調(diào)函數(shù),公共層用它取得對(duì)應(yīng)cpu的cpu_dbs_common_info結(jié)構(gòu)指針。get_cpu_dbs_info_s 回調(diào)函數(shù),公共層用它取得對(duì)應(yīng)cpu的cpu_dbs_common_info_s的派生結(jié)構(gòu)指針,例如:od_cpu_dbs_info_s,cs_cpu_dbs_info_s。gov_dbs_timer 前面說(shuō)過(guò),cpu_dbs_common_info_s結(jié)構(gòu)中有一個(gè)工作隊(duì)列,該回調(diào)通常用作工作隊(duì)列的工作函數(shù)。gov_check_cpu 計(jì)算cpu負(fù)載的回調(diào)函數(shù),通常會(huì)直接調(diào)用公共層提供的dbs_check_cpu函數(shù)完成實(shí)際的計(jì)算工作。init 初始化回調(diào),用于完成該governor的一些額外的初始化工作。exit 回調(diào)函數(shù),governor被移除時(shí)調(diào)用。gov_ops 各個(gè)governor可以用該指針定義各自特有的一些操作接口。 dbs_data 該結(jié)構(gòu)體通常由governor的公共層代碼在governor的初始化階段動(dòng)態(tài)創(chuàng)建,該結(jié)構(gòu)的一個(gè)最重要的字段就是cdata:一個(gè)common_dbs_data結(jié)構(gòu)指針,另外,該結(jié)構(gòu)還包含一些定義governor工作方式的一些調(diào)節(jié)參數(shù)。該結(jié)構(gòu)的詳細(xì)定義如下:struct dbs_data { struct common_dbs_data *cdata; unsigned int min_sampling_rate; int usage_count; void *tuners; /* dbs_mutex protects dbs_enable in governor start/stop */ struct mutex mutex;};

幾個(gè)主要的字段:

cdata 一個(gè)common_dbs_data結(jié)構(gòu)指針,通常由具體governor的實(shí)現(xiàn)部分定義好,然后作為參數(shù),通過(guò)公共層的API:cpufreq_governor_dbs,傳遞到公共層,cpufreq_governor_dbs函數(shù)在創(chuàng)建好dbs_data結(jié)構(gòu)后,把該指針賦值給該字段。min_sampling_rate 用于記錄統(tǒng)計(jì)cpu負(fù)載的采樣周期。usage_count 當(dāng)沒(méi)有設(shè)置have_governor_per_policy時(shí),意味著所有的policy采用同一個(gè)governor,該字段就是用來(lái)統(tǒng)計(jì)目前該governor被多少個(gè)policy引用。tuners 指向governor的調(diào)節(jié)參數(shù)結(jié)構(gòu),不同的governor可以定義自己的tuner結(jié)構(gòu),公共層代碼會(huì)在governor的初始化階段調(diào)用common_dbs_data結(jié)構(gòu)的init回調(diào)函數(shù),governor的實(shí)現(xiàn)可以在init回調(diào)中初始化tuners字段。 如果設(shè)置了have_governor_per_policy,每個(gè)policy擁有各自獨(dú)立的governor,也就是說(shuō),擁有獨(dú)立的dbs_data結(jié)構(gòu),它會(huì)記錄在cpufreq_policy結(jié)構(gòu)的governor_data字段中,否則,如果沒(méi)有設(shè)置have_governor_per_policy,多個(gè)policy共享一個(gè)governor,和同一個(gè)dbs_data結(jié)構(gòu)關(guān)聯(lián),此時(shí),dbs_data被賦值在common_dbs_data結(jié)構(gòu)的gdbs_data字段中。

cpufreq_governor 這個(gè)結(jié)構(gòu)在本系列文章的第一篇已經(jīng)介紹過(guò)了,請(qǐng)參看Linux動(dòng)態(tài)頻率調(diào)節(jié)系統(tǒng)CPUFreq之一:概述。幾個(gè)數(shù)據(jù)結(jié)構(gòu)的關(guān)系如下圖所示:

圖 1.1 governor的數(shù)據(jù)結(jié)構(gòu)關(guān)系

下面我們以ondemand這個(gè)系統(tǒng)已經(jīng)實(shí)現(xiàn)的governor為例,說(shuō)明一下如何實(shí)現(xiàn)一個(gè)governor。具體的代碼請(qǐng)參看:/drivers/cpufreq/cpufreq_ondemand.c。


2. 定義一個(gè)governor

要實(shí)現(xiàn)一個(gè)governor,首先要定義一個(gè)cpufreq_governor結(jié)構(gòu),對(duì)于ondeman來(lái)說(shuō),它的定義如下:

struct cpufreq_governor cpufreq_gov_ondemand = { .name = "ondemand", .governor = od_cpufreq_governor_dbs, .max_transition_latency = TRANSITION_LATENCY_LIMIT, .owner = THIS_MODULE,};

其中,governor是這個(gè)結(jié)構(gòu)的核心字段,cpufreq_governor注冊(cè)后,cpufreq的核心層通過(guò)該字段操縱這個(gè)governor的行為,包括:初始化、啟動(dòng)、退出等工作。現(xiàn)在,該字段被設(shè)置為od_cpufreq_governor_dbs,我們看看它的實(shí)現(xiàn):

static int od_cpufreq_governor_dbs(struct cpufreq_policy *policy, unsigned int event){ return cpufreq_governor_dbs(policy, &od_dbs_cdata, event);}

只是簡(jiǎn)單地調(diào)用了governor的公共層提供的API:cpufreq_governor_dbs,關(guān)于這個(gè)API,我們?cè)诤竺鏁?huì)逐一進(jìn)行展開(kāi),這里我們注意到參數(shù):&od_dbs_cdata,正是我們前面討論過(guò)得common_dbs_data結(jié)構(gòu),作為和governor公共層的接口,在這里它的定義如下:

static struct common_dbs_data od_dbs_cdata = { .governor = GOV_ONDEMAND, .attr_group_gov_sys = &od_attr_group_gov_sys, .attr_group_gov_pol = &od_attr_group_gov_pol, .get_cpu_cdbs = get_cpu_cdbs, .get_cpu_dbs_info_s = get_cpu_dbs_info_s, .gov_dbs_timer = od_dbs_timer, .gov_check_cpu = od_check_cpu, .gov_ops = &od_ops, .init = od_init, .exit = od_exit,};

這里先介紹一下get_cpu_cdbs和get_cpu_dbs_info_s這兩個(gè)回調(diào),前面介紹cpu_dbs_common_info_s結(jié)構(gòu)的時(shí)候已經(jīng)說(shuō)過(guò),各個(gè)governor需要定義一個(gè)cpu_dbs_common_info_s結(jié)構(gòu)的派生結(jié)構(gòu),對(duì)于ondemand來(lái)說(shuō),這個(gè)派生結(jié)構(gòu)是:od_cpu_dbs_info_s。兩個(gè)回調(diào)函數(shù)分別用來(lái)獲得基類和派生類這兩個(gè)結(jié)構(gòu)的指針。我們先看看od_cpu_dbs_info_s是如何定義的:

static DEFINE_PER_CPU(struct od_cpu_dbs_info_s, od_cpu_dbs_info);

沒(méi)錯(cuò),它被定義為了一個(gè)per_cpu變量,也就是說(shuō),每個(gè)cpu擁有各自獨(dú)立的od_cpu_dbs_info_s,這很正常,因?yàn)槊總€(gè)cpu需要的實(shí)時(shí)負(fù)載是不一樣的,需要獨(dú)立的上下文變量來(lái)進(jìn)行負(fù)載的統(tǒng)計(jì)。前面也已經(jīng)列出了od_cpu_dbs_info_s的聲明,他的第一個(gè)字段cdbs就是一個(gè)cpu_dbs_common_info_s結(jié)構(gòu)。內(nèi)核為我們提供了一個(gè)輔助宏來(lái)幫助我們定義get_cpu_cdbs和get_cpu_dbs_info_s這兩個(gè)回調(diào)函數(shù):

#define define_get_cpu_dbs_routines(_dbs_info) /static struct cpu_dbs_common_info *get_cpu_cdbs(int cpu) /{ / return &per_cpu(_dbs_info, cpu).cdbs; /} / /static void *get_cpu_dbs_info_s(int cpu) /{ / return &per_cpu(_dbs_info, cpu); /}

所以,在cpufreq_ondemand.c中,我們只要簡(jiǎn)單地使用上述的宏即可定義這兩個(gè)回調(diào):

define_get_cpu_dbs_routines(od_cpu_dbs_info);

經(jīng)過(guò)上述這一系列的定義以后,governor的公共層即可通過(guò)這兩個(gè)回調(diào)獲取各個(gè)cpu所對(duì)應(yīng)的cpu_dbs_common_info_s和od_cpu_dbs_info_s的結(jié)構(gòu)指針,用來(lái)記錄本次統(tǒng)計(jì)周期的一些上下文參數(shù)(idle時(shí)間和運(yùn)行時(shí)間等等)。


3. 初始化一個(gè)governor

當(dāng)一個(gè)governor被policy選定后,核心層會(huì)通過(guò)__cpufreq_set_policy函數(shù)對(duì)該cpu的policy進(jìn)行設(shè)定,參看 Linux動(dòng)態(tài)頻率調(diào)節(jié)系統(tǒng)CPUFreq之二:核心(core)架構(gòu)與API中的第4節(jié)和圖4.1。如果policy認(rèn)為這是一個(gè)新的governor(和原來(lái)使用的舊的governor不相同),policy會(huì)通過(guò)__cpufreq_governor函數(shù),并傳遞CPUFREQ_GOV_POLICY_INIT參數(shù),而__cpufreq_governor函數(shù)實(shí)際上是調(diào)用cpufreq_governor結(jié)構(gòu)中的governor回調(diào)函數(shù),在第2節(jié)中我們已經(jīng)知道,這個(gè)回調(diào)最后會(huì)進(jìn)入governor公共API:cpufreq_governor_dbs,下面是它收到CPUFREQ_GOV_POLICY_INIT參數(shù)時(shí),經(jīng)過(guò)簡(jiǎn)化后的代碼片段:

case CPUFREQ_GOV_POLICY_INIT: ...... dbs_data = kzalloc(sizeof(*dbs_data), GFP_KERNEL); ...... dbs_data->cdata = cdata; dbs_data->usage_count = 1; rc = cdata->init(dbs_data); ...... rc = sysfs_create_group(get_governor_parent_kobj(policy), get_sysfs_attr(dbs_data)); ...... policy->governor_data = dbs_data; ...... /* Bring kernel and HW constraints together */ dbs_data->min_sampling_rate = max(dbs_data->min_sampling_rate, MIN_LATENCY_MULTIPLIER * latency); set_sampling_rate(dbs_data, max(dbs_data->min_sampling_rate, latency * LATENCY_MULTIPLIER)); if ((cdata->governor == GOV_CONSERVATIVE) && (!policy->governor->initialized)) { struct cs_ops *cs_ops = dbs_data->cdata->gov_ops; cpufreq_register_notifier(cs_ops->notifier_block, CPUFREQ_TRANSITION_NOTIFIER); } if (!have_governor_per_policy()) cdata->gdbs_data = dbs_data; return 0;

首先,它會(huì)給這個(gè)policy分配一個(gè)dbs_data實(shí)例,然后把通過(guò)參數(shù)cdata傳入的common_dbs_data指針,賦值給它的cdata字段,這樣,policy就可以通過(guò)該字段獲得governor的操作接口(通過(guò)cdata的一系列回調(diào)函數(shù))。然后,調(diào)用cdata的init回調(diào)函數(shù),對(duì)這個(gè)governor做進(jìn)一步的初始化工作,對(duì)于ondemand來(lái)說(shuō),init回調(diào)的實(shí)際執(zhí)行函數(shù)是:od_init,主要是完成和governor相關(guān)的一些調(diào)節(jié)參數(shù)的初始化,然后把初始化好的od_dbs_tuners結(jié)構(gòu)指針賦值到dbs_data的tuners字段中,它的詳細(xì)代碼這里就不貼出了。接著,通過(guò)sysfs_create_group函數(shù),建立該governor在sysfs中的節(jié)點(diǎn),以后我們就可以通過(guò)這些節(jié)點(diǎn)對(duì)該governor的算法邏輯進(jìn)行微調(diào),ondemand在我的電腦中,建立了以下這些節(jié)點(diǎn)(sys/devices/system/cpu/cpufreq/ondemand): sampling_rate; io_is_busy; up_threshold; sampling_down_factor; ignore_nice; powersave_bias; sampling_rate_min;

繼續(xù),把初始化好的dbs_data結(jié)構(gòu)賦值給policy的governor_data字段,以方便以后的訪問(wèn)。最后是通過(guò)set_sampling_rate設(shè)置governor的采樣周期,如果還有設(shè)置have_governor_per_policy,把dbs_data結(jié)構(gòu)指針賦值給cdata結(jié)構(gòu)的gdbs_data字段,至此,governor的初始化工作完成,下面是整個(gè)過(guò)程的序列圖:

圖 3.1 governor的初始化


4. 啟動(dòng)一個(gè)governor

核心層會(huì)通過(guò)__cpufreq_set_policy函數(shù),通過(guò)CPUFREQ_GOV_POLICY_INIT參數(shù),在公共層的API:cpufreq_governor_dbs中,完成了對(duì)governor的初始化工作,緊接著,__cpufreq_set_policy會(huì)通過(guò)CPUFREQ_GOV_START參數(shù),和初始化governor的流程一樣,最終會(huì)到達(dá)cpufreq_governor_dbs函數(shù)中,我們看看它是如何啟動(dòng)一個(gè)governor的:

case CPUFREQ_GOV_START: if (!policy->cur) return -EINVAL; mutex_lock(&dbs_data->mutex); for_each_cpu(j, policy->cpus) { struct cpu_dbs_common_info *j_cdbs = dbs_data->cdata->get_cpu_cdbs(j); j_cdbs->cpu = j; j_cdbs->cur_policy = policy; j_cdbs->prev_cpu_idle = get_cpu_idle_time(j, &j_cdbs->prev_cpu_wall, io_busy); if (ignore_nice) j_cdbs->prev_cpu_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE]; mutex_init(&j_cdbs->timer_mutex); INIT_DEFERRABLE_WORK(&j_cdbs->work, dbs_data->cdata->gov_dbs_timer); }

首先,遍歷使用該policy的所有的處于online狀態(tài)的cpu,針對(duì)每一個(gè)cpu,做以下動(dòng)作:

取出該cpu相關(guān)聯(lián)的cpu_dbs_common_info結(jié)構(gòu)指針,之前已經(jīng)討論過(guò),governor定義了一個(gè)per_cpu變量來(lái)定義各個(gè)cpu所對(duì)應(yīng)的cpu_dbs_common_info結(jié)構(gòu),通過(guò)common_dbs_data結(jié)構(gòu)的回調(diào)函數(shù)可以獲取該結(jié)構(gòu)的指針。

初始化cpu_dbs_common_info結(jié)構(gòu)的cpu,cur_policy,prev_cpu_idle,prev_cpu_wall,prev_cpu_nice字段,其中,prev_cpu_idle,prev_cpu_wall這兩個(gè)字段會(huì)被以后的負(fù)載計(jì)算所使用。

為每個(gè)cpu初始化一個(gè)工作隊(duì)列,工作隊(duì)列的執(zhí)行函數(shù)是common_dbs_data結(jié)構(gòu)中的gov_dbs_timer字段所指向的回調(diào)函數(shù),對(duì)于ondemand來(lái)說(shuō),該函數(shù)是:od_dbs_timer。這個(gè)工作隊(duì)列會(huì)被按照設(shè)定好的采樣率定期地被喚醒,進(jìn)行cpu負(fù)載的統(tǒng)計(jì)工作。

然后,記錄目前的時(shí)間戳,調(diào)度初始化好的工作隊(duì)列在稍后某個(gè)時(shí)間點(diǎn)運(yùn)行:

/* Initiate timer time stamp */ cpu_cdbs->time_stamp = ktime_get(); gov_queue_work(dbs_data, policy, delay_for_sampling_rate(sampling_rate), true);

下圖表達(dá)了啟動(dòng)一個(gè)governor的過(guò)程:

圖 4.1 啟動(dòng)一個(gè)governor

工作隊(duì)列被調(diào)度執(zhí)行后,會(huì)在工作隊(duì)列的執(zhí)行函數(shù)中進(jìn)行cpu負(fù)載的統(tǒng)計(jì)工作,這個(gè)我們?cè)谙乱还?jié)中討論。


5. 系統(tǒng)負(fù)載的檢測(cè)

上一節(jié)我們提到,核心層啟動(dòng)一個(gè)governor后,會(huì)在每個(gè)使用該governor的cpu上建立一個(gè)工作隊(duì)列,工作隊(duì)列的執(zhí)行函數(shù)是在common_dbs_data中g(shù)ov_dbs_timer字段所指向的函數(shù),理所當(dāng)然,該函數(shù)由各個(gè)governor的具體代碼來(lái)實(shí)現(xiàn),對(duì)于ondemand governor,它的實(shí)現(xiàn)函數(shù)是od_dbs_timer。governor的公共層代碼為我們提供了一個(gè)API:dbs_check_cpu,該API用來(lái)計(jì)算兩個(gè)統(tǒng)計(jì)周期期間某個(gè)cpu的負(fù)載情況,我們先分析一下dbs_check_cpu:

void dbs_check_cpu(struct dbs_data *dbs_data, int cpu){ struct cpu_dbs_common_info *cdbs = dbs_data->cdata->get_cpu_cdbs(cpu); ...... policy = cdbs->cur_policy; /* Get Absolute Load (in terms of freq for ondemand gov) */ for_each_cpu(j, policy->cpus) { struct cpu_dbs_common_info *j_cdbs; ...... j_cdbs = dbs_data->cdata->get_cpu_cdbs(j); ...... cur_idle_time = get_cpu_idle_time(j, &cur_wall_time, io_busy); wall_time = (unsigned int) (cur_wall_time - j_cdbs->prev_cpu_wall); j_cdbs->prev_cpu_wall = cur_wall_time; idle_time = (unsigned int) (cur_idle_time - j_cdbs->prev_cpu_idle); j_cdbs->prev_cpu_idle = cur_idle_time; ...... load = 100 * (wall_time - idle_time) / wall_time; ...... load *= cur_freq; /* 實(shí)際的代碼不是這樣,為了簡(jiǎn)化討論,精簡(jiǎn)為實(shí)際的計(jì)算邏輯*/ if (load > max_load) max_load = load; } dbs_data->cdata->gov_check_cpu(cpu, max_load);}

由代碼可以看出,遍歷該policy下每個(gè)online的cpu,取出該cpu對(duì)應(yīng)的cpu_dbs_common_info結(jié)構(gòu),該結(jié)構(gòu)中的prev_cpu_idle和prev_cpu_wall保存有上一次采樣周期時(shí)記錄的idle時(shí)間和運(yùn)行時(shí)間,負(fù)載的計(jì)算其實(shí)很簡(jiǎn)單:

idle_time = 本次idle時(shí)間 - 上次idle時(shí)間;wall_time = 本次總運(yùn)行時(shí)間 - 上次總運(yùn)行時(shí)間;負(fù)載load = 100 * (wall_time - idle_time)/ wall_time;把所有cpu中,負(fù)載最大值記入max_load中,作為選擇頻率的依據(jù);

計(jì)算出最大負(fù)載max_load后,調(diào)用具體governor實(shí)現(xiàn)的gov_check_cpu回調(diào)函數(shù),對(duì)于ondemand來(lái)說(shuō),該回調(diào)函數(shù)是:od_check_cpu,我們跟進(jìn)去看看:

static void od_check_cpu(int cpu, unsigned int load_freq){ struct od_cpu_dbs_info_s *dbs_info = &per_cpu(od_cpu_dbs_info, cpu); struct cpufreq_policy *policy = dbs_info->cdbs.cur_policy; struct dbs_data *dbs_data = policy->governor_data; struct od_dbs_tuners *od_tuners = dbs_data->tuners; dbs_info->freq_lo = 0; /* Check for frequency increase */ if (load_freq > od_tuners->up_threshold * policy->cur) { /* If switching to max speed, apply sampling_down_factor */ if (policy->cur < policy->max) dbs_info->rate_mult = od_tuners->sampling_down_factor; dbs_freq_increase(policy, policy->max); return; }

當(dāng)負(fù)載比預(yù)設(shè)的閥值高時(shí)(od_tuners->up_threshold,默認(rèn)值是95%),立刻選擇該policy最大的工作頻率作為接下來(lái)的工作頻率。如果負(fù)載沒(méi)有達(dá)到預(yù)設(shè)的閥值,但是當(dāng)前頻率已經(jīng)是最低頻率了,則什么都不做,直接返回:

if (policy->cur == policy->min) return;

運(yùn)行到這里,cpu的頻率可能已經(jīng)在上面的過(guò)程中被設(shè)置為最大頻率,實(shí)際上我們可能并不需要這么高的頻率,所以接著判斷,當(dāng)負(fù)載低于另一個(gè)預(yù)設(shè)值時(shí),這時(shí)需要計(jì)算一個(gè)合適于該負(fù)載的新頻率:

if (load_freq < od_tuners->adj_up_threshold * policy->cur) { unsigned int freq_next; freq_next = load_freq / od_tuners->adj_up_threshold; /* No longer fully busy, reset rate_mult */ dbs_info->rate_mult = 1; if (freq_next < policy->min) freq_next = policy->min; if (!od_tuners->powersave_bias) { __cpufreq_driver_target(policy, freq_next, CPUFREQ_RELATION_L); return; } freq_next = od_ops.powersave_bias_target(policy, freq_next, CPUFREQ_RELATION_L); __cpufreq_driver_target(policy, freq_next, CPUFREQ_RELATION_L); }}

對(duì)于ondemand來(lái)說(shuō),因?yàn)閭魅氲呢?fù)載是乘上了當(dāng)前頻率后的歸一化值,所以計(jì)算新頻率時(shí),直接用load_freq除以想要的負(fù)載即可。本來(lái)計(jì)算出來(lái)的頻率直接通過(guò)__cpufreq_driver_target函數(shù),交給cpufreq_driver調(diào)節(jié)頻率即可,但是這里的處理考慮了powersave_bias的設(shè)置情況,當(dāng)設(shè)置了powersave_bias時(shí),表明我們?yōu)榱诉M(jìn)一步節(jié)省電力,我們希望在計(jì)算出來(lái)的新頻率的基礎(chǔ)上,再乘以一個(gè)powersave_bias設(shè)定的百分比,作為真正的運(yùn)行頻率,powersave_bias的值從0-1000,每一步代表0.1%。實(shí)際的情況比想象中稍微復(fù)雜一點(diǎn),考慮到乘以一個(gè)powersave_bias后的新頻率可能不在cpu所支持的頻率表中,ondemand算法會(huì)在頻率表中查找,分別找出最接近新頻率的一個(gè)區(qū)間,由高低兩個(gè)頻率組成,低的頻率記入od_cpu_dbs_info_s結(jié)構(gòu)的freq_lo字段中,高的頻率通過(guò)od_ops.powersave_bias_target回調(diào)返回。同時(shí),od_ops.powersave_bias_target回調(diào)函數(shù)還計(jì)算出高低兩個(gè)頻率應(yīng)該運(yùn)行的時(shí)間,分別記入od_cpu_dbs_info_s結(jié)構(gòu)的freq_hi_jiffies和freq_low_jiffies字段中。原則是,通過(guò)兩個(gè)不同頻率的運(yùn)行時(shí)間的組合,使得綜合結(jié)果接近我們想要的目標(biāo)頻率。詳細(xì)的計(jì)算邏輯請(qǐng)參考函數(shù):generic_powersave_bias_target。 討論完上面兩個(gè)函數(shù),讓我們回到本節(jié)的開(kāi)頭,負(fù)載的計(jì)算工作是在一個(gè)工作隊(duì)列中發(fā)起的,前面說(shuō)過(guò),ondemand對(duì)應(yīng)的工作隊(duì)列的工作函數(shù)是od_dbs_timer,我們看看他的實(shí)現(xiàn)代碼:

static void od_dbs_timer(struct work_struct *work){ ...... /* Common NORMAL_SAMPLE setup */ core_dbs_info->sample_type = OD_NORMAL_SAMPLE; if (sample_type == OD_SUB_SAMPLE) { delay = core_dbs_info->freq_lo_jiffies; __cpufreq_driver_target(core_dbs_info->cdbs.cur_policy, core_dbs_info->freq_lo, CPUFREQ_RELATION_H); } else { dbs_check_cpu(dbs_data, cpu); if (core_dbs_info->freq_lo) { /* Setup timer for SUB_SAMPLE */ core_dbs_info->sample_type = OD_SUB_SAMPLE; delay = core_dbs_info->freq_hi_jiffies; } }max_delay: if (!delay) delay = delay_for_sampling_rate(od_tuners->sampling_rate * core_dbs_info->rate_mult); gov_queue_work(dbs_data, dbs_info->cdbs.cur_policy, delay, modify_all); mutex_unlock(&core_dbs_info->cdbs.timer_mutex);}

如果sample_type是OD_SUB_SAMPLE時(shí),表明上一次采樣時(shí),需要用高低兩個(gè)頻率來(lái)模擬實(shí)際的目標(biāo)頻率中的第二步:需要運(yùn)行freq_lo,并且持續(xù)時(shí)間為freq_lo_jiffies。否則,調(diào)用公共層計(jì)算負(fù)載的API:dbs_check_cpu,開(kāi)始一次新的采樣,當(dāng)powersave_bias沒(méi)有設(shè)置時(shí),該函數(shù)返回前,所需要的新的目標(biāo)頻率會(huì)被設(shè)置,考慮到powersave_bias的設(shè)置情況,判斷一下如果freq_lo被設(shè)置,說(shuō)明需要用高低兩個(gè)頻率來(lái)模擬實(shí)際的目標(biāo)頻率,高頻率已經(jīng)在dbs_check_cpu返回前被設(shè)置(實(shí)際的設(shè)置工作是在od_check_cpu中),所以把sample_type設(shè)置為OD_SUB_SAMPLE,以便下一次運(yùn)行工作函數(shù)進(jìn)行采樣時(shí)可以設(shè)置低頻率運(yùn)行。最后,調(diào)度工作隊(duì)列在下一個(gè)采樣時(shí)刻再次運(yùn)行,這樣,cpu的工作頻率實(shí)現(xiàn)了在每個(gè)采樣周期,根據(jù)實(shí)際的負(fù)載情況,動(dòng)態(tài)地設(shè)定合適的工作頻率進(jìn)行運(yùn)行,既滿足了性能的需求,也降低了系統(tǒng)的功耗,達(dá)到了cpufreq系統(tǒng)的最終目的,整個(gè)流程可以參考下

圖 5.1 負(fù)載計(jì)算和頻率選擇

5. 各種Governors說(shuō)明

1.Performance governor: Highest frequency

設(shè)置當(dāng)前處理的頻率到最高頻率,后一直保持在這個(gè)頻率這個(gè)Governors目標(biāo)就是讓系統(tǒng)輸出最高performance

2.Powersave Governor:Lowest Frequency

設(shè)置當(dāng)前處理器頻率在最低的頻率,然后保持在這一頻率目標(biāo)就是設(shè)置當(dāng)設(shè)置當(dāng)前處理器的頻率在最低的頻率,不管系統(tǒng)有多忙都不提高處理器頻率。這個(gè)Governor看著會(huì)省電,但實(shí)際并不會(huì)省。因?yàn)閜erformance降低,處理task時(shí)間變長(zhǎng),所以相比其他,其進(jìn)入睡眠的時(shí)間也最慢 3.Userspace governor: Manual frequencies允許用戶手動(dòng)選擇一組頻率這種模式通常與運(yùn)行在userspace的daemon進(jìn)程配合,去控制處理器頻率

4.Ondemand governor: Frequency change based on processor use

在2.6.10內(nèi)核開(kāi)始使用,這也是第一個(gè)根據(jù)cpu使用狀況來(lái)動(dòng)態(tài)調(diào)整cpu頻率的governorOndemand governor會(huì)檢查當(dāng)前cpu使用情況,如果其load超過(guò)某一閾值(threshold),就會(huì)把當(dāng)前cpu頻率設(shè)定到最高的頻率。然后發(fā)現(xiàn)load低于閾值就會(huì)逐步降低cpu頻率。

5.Conservative governor: A more gradual ondemand

這個(gè)governor基于ondemand governor,從2.6.12內(nèi)核開(kāi)始導(dǎo)入

它會(huì)檢查cpu load高于或者低于某一閾值,如果cpu load比閾值高,則逐步提高cpu頻率,如果發(fā)現(xiàn)cpu load比閾值低,則逐步減小cpu頻率

6.interactive governor : 看后面具體的介紹

6.詳細(xì)介紹每種governor

CPU Load計(jì)算方法:

load_freq = load * current frequencyif (load_freq > (up_threshold * current frequency)) next_freq = max_frequency (CPUFREQ_RELATION_H)if (load_freq < ((up_threshold - down_differential) * current frequency)) next_freq = load_freq / (up_threshold - down_differential) (CPUFREQ_RELATION_L)

Ex) if load=30, current frequency=800MHz next_freq= (30 * 800MHz) / (80 - 10) = 342MHz CPUFREQ_RELATION_L means higher than or equal target frequency ? Select 500MHz

6.1 Ondemand governor

如果cpu load超過(guò)某一閾值(up_threashould),ondemand governor會(huì)把cpu頻率提高到最高(scaling_max_freq)。up_threshold的默認(rèn)值為80.如果cpu load低于up_threshold,ondemand governor會(huì)把cpu頻率降低一檔。最低可以到scaling_min_freq。每個(gè)sampling_rate(默認(rèn)40ms),每個(gè)cpu load都會(huì)被重新計(jì)算ignore_nice: 有正數(shù)nice值的cpu的load,將不會(huì)被計(jì)入到整個(gè)cpu load當(dāng)中去。powersave_bias: 有些更強(qiáng)調(diào)省電的場(chǎng)景下,設(shè)置這個(gè)值(0.1~100%)可以減小cpu頻率以達(dá)到省電的目的。

6.2 Interactive governor

Designed for latency-sensitive, interactive workloadsIt is more aggressive about scaling the CPU speed up in response to CPU-intensive activity. Instead of sampling the CPU at a specified rate, the interactive governor will check whether to scale the CPU frequency up soon after coming out of idle.min_sample_time: The minimum amount of time to spend at the current frequency before ramping down. This is to ensure that the governor has seen enough historic CPU load data to determine the appropriate workload. Default is 80000 us.go_maxspeed_load: The CPU load at which to ramp to max speed. Default is 85.

The relation between NO_HZ and timer used on CPUFREQ

If NO_HZ is enabled, PERIODIC Timer(periodic tick, tick_sched_timer()) isn’t happened during IDLE state. But, If user registers ONESHOT Timer on each device, ONESHOT Timer is happened during IDLE state.If CPUFREQ governor uses the work-queue to check CPU load periodically, work-queue is executed during IDLE with NO_Hz. Because work-queue is not Timer.
發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 集安市| 汤原县| 洛阳市| 城固县| 眉山市| 霍城县| 平顺县| 建阳市| 军事| 同德县| 龙山县| 五莲县| 绥棱县| 尼木县| 华阴市| 页游| 竹溪县| 西藏| 信丰县| 潼南县| 定兴县| 霍林郭勒市| 天峨县| 阿拉尔市| 九台市| 泸定县| 沙湾县| 富源县| 灵台县| 托克逊县| 雷州市| 长葛市| 嘉善县| 扎鲁特旗| 新绛县| 封开县| 屏山县| 寿光市| 竹溪县| 舞阳县| 揭阳市|