【Linux kernel/cpufreq】framework ----cpufreq governor

CPU调频器
框架层:提供框架支持以及调频策略;
驱动层:提供驱动能力支持;
CPU调频器:Governor其实我们平时所说的DCVS,当然DVFS包含了硬件的调频能力与软件的算法。此处集中在软件算法部分。核心层提供了API cpufreq_register_governor来进行注册。通过查看scaling_available_governor可以看到所有已经注册的调频器,通过设置scaling_governor可以进行各种governor之间的切换。
【Linux kernel/cpufreq】framework ----cpufreq governor_第1张图片
图示:DCVS与governor

框架层通过cpufreq_policy设置了上层约束,governor在这个约束的范围内通过采集CPU的使用情况来决定是否需要调频,调到多少的频率合适。
【Linux kernel/cpufreq】framework ----cpufreq governor_第2张图片
图示:governor的输入与输出

【Linux kernel/cpufreq】framework ----cpufreq governor_第3张图片
图示:CPU使用率
下面介绍几种常用的governor:
【Linux kernel/cpufreq】framework ----cpufreq governor_第4张图片
interactive
基于linux模块化的设计,早期的governor基本都是对CPU的使用率进行定时采样检查,然后根据使用率的情况调整CPU频率。Interactive governor作为早期android的调频器,也属于此类。它除了周期性采样CPU的使用率之外,还加入了一些优化项,来满足交互式终端的性能体验需求。
【Linux kernel/cpufreq】framework ----cpufreq governor_第5张图片
图示:interactive governor工作原理(tips:不要把任务都放在同一个进程里面,尽量分散到多个线程上)

Interactive governor针对android这类交互终端做了一些改进如下:

1) 引入hi_speed的概念,当CPU使用率超过一定阈值(go_hispeed_load)的时候,会直接跳到预先设定的hi_speed的CPU频率,来满足交互的性能。避免governor周期采样调频的时间延迟。

2) 引入target_loads的概念,一般governor都默认采用80%作为调频的阈值,即CPU使用率超过80%,向上提高频率;低于80%,向下降低频率。但是具体的CPU的个体上存在差异,比如高频率部分的能效比较差,低频段的能效比较好。可以通过target_loads来设定不同频率区间的调频阈值。

3) 引入above_hispeed_delay,在hispeed freq以上的部分,CPU频率需要在当前频率上驻留一定的时间才可以继续向上调频,用于功耗优化。

4) boost/boostpulse_duration: boost模式,上层应用程序按需触发boost模式。

5) 关于输入参数cpu 使用率,最初的interactive governor参数为CPU运行时间跟处于IDLE的时间。也就是说usage使用率处于0%~100%之间。设想一个大任务或类while(1)循环的场景,其负载始终处于100%。以target load处于80%为例,每次只能向上增加25%的频率。以这样的速度调频的话,什么时候才能调到最高频率呢?所以后来将interactive的CPU使用率参数从cputime切换成scheduler调度器统计的CPU使用负载(高通在walt的时候引入)。
schedutil
之前我们介绍过:CPU调速器schedutil原理分析

上面讲interactive的部分分析了其优缺点,虽然可以采用scheduler统计的负载来代替cputime的使用率,但是interactive仍然存在一个问题,即采样周期。由于interactive是周期性采样调频的,其结果必然受到了采样周期的影响。采样周期短的话,其响应更加及时,但是系统开销大;采样周期长的话,响应比较缓慢,但是系统开销比较小。随着屏幕的刷新率从60HZ提到了90HZ, 120HZ。每一帧绘制的时间越来越短,对于CPU频率的响应延迟要求越来越高。

基于上述的一些原因,引入了schedutil调频器。虽然其名字叫schedutil,即来自于scheduler的utilization,但是这一点在schedutil出现之前高通已经在interactive上引入了。个人认为schedutil最大的改动在于将周期性的采样触发改成了基于事件驱动触发(反模块化)。这些事件来自于scheduler。其工作原理大体如下:

