interactive governor study for android
interactive governor全部思考思路:
1、Interactive governor 初始化,一些定時器綁定的函數(shù),創(chuàng)建內(nèi)核線程進行調(diào)頻并注冊interative governor(對應(yīng)cpufreq_interactive_initfunction)。
?
2、注冊governor執(zhí)行函數(shù):cpufreq_governor_interactive這個函數(shù)通過三個不同的event來處理不同的情況,分別如下:
CPUFREQ_GOV_START:用來對每個CPU對應(yīng)的Governor info進行初始化操作并創(chuàng)建相應(yīng)的governor的sysfs interface,并注冊idle和頻率改變通知鏈。由于interactive governor是通過定時器來實時的統(tǒng)計cpuload,所以這個governor一啟動的時候就必須設(shè)定相關(guān)的定時器。通過函數(shù)cpufreq_interactive_timer_start來實現(xiàn)的。
CPUFREQ_GOV_STOP:用來暫停此governor的工作,那么就必須將啟動這個governor設(shè)定的一些信息就必須清除了,比如創(chuàng)建的通知鏈、定時器、sysfsinterface等等。
CPUFREQ_GOV_LIMITS:這里是當用戶修改CPU的最小最大頻率的時候會調(diào)用此處,目的是用戶修改之后,governor要知道這些并將其寫入到相應(yīng)的CPU相關(guān)的結(jié)構(gòu)體中。即對結(jié)構(gòu)體cpufreq_policy中的最大最小頻率進行修改,其實在linux系統(tǒng)中或者android手機中通過console執(zhí)行”echox >/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq“,就可以修改最小頻率。
通過kernel給出的文檔我們可以更加容易的理解這三個event的用意:
CPUFREQ_GOV_START: This governor shall start its duty for the CPU policy->cpu
CPUFREQ_GOV_STOP: This governor shall end its duty for the CPU policy->cpu
CPUFREQ_GOV_LIMITS: The limits for CPU policy->cpu have changed to policy->minand policy->max.
3、既然啟動了interactive governor,那么它是如何工作的呢?
首先啟動code:cpufreq_interactive_timer_start,在初始化的時候,每一個CPU core的timer都是獨立工作的。Code is as follow:
staticvoid cpufreq_interactive_timer_start(int cpu)
{
structcpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu);
unsignedlong expires = jiffies + usecs_to_jiffies(timer_rate);
unsignedlong flags;
?
pcpu->cpu_timer.expires= expires;//CPU core 統(tǒng)計load的定時器
add_timer_on(&pcpu->cpu_timer,cpu);//加入到定時器鏈表中去
if(timer_slack_val >= 0 && pcpu->target_freq >pcpu->policy->min) {
expires+= usecs_to_jiffies(timer_slack_val);
pcpu->cpu_slack_timer.expires= expires;
add_timer_on(&pcpu->cpu_slack_timer,cpu);//not consider
}
?
spin_lock_irqsave(&pcpu->load_lock,flags);
//計算CPU啟動到現(xiàn)在的idle時間
pcpu->time_in_idle=
get_cpu_idle_time(cpu,&pcpu->time_in_idle_timestamp);
pcpu->cputime_speedadj= 0;
//計算啟動啟動到現(xiàn)在的時間
pcpu->cputime_speedadj_timestamp= pcpu->time_in_idle_timestamp;
//上面的兩個參數(shù)是用來計算cpuload使用的,即cpuidle時間是多少,忙的時間是多少。
spin_unlock_irqrestore(&pcpu->load_lock,flags);
}
?
定時器設(shè)置之后,一旦定時器到期的話就會調(diào)用定時器綁定的函數(shù),并執(zhí)行這個函數(shù),這個函數(shù)是:
staticvoid cpufreq_interactive_timer(unsigned long data)。這個函數(shù)是interactivegovernor的關(guān)鍵,只要把這個函數(shù)弄懂了基本上就搞明白了這個governor是怎么工作的了。下面就來分析此函數(shù)。看它是怎樣jisuacpuload的,通過如下代碼實現(xiàn):
?
now= update_load(data);//計算CPU運行到現(xiàn)在的總時間
//現(xiàn)在的總時間減去上次的總時間就是在一個統(tǒng)計周期里CPU運行的時間。
delta_time= (unsigned int)(now – pcpu->cputime_speedadj_timestamp);
/*下面的這個值,我們可以通過update_load這個函數(shù)來看它是怎么計算的。是 active_time乘以當前頻率的value,pcpu->cputime_speedadj+= active_time * pcpu->policy->cur;*/
cputime_speedadj= pcpu->cputime_speedadj;
spin_unlock_irqrestore(&pcpu->load_lock,flags);
?
if(WARN_ON_ONCE(!delta_time))//detect whether delta_time is zero?
gotorearm;
/*活動時間除以總的運行時間在乘以當前頻率,值存儲在cputime_speedadj中*/
do_div(cputime_speedadj,delta_time);
/*上面計算的結(jié)果存儲在cputime_speedadj中,是一個百分比*當前頻率,下面乘以 100的目的就是計算cpu_load的時候是一個大于1的整數(shù)。*/
?
loadadjfreq= (unsigned int)cputime_speedadj * 100;
/*得到cpu_load的值,至于為什么這樣做,是值得思考的。我的理解就是既考慮了時間又考慮了當前頻率,因為在調(diào)整頻率的時候不能只看單純的load信息,即cpu的忙閑的比例。*/
cpu_load= loadadjfreq / pcpu->target_freq;
上面的分析我們已經(jīng)知道了CPUload的值是怎樣計算的。接下來如何使用CPUload的值來調(diào)節(jié)CPU的頻率呢?
看如下的code:
boosted= boost_val || now < boostpulse_endtime;//用在突發(fā)任務(wù)的時候
if(cpu_load >= go_hispeed_load || boosted) {
if(pcpu->target_freq < hispeed_freq) {
new_freq= hispeed_freq;
}else {
new_freq= choose_freq(pcpu, loadadjfreq);
?
if(new_freq < hispeed_freq)
new_freq= hispeed_freq;
}
}else {
new_freq= choose_freq(pcpu, loadadjfreq);
}
go_hispeed_loaddefault value is 99.我們可以很清楚的看到這些語句是如何實現(xiàn)計算new_freq的值的。這些語句比較好理解,如果CPUload>=99的話,那么無論如何都要將CPU的頻率提高到最高頻率。否則按照普通方式進行處理。
?
這個函數(shù)choose_freq是用來計算new_freq的,那么它又是如何實現(xiàn)的呢?簡單的說下就是系統(tǒng)設(shè)置了一個target_load,目的是當前設(shè)置當前的頻率是CPUcore的load值降低到此target_load值之下,如果這個值越小,系統(tǒng)就會越頻繁的升高CPU的頻率使loadvalue<target_load。它的整個代碼就是這個意思,對于target_load的含義我們看kerneldocument有如下解釋:
target_loads:CPU load values used to adjust speed to influence thecurrentCPU load toward that value. In general, the lower the targetload,the more often the governor will raise CPU speeds to bring loadbelowthe target. The format is a single target load, optionallyfollowedby pairs of CPU speeds and CPU loads to target at or abovethosespeeds. Colons can be used between the speeds and associated
targetloads for readability. For example:
851000000:90 1700000:99
targetsCPU load 85% below speed 1GHz, 90% at or above 1GHz, until1.7GHzand above, at which load 99% is targeted. If speeds arespecifiedthese must appear in ascending order. Higher target load
valuesare typically specified for higher speeds, that is, target loadvaluesalso usually appear in an ascending order. The default istargetload 90% for all speeds.好好思考這些東西對理解一些參數(shù)還是很有幫助的。
找到了new_freq的值,還沒有完,code繼續(xù)告訴我們,究竟在什么時候才可以進行調(diào)節(jié)頻率而不影響系統(tǒng)的實時性能,尤其對interactivegovernor是一個升頻快速的governor,能夠及時的響應(yīng)系統(tǒng)的負載信息,及時的調(diào)節(jié)CPU頻率來滿足用戶體驗,當然這是以耗電為代價的。
不說廢話,接著分析,知道我們看到調(diào)節(jié)頻率的code為止:
if(pcpu->target_freq >= hispeed_freq &&
new_freq > pcpu->target_freq &&
now - pcpu->hispeed_validate_time <
freq_to_above_hispeed_delay(pcpu->target_freq)) {
trace_cpufreq_interactive_notyet(data,cpu_load, pcpu->target_freq,pcpu->policy->cur,new_freq);
goto rearm;
}
?
pcpu->hispeed_validate_time= now;
?
if(cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table,new_freq, CPUFREQ_RELATION_L,
&index))
goto rearm;
?
new_freq= pcpu->freq_table[index].frequency;
我們來分析第一個if語句。如果滿足了第一個條件就是當前的目標頻率是最高頻率了,新計算出來的new_freq比最高頻率還高的話,繼續(xù)看now- pcpu->hispeed_validate_time <freq_to_above_hispeed_delay(pcpu->target_freq)這條語句,比較難以費解。我的理解是這樣的:CPU到現(xiàn)在的運行時間減去CPU上次呆在hispeed的時間點上,如果這個value小于設(shè)定的值,即函數(shù)freq_to_above_hispeed_delay(pcpu->target_freq)。即如果系統(tǒng)的頻率處在較高的頻率點上的時間小于這個值的話,那么就沒有必要調(diào)節(jié)頻率了,因為此時的CPU的頻率值就符合要求的。所以直接”gotorearm“了。
如果這條語句不符合,則更新pcpu->hispeed_validate_time,設(shè)置為CPU到現(xiàn)在的運行時間。
接下來分析第二條if語句。在freq_table中查找>=new_freq的頻率點。有的話,直接設(shè)置new_freq為freq_table中的值。這樣我們的new_freq就找出來了,至于是否就是本次CPU的調(diào)節(jié)的頻率接著往下看。
下面這些code還是比較費解的。
/*
* Do not scale below floor_freq unless we have been at or above the
* floor frequency for the minimum sample time since last validated.
*/
if(new_freq < pcpu->floor_freq) {
if(now - pcpu->floor_validate_time < min_sample_time) {
trace_cpufreq_interactive_notyet(
data,cpu_load, pcpu->target_freq,
pcpu->policy->cur,new_freq);
goto rearm;
}
}
我的理解就是當new_freq小于pcpu->floor_freq(基準頻率,暫且這樣說吧)的時候,那么就沒有必要急于頻率的調(diào)整了,如果此時的CPU運行時間減去pcpu->floor_validate_time還小于最小抽樣間隔,那么就真的不需要調(diào)整頻率了,為和選用最小抽樣時間間隔來做比較呢,而這整體現(xiàn)了google工程師的高明之處,在最小抽樣間隔期間內(nèi),CPU的頻率是不會改變的,這就說明了這點。
接著看下面的code:
它的注釋很好的解釋了一切。不再多講。
/*
* Update the timestamp for checking whether speed has been held at
* or above the selected frequency for a minimum of min_sample_time,
* if not boosted to hispeed_freq. If boosted to hispeed_freqthen we
* allow the speed to drop as soon as the boostpulse duration expires
* (or the indefinite boost is turned off).
*/
?
if(!boosted || new_freq > hispeed_freq) {
pcpu->floor_freq= new_freq;
pcpu->floor_validate_time= now;
}
接著下面的code:
if(pcpu->target_freq == new_freq) {
trace_cpufreq_interactive_already(
data,cpu_load, pcpu->target_freq,
pcpu->policy->cur,new_freq);
goto? rearm_if_notmax;
}
如果計算的new_freq與目標頻率是一樣的,跳轉(zhuǎn)到”rearm_if_notmax“處,在那里判斷是否是最高頻率,如果是最高頻率的話,就結(jié)束本次頻率的調(diào)整,并關(guān)閉cputime定時器,CPU頻率會一直呆在最高頻率上(不管其他CPUcore的load是輕還重),等待下次此CPUcore idle的時候重新設(shè)置定時器。idle的進出我們一開始就分析了,使用了idle通知鏈機制:
idle_notifier_register(&cpufreq_interactive_idle_nb);//注冊
//通知鏈處理函數(shù)
staticint cpufreq_interactive_idle_notifier(struct notifier_block *nb,
unsigned long val,
void *data)
{
switch(val) {
case IDLE_START:
cpufreq_interactive_idle_start();
break;
case IDLE_END:
cpufreq_interactive_idle_end();
break;
}
?
return0;
}
?
staticstruct notifier_block cpufreq_interactive_idle_nb = {
.notifier_call= cpufreq_interactive_idle_notifier,//對應(yīng)的通知鏈處理函數(shù)
};
接下來,一切都比較順利的情況,interactivegovernor是怎樣實現(xiàn)頻率調(diào)節(jié)的:
相關(guān)代碼如下:
pcpu->target_freq= new_freq;//設(shè)置新的目標頻率
spin_lock_irqsave(&speedchange_cpumask_lock,flags);
/*設(shè)置需要調(diào)節(jié)頻率的CPUcore的cpumask*/
cpumask_set_cpu(data,&speedchange_cpumask);
spin_unlock_irqrestore(&speedchange_cpumask_lock,flags);
/*喚醒內(nèi)核線程來執(zhí)行頻率的改變*/
wake_up_process(speedchange_task);
?
rearm_if_notmax:
/*
* Already set max speed and don't see a need to change that,
* wait until next idle to re-evaluate, don't need timer.非常重要
*/
if(pcpu->target_freq == pcpu->policy->max) {
goto exit;
}
rearm:
/*
*判斷cpu_timer是否為空,為空的話就重新設(shè)置相關(guān)的timer并
*將其加入到定時器鏈表中。
*/
if(!timer_pending(&pcpu->cpu_timer))
cpufreq_interactive_timer_resched(pcpu);
?
exit:
/*放棄使能信號量*/
up_read(&pcpu->enable_sem);
return;
到此為止,cpufreq_interactive_timer函數(shù)就這樣結(jié)束了,比較復雜,這些搞懂了,其他之類的函數(shù)就是為它服務(wù)的。
?
下面進入調(diào)節(jié)頻率的函數(shù)了,也就是創(chuàng)建的內(nèi)核線程綁定的函數(shù)cpufreq_interactive_speedchange_task,分析如下:
staticint cpufreq_interactive_speedchange_task(void *data)
{
unsignedint cpu;
cpumask_ttmp_mask;
unsignedlong flags;
structcpufreq_interactive_cpuinfo *pcpu;
?
/*這個循環(huán)不斷的在執(zhí)行*/
while(1) {
set_current_state(TASK_INTERRUPTIBLE);
spin_lock_irqsave(&speedchange_cpumask_lock,flags);
?
if(cpumask_empty(&speedchange_cpumask)) {
spin_unlock_irqrestore(&speedchange_cpumask_lock,flags);
schedule();//如果沒有哪個CPUcore的頻率需要調(diào)整,就去執(zhí)行其他事情
?
if(kthread_should_stop())
break;
?
spin_lock_irqsave(&speedchange_cpumask_lock,flags);
}
/*將線程設(shè)置為可運行狀態(tài)*/
set_current_state(TASK_RUNNING);
tmp_mask= speedchange_cpumask;//臨時保存
/*記得每次都要清除,因為這個值可能時刻在改變著*/
cpumask_clear(&speedchange_cpumask);
spin_unlock_irqrestore(&speedchange_cpumask_lock,flags);
/*這里開始真正的頻率調(diào)節(jié)了*/
for_each_cpu(cpu,&tmp_mask) {
unsignedint j;
unsignedint max_freq = 0;
?
pcpu= &per_cpu(cpuinfo, cpu);
if(!down_read_trylock(&pcpu->enable_sem))
continue;
if(!pcpu->governor_enabled) {
up_read(&pcpu->enable_sem);
continue;
}
?
for_each_cpu(j,pcpu->policy->cpus) {
structcpufreq_interactive_cpuinfo *pjcpu =&per_cpu(cpuinfo,j);
/*找出所有CPUcore的最大頻率*/
if(pjcpu->target_freq > max_freq)
max_freq= pjcpu->target_freq;
}
/*調(diào)節(jié)頻率,執(zhí)行的函數(shù)是一個回調(diào)函數(shù)(callbackfunction),應(yīng)該是DVFSdriver執(zhí)行具體動作的*/
if(max_freq != pcpu->policy->cur)
__cpufreq_driver_target(pcpu->policy,max_freq,CPUFREQ_RELATION_H);
trace_cpufreq_interactive_setspeed(cpu,pcpu->target_freq,pcpu->policy->cur);
?
up_read(&pcpu->enable_sem);
}
}
?
return 0;
}
到此調(diào)節(jié)頻率完成了。就這樣周而復始的執(zhí)行上述code。
我們必須知道一種情況就是,如果某個CPUcore不處在idlestatus,并且此時的CPUcore的frequency==max_freq的話,統(tǒng)計CPUcore load的函數(shù)(staticvoid cpufreq_interactive_timer(unsigned long data))不會執(zhí)行,直到下次idle的到來。
如果對于有些參數(shù)不是很理解的話,可以查看kernel目錄下:document/cpufreq/governor.txt,里面對多種governor的參數(shù)進行了詳細的說明。
?
還有一些細節(jié)自己還是沒有弄明白,希望有疑問的朋友,或者說的有錯誤的地方,望各位看官不吝賜教,謝謝!
總結(jié)
以上是生活随笔為你收集整理的interactive governor study for android的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: yarn install报网络问题
- 下一篇: 【游戏开发引擎】 实验3:Captain