转自:https://blog.youkuaiyun.com/chongyang198999/article/details/49451587
一.choose_freq函数概述
在interative策略中,choose_freq函数用来选择一个合适的频率,使选频后系统的workload小于或等于target load. 其核心思想是:选取一个最小的频率,使在这个频率中的target load达到理想状态。
1. 影响选频的核心因素有两个:
1.两次统计时间内系统频率的平均频率loadadjfreq,
2.系统设定好的target load,在interative初始化的时候设定,tunables->target_loads = default_target_loads;
2. 先大概描述下两个核心因素的情况:
1)根据cpufreq_interactive_timer中对cpu当前负载的计算我们知道:
Cur_load = loadadjfreq / cur_freq
而在choose_freq函数中,计算target freq时:
target_freq = loadadjfreq / t1
其中t1为系统设定好的target load,默认为90。
因此,综合上述两个式子,可以得到:
Target_freq = cur_freq * (cur_load / target_load) ;
也就是说,如果当前cpu负载大于90%,期望从频率表选择一个大于当前的频率,从而减少负载,如果负载小于90%,则期望从频率表选择一个频率以提高当前负载。
2)在interative初始化时,会为每个cpu频率设定一个理想的target load,以负载+频率的键值对方式,保存在cpufreq_interactive_tunables结构中的target_loads[]数组,ntarget_loads记录一共有多少个键值对。比如,我们可以以下面的方式,指定对应cpu频率下,理想的cpu负载。
Target_loads[] = {90, 800, 85 ,900, 95, 1000};
意思是,如果目标频率在区间[800, 900)之间,则理想cpu负载应在90%左右,如果当前目标频率在区间[900, 100),理想负载在85%左右,从访问方式可以看出,数组中频率最好以递增方式安排。
freq_to_targetload中获取target load:
for (i = 0; i < tunables->ntarget_loads - 1 &&
freq >= tunables->target_loads[i+1]; i += 2)
;
ret = tunables->target_loads[i];
在interative中只设定为90,因此对于所有频段,target的cpu负载都是90%。
二.choose_freq程序流程
三.结合具体样例分析频率的选择
例1
假如当前频率为800Mz,target_load默认为90,且有如下频率表:
{800Mz, 900Mz, 1000Mz, 1100Mz}
当前负载为91,根据Target_freq = cur_freq * (cur_load / target_load)
Target_freq = 800Mz * (91 / 90) = 808Mz.
根据cpufreq_frequency_table_target()传入参数,从频率表取出比target_freq大的最小频率,也就是900M。此时,freq > prevfreq,则fremin=prefreq,且freq不大于freqmax,因此会重新进入while循环。因为target_load默认值固定为90,因此第二次循环,从频率表取出来的频率不变,freq = prevfreq,循环结束,返回查找出来的900Mz。
例2
假如当前频率为1100Mz,target_load默认为90,有如下频率表:
{800Mz, 900Mz, 1000Mz, 1100Mz}
当前负载为82,根据Target_freq = cur_freq * (cur_load / target_load)
Target_freq = 1100Mz * (82 / 90) = 1002Mz.
从频率表选择比target_freq(1002Mz)大的最小频率,也就是1100Mz。同理,最后返回1100Mz。
由此可见,如果当前负载超过90,且频率不是最大值,很容易升频,而当负载为81时,才可能降频(仅限于前面两个例子),可能为了性能考量。
Freqmin和freqmax等变量,是为了多target load准备的,本程序中target load固定90的,但我们也可以考虑下,以体会此处算法的精巧。
例3
如果当前频率800Mhz,intravite初始化时,系统设定的负载与频率表的对应关系如下:
Target_loads[] = {90, 800, 85 ,900, 95, 1000, 88, 1100};
当前负载为98,根据Target_freq1 = cur_freq * (cur_load / target_load)
Target_freq1 = 800Mhz * (95 / 90) = 844Mhz
从频率表选出900Mhz作为当前freq,因为prevfreq < freq < freqmax, 此时freqmin = prevfreq = 800Mhz,然后重新进入while循环
此时prevfreq = freq = 900Mhz, 计算
target_freq2 = 800Mhz * ( 95 / 85) = 894Mhz
因此从频率表选出频率为freq = 900Mhz。因为900与prevfreq相等。因此返回900Mhz。算法这样设计,主要是防止频率跨度太大,或频率下降太快。由于需要调整各种target_load和频率表,比较麻烦,如果感兴趣可以自己更改相关参数。
/*
* If increasing frequencies never map to a lower target load then
* choose_freq() will find the minimum frequency that does not exceed its
* target load given the current load.
*/
static unsigned int choose_freq(struct cpufreq_interactive_cpuinfo *pcpu, unsigned int loadadjfreq)
{
//freq的初始数值是pcpu->policy->cur
unsigned int freq = pcpu->policy->cur;
//prevfreq保存着上次的freq,freq表示本次选频的结果,当二者相等时,就表示已经达到了最佳的选频结果.
unsigned int prevfreq, freqmin, freqmax;
unsigned int tl;//其中t1为系统设定好的target load,默认为90
int index;
freqmin = 0;
freqmax = UINT_MAX;
do {
prevfreq = freq;
//通过freq_to_targetload得到target load——tl
tl = freq_to_targetload(pcpu->policy->governor_data, freq);
/*
* Find the lowest frequency where the computed load is less
* than or equal to the target load.
*/
//CPUFREQ_RELATION_L,表示要取大于等于target的最小值
if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table, loadadjfreq / tl,CPUFREQ_RELATION_L, &index))
break;
freq = pcpu->freq_table[index].frequency;
if (freq > prevfreq) {// cur_load小于target_load
/* The previous frequency is too low. */
freqmin = prevfreq;
if (freq >= freqmax) {
/*
* Find the highest frequency that is less
* than freqmax.
*/
//CPUFREQ_RELATION_H,表示要取小于等于target的最大值
if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table,freqmax - 1, CPUFREQ_RELATION_H, &index))
break;
freq = pcpu->freq_table[index].frequency;
if (freq == freqmin) {
/*
* The first frequency below freqmax
* has already been found to be too
* low. freqmax is the lowest speed
* we found that is fast enough.
*/
freq = freqmax;
break;
}
}
} else if (freq < prevfreq) {// cur_load大于target_load
/* The previous frequency is high enough. */
freqmax = prevfreq;
if (freq <= freqmin) {
/*
* Find the lowest frequency that is higher
* than freqmin.
*/
//CPUFREQ_RELATION_L,表示要取大于等于target的最小值
if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table,freqmin + 1, CPUFREQ_RELATION_L,&index))
break;
freq = pcpu->freq_table[index].frequency;
/*
* If freqmax is the first frequency above
* freqmin then we have already found that
* this speed is fast enough.
*/
if (freq == freqmax)
break;
}
}
/* If same frequency chosen as previous then done. */
} while (freq != prevfreq);
return freq;
}
// 系统设置的load,一般默认值是90
static unsigned int freq_to_targetload(
struct cpufreq_interactive_tunables *tunables, unsigned int freq)
{
int i;
unsigned int ret;
unsigned long flags;
spin_lock_irqsave(&tunables->target_loads_lock, flags);
for (i = 0; i < tunables->ntarget_loads - 1 &&freq >= tunables->target_loads[i+1]; i += 2)
;
ret = tunables->target_loads[i];
spin_unlock_irqrestore(&tunables->target_loads_lock, flags);
return ret;
}