1) 当一个任务被唤醒时,任务被放入到了某个CPU的队列中。此时意味着当前CPU的任务负载变重,需要及时的提高频率来保证性能。于是主动触发了调频。

2) 当一个任务完成工作,进入休眠状态,从任务队列中被移除。此时意味着当前CPU的任务负载变轻,需要及时的降低频率来降低功耗。于是主动触发了调频。

上面是2个典型的场景,诸如此类。当CPU上任务负载发生变化的时候,即由scheduler触发频率的调整。

cpufreq_schedutil.c中注册了hook函数
【Linux kernel/cpufreq】framework ----cpufreq governor_第6张图片
scheduler在某些事件发生的时候主动调用cpufreq_update_util来进行频率的调整。其实已经在调度器跟调频器之间搭了座桥梁。

cpufreq governor的实现
struct cpufreq_governor
kernel通过struct cpufreq_governor抽象cpufreq governor,如下:

 /* include/linux/cpufreq.h */
 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;
 };

name,该governor的名称,唯一标识某个governor。

initialized,记录governor是否已经初始化okay。

max_transition_latency,容许的最大频率切换延迟,硬件频率的切换必须小于这个值,才能满足需求。

governor_list,用于将该governor挂到一个全局的governor链表(cpufreq_governor_list)上。

show_setspeed和store_setspeed,有些governor支持从用户空间修改频率值,此时该governor必须提供show_setspeed和store_setspeed两个回调函数,用于响应用户空间的scaling_setspeed请求。

governor,cpufreq governor的主要功能都是通过该回调函数实现,该函数借助不同的event,以状态机的形式,实现governor的启动、停止等操作。

governor event
kernel将governor的控制方式抽象为下面的5个event,cpufreq core在合适的时机,以event的形式(.governor回调),控制governor完成相应的调频动作。

/* include/linux/cpufreq.h */

/* Governor Events */
#define CPUFREQ_GOV_START       1
#define CPUFREQ_GOV_STOP        2
#define CPUFREQ_GOV_LIMITS      3
#define CPUFREQ_GOV_POLICY_INIT 4
#define CPUFREQ_GOV_POLICY_EXIT 5

CPUFREQ_GOV_POLICY_INIT,policy启动新的governor之前(通常在cpufreq policy刚创建或者governor改变时)发送。governor接收到这个event之后,会进行前期的准备工作,例如初始化一些必要的数据结构(如timer)等。并不是所有governor都需要这个event,后面如果有时间,我们以ondemand governor为例,再介绍它的意义。

CPUFREQ_GOV_START启动governor。

CPUFREQ_GOV_STOP、CPUFREQ_GOV_POLICY_EXIT,和前面两个event的意义相反。

CPUFREQ_GOV_LIMITS,通常在governor启动后发送,要求governor检查并修改频率值,使其在policy规定的有效范围内。

governor register
所有governor都是通过cpufreq_register_governor注册到kernel中的,该接口比较简单,查找是否有相同名称的governor已经注册,如果没有,将这个governor挂到全局的链表即可,如下:

int cpufreq_register_governor(struct cpufreq_governor *governor)
{
        int err;

        if (!governor)
                return -EINVAL;

        if (cpufreq_disabled())
                return -ENODEV;

        mutex_lock(&cpufreq_governor_mutex);

        governor->initialized = 0;
        err = -EBUSY;
        if (__find_governor(governor->name) == NULL) {
                err = 0;
                list_add(&governor->governor_list, &cpufreq_governor_list);
        }

        mutex_unlock(&cpufreq_governor_mutex);
        return err;
}
EXPORT_SYMBOL_GPL(cpufreq_register_governor);

学习wiki:
linux cpufreq framework(4)_cpufreq governor
深入浅出CPUFreq

你可能感兴趣的:(Linux,kernel基础,linux)