【cpufreq】linux cpufreq governor实现分析(3)

performance/powersave策略

这两个都是设置静态的频率,performance设置最高频,powersave设置最低频。切换governor的时候配置好频率:

store_scaling_governor->cpufreq_set_policy->cpufreq_governor_limits

userspace策略

用户写文件节点/sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed时,调用store_scaling_setspeed函数修改频率。

ondemand、conservative策略

这两个governor计算负载的方法以及调频的流程相同,不同的是利用负载计算预期频率的策略,因此4.14及更新的kernel把这两个governor相同的部分做了一些提炼。

Interactive策略

重要概念

hispeed_freq:当CPU频率为最低频且负载突然超过go_hispeed_load时,CPU跳到此频率
go_hispeed_load:hispeed_freq对应的负载
min_sample_time:在降频前需要在当前频率运行保持的时间
sampling_rate:interative管理器的采样间隔
target_loads:为每个频率设置不同的负载门限,以负载+频率的数组形式存储:如75:800:80:900:85:1300:90:1500:95,详细见如下分析。
above_hispeed_delay:频率升高时需要保持的时间,以频率+时间的数组形式存储

调频基本流程

设置sched的回调函数,每次发生任务调度时设置一个irq_work任务,在irq_work中重新计算目标频率

gov_set_update_util->cpufreq_add_update_util_hook->cpufreq_update_util->update_util_handler->irq_work_queue
->eval_target_freq
  -> update_load /* 计算CPU移动平均负载频率loadadjfreq = cur_load * cur_freq,代表CPU实际需要的频率 */
  -> choose_freq /* 根据target_load和loadadjfreq,计算target_freq */

choose_freq函数根据target_load数组使用如下公式来计算目标频率,通过多次迭代把负载控制在合理的范围内。

       target_freq= loadadjfreq / tl = cur_freq * cur_load / tl;

static unsigned int choose_freq(struct interactive_cpu *icpu, unsigned int loadadjfreq)
{
struct cpufreq_policy *policy = icpu->ipolicy->policy;
struct cpufreq_frequency_table *freq_table = policy->freq_table;
unsigned int prevfreq, freqmin = 0, freqmax = UINT_MAX, tl;
unsigned int freq = policy->cur;
int index;

do {
prevfreq = freq;
tl = freq_to_targetload(icpu->ipolicy->tunables, freq); /* 根据目标freq返回目标负载 */
/*
* Find the lowest frequency where the computed load is less
* than or equal to the target load.
* target_freq= loadadjfreq / tl = cur_freq * cur_load / tl; /* 根据这个公式逐渐收缩,多次调整找到最佳tl和目标freq */
*/
index = cpufreq_frequency_table_target(policy, loadadjfreq / tl, CPUFREQ_RELATION_L);
freq = freq_table[index].frequency;
if (freq > prevfreq) {
/* The previous frequency is too low */
      ...
} else if (freq < prevfreq) {
/* The previous frequency is high enough. */
      ...
}
/* If same frequency chosen as previous then done. */
} while (freq != prevfreq);

return freq;
}

下面通过一个例子来描述上面函数的计算过程

假设CPU支持的频率为[200:800]内每隔50MHz的频率。

1)target_loads = 75,那么所有频率的的门限均为75。
升频情况

    如果当前频率为350MHz,负载为85超过了75,那么根据公式
    target_freq= 350 * 85 / 75 = 396,则在频率表中匹配的档位为400。
降频情况:

    如果当前频率为350MHz,负载为65低于了75,那么根据公式
    target_freq= 350 * 65 / 75 = 303,则在频率表中匹配的档位为350。

2)target_loads = 70:400:80:600:90,为不同频率分别设置不同的门限,好处是较低频率可以有较快的反应。(0:400)内的频率负载门限为70,[400:600)内的频率负载门限为80;[600:)内的频率负载门限为90。
升频情况

    如果当前频率为350MHz,负载为85超过了70,那么根据公式
    target_freq= 350 * 85 / 70 = 425,则在频率表中匹配的档位为450。450的门限为80,再次计算
    target_freq= 350 * 85 / 80 = 371,则在频率表中匹配的档位为400。 400的门限为80,所以最终计算的频率为400。

降频情况

    如果当前频率为450MHz,负载为50低于了80,那么根据公式
    target_freq= 450 * 50 / 80 = 281,则在频率表中匹配的档位为300,300的门限为70,再次计算
    target_freq= 450 * 50 / 70 = 321,则在频率表中匹配的档位为350,350的门限为70,所以最终计算的频率为350。

Schedutil策略

基本思想

初始化时调用cpufreq_add_update_util_hook注册调频回调函数。

sugov_start

    ->cpufreq_add_update_util_hook

cpufreq_update_util会由kernel调度器调用,每当任务调度时执行回调函数计算负载和预期频率。

cpufreq_update_util

posted @ 2023-02-05 23:23  zephyr~  阅读(598)  评论(0编辑  收藏  举报