linux cpu管理(五) cpu idle
转:https://zhuanlan.zhihu.com/p/539722367
本文基于以下软硬件假定:
架构:AARCH64
内核版本:5.14.0-rc51
atf版本:Arm trust firmware V2.5
1 cpu idle目标
cpu是计算机系统的核心,其主要功能是执行程序。它就像流水线的工人一样,按照固定节拍,重复着取指、译码及指令执行操作。而当cpu上没有可执行任务时,就会进入空闲状态,那么该状态下cpu应该如何运行呢?
写过单片机程序的同学一定对下面这段代码很熟悉:
while (1) {
if (condition)
break;
}
该程序判断某个条件是否成立,若不成立则持续执行死循环操作,这也是最简单的idle处理方式。但是cpu的运行是需要消耗能量的,对于像智能手机之类的移动设备,cpu空转就会直接影响到其续航能力。
为此,cpu需要提供更加丰富的低功耗管理模式,如arm64架构提供了一条wfi指令。cpu一旦执行该指令就会进入低功耗状态,该状态会关闭cpu时钟,从而可降低其动态功耗。当cpu接收到中断之后,就又会被唤醒而重新投入运行。
随着人们对设备功耗要求的不断提高,也对cpu idle状态的管理提出了更高的要求。如其在idle时是否可以进入更深的睡眠状态,对于包含cluster的拓扑,在一个cluster中所有cpu都进入idle状态时,是否应该将cluster本身也设置为idle状态等。
由于不同cpu架构睡眠状态的实现方式不同,因此内核需要为其提供相应的驱动程序。而由于功耗管理最终需要操作具体cpu相关的硬件,故内核又为每个cpu抽象出一个对应的cpuidle设备。
更进一步,对于cpu来说,其睡眠状态越深,则相应的功耗就越低。但是任何事物都是有代价的,更深的睡眠状态就意味更高的唤醒延迟。因此,内核需要评估在不同场景下,cpu应该要进入哪种睡眠状态。
显然这不是一件容易的事情,为此内核为其提供了一套管理策略相关的框架cpu idle governor。不同的管理策略可以实现为不同governor,这些governor会有不同的评分(rating)。cpuidle驱动可以指定使用某个特定的governor,但若未指定,则在驱动注册时比较系统中所有已注册governor的rating值,为其选择一个分值最高的governor。
2 cpu idle数据结构
2.1 cpu idle设备
由于cpu idle的管理对象为cpu,且每个cpu都可以独立控制自己的idle状态。为了方便对这些实例的管理,内核为每个cpu抽象出了一个虚拟的cpuidle设备。该设备的主要功能是提供idle管理相关的信息和统计数据,其定义如下:
struct cpuidle_device {
unsigned int registered:1; (1)
unsigned int enabled:1; (2)
unsigned int poll_time_limit:1; (3)
unsigned int cpu;
ktime_t next_hrtimer; (4)
int last_state_idx; (5)
u64 last_residency_ns; (6)
u64 poll_limit_ns; (7)
u64 forced_idle_latency_limit_ns;
struct cpuidle_state_usage states_usage[CPUIDLE_STATE_MAX]; (8)
struct cpuidle_state_kobj *kobjs[CPUIDLE_STATE_MAX]; (9)
struct cpuidle_driver_kobj *kobj_driver;
struct cpuidle_device_kobj *kobj_dev;
struct list_head device_list;
#ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED
cpumask_t coupled_cpus;
struct cpuidle_coupled *coupled; (10)
#endif
};
(1)该cpu idle设备是否已经注册
(2)该cpuidle设备是否已使能
(3)cpu的tick中断用于驱动调度器运转,正常情况该中断会以HZ为频率周期性地触发。当cpu进入idle睡眠状态时,实际上调度器中已经没有可运行任务,因此该中断在此时并没有什么作用。
更糟糕的是对于wfi之类的低功耗状态,还会被该定时器周期性地唤醒。使得idle cpu不停地处于唤醒->睡眠->唤醒->睡眠的循环中,从而显著增加cpu功耗。为了解决该问题内核引入了tick时钟的nohz模式,即在cpu进入idle时可关闭其对应的tick时钟。
但是有时idle时间很短,如可能低于一个tick周期,此时就没必要进入睡眠状态和关闭tick时钟,故cpu idle模块只需采用简单轮询方式即可。还有像haltpoll之类的governor,本身就是通过轮询方式实现idle流程的。这些情形都需要使用该参数表示轮询时间是否已到期
(4)由于cpu的预期空闲时间,是governor选择睡眠状态的重要参考依据,预期空闲时间越长,可以采用的睡眠状态就越深。一般来说定时器到期时,表明cpu上有事件要处理,其也可能需要退出空闲状态执行相应的任务。因此governor在计算预期空闲时间时,会参考下一次到期定时器的时间
(5)上一次进入idle时对应睡眠状态的index
(6)该设备上一次停留在idle状态的时间,单位为ns
(7)当采用轮询方式时,该值用于指定最大的轮询时间
(8)该参数用于保存每个idle状态的一些统计数据
(9)用于sysfs相关的kobj
(10)有些平台的cpu不能独立执行上下电等操作,从而使相关cpu的idle状态相互依赖。因此在执行cpu idle操作时,需要考虑这种依赖关系。cpu idle coupled特性即用于解决该问题,其中coupled_cpus就是表示与本cpu有依赖关系的cpu集合。由于这种特性与cpu idle的主功能没有关系,因此本文将不对该特性做详细介绍
2.2 cpu idle驱动
cpu idle驱动用于管理实际的cpu idle状态,它主要包含一个该驱动支持的cpu idle状态数组,以及一些其它配置参数。以下为其定义:
struct cpuidle_driver {
const char *name;
struct module *owner;
unsigned int bctimer:1; (1)
struct cpuidle_state states[CPUIDLE_STATE_MAX]; (2)
int state_count; (3)
int safe_state_index; (4)
struct cpumask *cpumask; (5)
const char *governor; (6)
};
(1)当cpu idle状态设置了CPUIDLE_FLAG_TIMER_STOP标志,就会在进入idle时停掉其local timer。从而导致其自身无法唤醒该cpu上的定时器,因此内核实现了broadcast timer机制。
它会选择一个未睡眠cpu的timer作为broadcast timer,当其它cpu进入idle且关闭local timer之后,其对应的下一次到期定时器将会由broadcast timer代理执行。当定时器到期后,broadcast定时器会通过ipi方式将该事件通知到idle cpu,从而唤醒该cpu。
本参数就是cpu idle框架使用,用于指示在cpu idle驱动注册和注销时,是否需要设置一个broadcast timer
(2)cpu idle状态数组,用于保存该驱动支持的所有cpu idle状态相关信息,这些状态按照功耗从大道小的顺序进行排序
(3)该驱动支持的cpu idle状态数量
(4)该参数与coupled状态相关
(5)该驱动支持的cpu集合掩码
(6)用于指定该驱动希望使用的cpu idle governor。若不指定该值,则通过系统中已注册governor的rating,选择其中一个分值最高的来使用
2.3 cpu idle状态
由前面讨论可知,cpu idle的目标是在cpu进入空闲状态时,根据不同睡眠状态下的功耗和延迟参数,确定cpu应该要进入哪一种睡眠模式。
为此内核为每一种睡眠模式定义了一个状态结构体,用于表示其在对应状态下的相关参数,以下为其定义:
struct cpuidle_state {
char name[CPUIDLE_NAME_LEN];
char desc[CPUIDLE_DESC_LEN];
s64 exit_latency_ns; (1)
s64 target_residency_ns; (2)
unsigned int flags; (3)
unsigned int exit_latency;
int power_usage; (4)
unsigned int target_residency;
int (*enter) (struct cpuidle_device *dev,
struct cpuidle_driver *drv,
int index); (5)
int (*enter_dead) (struct cpuidle_device *dev, int index);
int (*enter_s2idle)(struct cpuidle_device *dev,
struct cpuidle_driver *drv,
int index);
};
(1)exit_latency_ns:表示若使用这种idle状态,则cpu被唤醒退出idle时需要消耗的时间。该值以ns为单位,相应地exit_latency与其含义相同,但以ms为单位。这个值用于衡量系统对退出延迟的容忍度
(2)target_residency_ns:由于每种idle状态的切换需要消耗能量,睡眠状态越深则其切换所消耗的能量也越多。因此,若睡眠时间较短,却使用较深的状态是得不偿失的。为此只有当目标睡眠时间超过一个特定值之后,才会使用这种睡眠状态,这个参数即用于表示该值。而governor的一个关键功能就是估计idle可能的睡眠时间
(3)idle相关的标志
(4)该idle状态下的cpu功耗
(5)cpu进入idle状态的回调函数
2.4 cpu idle governor
cpu idle governor用于实现cpu idle睡眠状态的选择策略,以下为其结构体定义:
struct cpuidle_governor {
char name[CPUIDLE_NAME_LEN]; (1)
struct list_head governor_list; (2)
unsigned int rating; (3)
int (*enable) (struct cpuidle_driver *drv,
struct cpuidle_device *dev); (4)
void (*disable)(struct cpuidle_driver *drv,
struct cpuidle_device *dev); (5)
int (*select) (struct cpuidle_driver *drv,
struct cpuidle_device *dev,
bool *stop_tick); (6)
void (*reflect) (struct cpuidle_device *dev, int index); (7)
}
(1)governor的名字
(2)内核将所有已注册的governor加入到一个链表中,该参数为其链表节点
(3)该governor的评分
(4)使能cpu idle设备的governor
(5)通知cpu idle设备的governor
(6)该函数是governor的主函数,用于选择idle的state
(7)该函数用于更新统计数据
2.5 cpu idle软件架构
cpuidle软件框架与内核其它模块类似,也包括核心层,设备层,通用驱动层与架构相关的驱动层,此外它还包括用于选择cpuidle模式的governor模块。最后cpu的idle状态是由内核调度系统触发的,因此最终的服务对象为调度子系统。综上所述,其相应的软件架构图如下:

3 cpu idle初始化流程
cpu idle初始化包括governor注册、驱动注册和设备注册三部分,其中驱动和设备注册流程由架构相关的初始化流程执行。由于arm64的通用cpu idle代码框架采用psci方式(driver/cpuidle/cpuidle-psic.c)实现。因此,在后面介绍cpu idle设备和驱动注册流程时将以这种方式为例
3.1 governor注册
cpu idle governor在cpu idle驱动和设备之前注册,内核使用一个链表维护系统中所有已注册的governor。且在每个新governor被注册时都会将其rating值与已注册governor的rating值比较,并将rating值最高的governor最为当前governor。其注册流程如下:

当前内核一共支持ladder、menu、teo和haltpoll四种governor,它们都通过调用cpuidle_register_governor函数将自身注册到系统中。该函数的定义如下:
int cpuidle_register_governor(struct cpuidle_governor *gov)
{
…
if (cpuidle_find_governor(gov->name) == NULL) {
ret = 0;
list_add_tail(&gov->governor_list, &cpuidle_governors); (1)
if (!cpuidle_curr_governor ||
!strncasecmp(param_governor, gov->name, CPUIDLE_NAME_LEN) ||
(cpuidle_curr_governor->rating < gov->rating &&
strncasecmp(param_governor, cpuidle_curr_governor->name,
CPUIDLE_NAME_LEN)))
cpuidle_switch_governor(gov); (2)
}
…
}
(1)将该governor加入全局链表cpuidle_governors中
(2)将该governor的rating值与current governor的rating值比较,若其分值更高则将current governor切换为新governor
3.2 psci方式的idle框架初始化
arm64架构的通用cpu idle初始化流程主要包括解析设备树的idle配置参数,注册cpu idle驱动和设备等,其调用流程如下:

3.2.1 设备树解析
设备树可以通过以下两种方式描述cpu idle的states:
(1)通过层次化的power-domains和domain-idle-states属性描述
(2)通过cpu-idle-states属性描述
下面为一个通过cpu-idle-states属性描述的例子:
cpu0: cpu@0 {
compatible = "arm,cortex-a53";
device_type = "cpu";
reg = <0x0 0x0>;
enable-method = "psci";
next-level-cache = <&A53_L2>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
…
};
...
cpu4: cpu@100 {
compatible = "arm,cortex-a73";
device_type = "cpu";
reg = <0x0 0x100>;
enable-method = "psci";
next-level-cache = <&A73_L2>;
cpu-idle-states = <&CPU_SLEEP_1 &CLUSTER_SLEEP_1>;
…
}
…
idle-states {
entry-method = "psci";
CPU_SLEEP_0: cpu-sleep-0 {
compatible = "arm,idle-state";
local-timer-stop; (1)
arm,psci-suspend-param = <0x0010000>; (2)
entry-latency-us = <400>; (3)
exit-latency-us = <650>; (4)
min-residency-us = <1500>; (5)
};
CLUSTER_SLEEP_0: cluster-sleep-0 {
compatible = "arm,idle-state";
local-timer-stop;
arm,psci-suspend-param = <0x1010000>;
entry-latency-us = <500>;
exit-latency-us = <1600>;
min-residency-us = <3500>;
};
…
}
其中idle-states节点中各属性的含义如下:
(1)用于指定cpu在进入idle状态时是否需要停止其local timer
(2)用于指示该状态state对应的参数,该值会通过psci命令传给bl31,并由bl31处理
(3)cpu进入该idle state时的延迟时间
(4)cpu从该idle state中唤醒的延迟时间
(5)由于进入不同idle state时的代价不同,因此cpu在进入idle之前,会估计其预期驻留在idle状态的时间,只有当其预期驻留时间超过min-residency-us时,使用该state才有价值
以上这些参数由dt_init_idle_driver init_state_node函数解析,其代码如下:
static int init_state_node(struct cpuidle_state *idle_state,
const struct of_device_id *match_id,
struct device_node *state_node)
{
…
idle_state->enter = match_id->data; (1)
idle_state->enter_s2idle = match_id->data;
err = of_property_read_u32(state_node, "wakeup-latency-us",
&idle_state->exit_latency); (2)
if (err) {
u32 entry_latency, exit_latency;
err = of_property_read_u32(state_node, "entry-latency-us",
&entry_latency);
…
err = of_property_read_u32(state_node, "exit-latency-us",
&exit_latency);
…
idle_state->exit_latency = entry_latency + exit_latency; (3)
}
err = of_property_read_u32(state_node, "min-residency-us",
&idle_state->target_residency); (4)
…
err = of_property_read_string(state_node, "idle-state-name", &desc); (5)
if (err)
desc = state_node->name;
idle_state->flags = 0;
if (of_property_read_bool(state_node, "local-timer-stop")) (6)
idle_state->flags |= CPUIDLE_FLAG_TIMER_STOP;
…
}
1)该参数用于设置进入该idle状态的回调函数。它通过设备match结构体的data成员指定,如对于arm64架构通用idle初始化流程,其定义如下(driver/cpuidle/cpuidle-psci.c):
static const struct of_device_id psci_idle_state_match[] = {
{ .compatible = "arm,idle-state",
.data = psci_enter_idle_state },
{ },
}
(2)通过wakeup-latency-us属性获取idle状态的退出延迟时间
(3)若wakeup-latency-us属性未指定,则通过entry-latency-us和exit-latency-us功能共同计算退出延迟
(4)根据min-residency-us属性,获取该状态的最小idle驻留时间
(5)获取该状态的名字
(6)根据local-timer-stop属性,确定cpu进入idle时是否需要停止local timer
3.2.2 cpu idle驱动注册
cpu idle驱动注册流程比较简单,它主要包含以下三部分内容
(1)idle state相关参数设置、以及可能的broadcast timer
(2)若设置了local-timer-stop属性,则为每个cpu设置相应的broadcast timer
(3)若为该driver指定了governor,则切换current governor
以下为其代码实现:
int cpuidle_register_driver(struct cpuidle_driver *drv)
{
…
ret = __cpuidle_register_driver(drv); (1)
…
if (!ret && !strlen(param_governor) && drv->governor &&
(cpuidle_get_driver() == drv)) {
mutex_lock(&cpuidle_lock);
gov = cpuidle_find_governor(drv->governor);
if (gov) {
cpuidle_prev_governor = cpuidle_curr_governor;
if (cpuidle_switch_governor(gov) < 0) (2)
cpuidle_prev_governor = NULL;
}
mutex_unlock(&cpuidle_lock);
}
…
}
1)驱动注册主流程
(2)执行cpu idle governor切换操作
其中__cpuidle_register_driver的主要实现如下:
```c
static int __cpuidle_register_driver(struct cpuidle_driver *drv)
{
…
__cpuidle_driver_init(drv); (1)
ret = __cpuidle_set_driver(drv); (2)
if (ret)
return ret;
if (drv->bctimer)
on_each_cpu_mask(drv->cpumask, cpuidle_setup_broadcast_timer,
(void *)1, 1); (3)
return 0;
}
(1)设置该idle state的一些相关参数
(2)将驱动指针保存到全局变量中
(3)若设置了local-timer-stop属性,则为每个cpu设置broadcast timer
**3.2.3 cpu idle设备注册**
cpu idle设备注册主要包括初始化一些参数值,将该设备添加到全局设备链表中,然后为其初始化sysfs属性和使能该设备。其代码流程如下:
```c
int cpuidle_register_device(struct cpuidle_device *dev)
{
…
__cpuidle_device_init(dev); (1)
ret = __cpuidle_register_device(dev); (2)
if (ret)
goto out_unlock;
ret = cpuidle_add_sysfs(dev); (3)
if (ret)
goto out_unregister;
ret = cpuidle_enable_device(dev); (4)
if (ret)
goto out_sysfs;
cpuidle_install_idle_handler(); (5)
…
}
(1)初始化该设备对应的统计数据等参数
(2)设置cpu idle设备统计数据的disable标志,并将该设备添加到全局设备链表中
(3)初始化该设备对应的sysfs属性
(4)使能对应的sysfs设备
(5)设置设备初始化完成标志
4 cpu idle触发流程
cpu启动完成时,会通过cpu_startup_entry函数将其自身切换到idle线程。除此之外,当某个cpu上没有可运行线程时,也会切换idle线程。它们的切换流程分别如下:
(1)primary cpu启动时切换到idle线程流程

(2)secondary cpu启动时切换到idle线程流程

(3)cpu上没有可执行任务时切换到idle线程流程

以上这几种方式最终都会执行idle线程的主函数do_idle,并最终通过该函数将cpu设置为特定的idle状态。以下为arm64架构下该函数的主要执行流程

从以上流程可知,内核有两种进入cpu idle的方式:
(1)当系统未注册cpu idle模块时,架构为内核提供了一个默认的cpu idle函数。该函数的实现很简单,当cpu需要进入idle状态时,直接执行wfi指令即可
(2)当系统注册了cpu idle模块,则通过cpuidle模块的当前governor选择一个合适的idle state,然后通过call_cpuidle函数执行相应的idle操作。该函数最终会调用state对应的enter函数,以使cpu进入idle状态
对于非acpi启动方式的arm64架构的cpu,该函数会调用psci接口陷入bl31,然后由bl31执行最终的硬件操作。该流程如下:

最后再看一下cpu何时会退出idle状态。我们知道内核调度器为了平衡cpu之间的负载,当新进程被创建或睡眠进程被唤醒时,需要为其重新选择cpu,从而可向空闲cpu分配任务。此外,在负载均衡流程中负载轻的cpu也会从其它负载重的cpu上拉取任务,它也可使cpu从idle状态转换为运行状态
5 cpu idle governor
从cpu idle流程中可看出,governor用于确定cpu应该采用哪一种idle state。它的主要依据为cpu预期空闲时间和idle退出延迟等。在介绍具体governer之前,我们先看一下这些参数的含义:
(1)退出延迟(exit_latency)
该参数用于表示cpu进入某个idle state后,退出该state所需要的延迟时间。由于功耗越低,延迟时间越长,因此为了尽量降低功耗, governor应该在满足系统容忍度前提下,选择功耗最低的state。
(2)idle目标驻留时间(target_residency)
由于cpu进入和退出idle状态是需要消耗能量的,因此若其在idle状态驻留时间太短,并不能达到降低功耗的目的。为此,每种idle状态都定义了一个最小驻留时间,为governor提供是否需要进入该state的参考
(3)延迟容忍度(latency_req)
该参数用于提供系统的延迟容忍度,governor可用该值判断给定idle state是否能满足系统的延迟要求
(4)预期驻留时间
由于每种idle state都提供了一个idle目标驻留时间,而实际上cpu并不知道其何时会被唤醒。因此需要采用一些预测算法,来预测其可能的idle驻留时间,该时间被称为预期驻留时间
(5)下一个定时器到期时间
由于cpu上的定时器到期表明其有任务需要处理,因此最近一次到期任务的时间可被用于预测其预期驻留时间
为了满足不同的需求,内核当前一共实现了haltpoll、ladder、teo和menu四种governor。下面我们将重点介绍haltpoll governor和ladder governor的实现原理,大家若对其它两种governor感兴趣,可以自行分析一下
5.1 haltpoll governor
它是用于优化虚拟机性能的一种cpu idle governor。其原理为当vcpu进入idle时,通过guest端执行poll操作,以避免使其陷入host中。它的优点是减少了vm切换和通过ipi唤醒vcpu的成本,但它也造成在guest睡眠时,host无法复用该vcpu对应的物理cpu,从而降低系统吞吐量的问题。
它一共定义了两种idle state,其中state[1]使用默认的idle实现,其相应的定义如下(drivers/cpuidle/cpuidle-haltpoll.c):
static struct cpuidle_driver haltpoll_driver = {
.name = "haltpoll",
.governor = "haltpoll",
.states = {
{ /* entry 0 is for polling */ },
{
.enter = default_enter_idle, (1)
.exit_latency = 1,
.target_residency = 1,
.power_usage = -1,
.name = "haltpoll idle",
.desc = "default architecture idle",
},
},
.safe_state_index = 0,
.state_count = 2,
}
static int default_enter_idle(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int index)
{
if (current_clr_polling_and_test()) {
local_irq_enable();
return index;
}
default_idle(); (2)
return index;
}
(1)state[1]通过default_enter_idle回调使用默认的idle处理函数
(2)调用默认idle处理函数
而state[0]通过cpuidle_poll_state_init函数定义,其代码如下:
void cpuidle_poll_state_init(struct cpuidle_driver *drv)
{
struct cpuidle_state *state = &drv->states[0];
snprintf(state->name, CPUIDLE_NAME_LEN, "POLL");
snprintf(state->desc, CPUIDLE_DESC_LEN, "CPUIDLE CORE POLL IDLE");
state->exit_latency = 0;
state->target_residency = 0;
state->exit_latency_ns = 0;
state->target_residency_ns = 0;
state->power_usage = -1;
state->enter = poll_idle;
state->flags = CPUIDLE_FLAG_POLLING;
}
该state的idle处理函数为poll_idle,其原理设置一个poll的到期时间,然后执行轮询操作,直到时间到期后退出。其代码如下(drivers/cpuidle/poll_state.c):
static int __cpuidle poll_idle(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int index)
{
u64 time_start = local_clock();
…
if (!current_set_polling_and_test()) {
unsigned int loop_count = 0;
u64 limit;
limit = cpuidle_poll_time(drv, dev); (1)
while (!need_resched()) {
cpu_relax();
if (loop_count++ < POLL_IDLE_RELAX_COUNT)
continue;
loop_count = 0;
if (local_clock() - time_start > limit) { (2)
dev->poll_time_limit = true;
break;
}
}
}
…
}
(1)获取轮询到期时间
(2)判断轮询时间是否到期,若到期则退出
相应地其governor的state选择函数也很简单,主要是根据一些条件判断cpu是使用轮询方式还是默认方式进入idle状态,以下为其代码实现:
static int haltpoll_select(struct cpuidle_driver *drv,
struct cpuidle_device *dev,
bool *stop_tick)
{
s64 latency_req = cpuidle_governor_latency_req(dev->cpu);
if (!drv->state_count || latency_req == 0) {
*stop_tick = false;
return 0;
}
if (dev->poll_limit_ns == 0)
return 1;
if (dev->last_state_idx == 0) {
if (dev->poll_time_limit == true)
return 1;
return 0;
}
return 0;
}
5.2 ladder governor
该governor通过cpu前一次idle状态的驻留时间是否超过该state延迟时间一个特定的值(promotion_time_ns),以及下一个state的延迟时间是否超过系统延迟容忍度,来确定是否需要提升idle state。由于该governor每次只能提升一个state,因此其state提升方式就像梯子一样逐级往上,这也是它的名字由来。以下为其state选择函数:
static int ladder_select_state(struct cpuidle_driver *drv,
struct cpuidle_device *dev, bool *dummy)
{
…
if (unlikely(latency_req == 0)) {
ladder_do_selection(dev, ldev, last_idx, 0);
return 0;
}
last_state = &ldev->states[last_idx];
last_residency = dev->last_residency_ns - drv->states[last_idx].exit_latency_ns;
if (last_idx < drv->state_count - 1 &&
!dev->states_usage[last_idx + 1].disable &&
last_residency > last_state->threshold.promotion_time_ns &&
drv->states[last_idx + 1].exit_latency_ns <= latency_req) { (1)
last_state->stats.promotion_count++;
last_state->stats.demotion_count = 0;
if (last_state->stats.promotion_count >= last_state->threshold.promotion_count) {
ladder_do_selection(dev, ldev, last_idx, last_idx + 1); (2)
return last_idx + 1;
}
}
if (last_idx > first_idx &&
(dev->states_usage[last_idx].disable ||
drv->states[last_idx].exit_latency_ns > latency_req)) { (3)
int i;
for (i = last_idx - 1; i > first_idx; i--) {
if (drv->states[i].exit_latency_ns <= latency_req)
break;
}
ladder_do_selection(dev, ldev, last_idx, i);
return i;
}
if (last_idx > first_idx &&
last_residency < last_state->threshold.demotion_time_ns) { (4)
last_state->stats.demotion_count++;
last_state->stats.promotion_count = 0;
if (last_state->stats.demotion_count >= last_state->threshold.demotion_count) {
ladder_do_selection(dev, ldev, last_idx, last_idx - 1); (5)
return last_idx - 1;
}
}
return last_idx;
}
(1)其下一个state的延迟小于系统延迟容忍度,且上一次idle驻留时间减去退出延迟大于promotion_time_ns,则表明其具有提升state的空间
(2)只有当以上条件满足promotion_count指定的次数(当前该值被定义为4)后,才会实际提升idle state
(3)若最后一次进入idle时对应state的延迟时间大于延迟容忍度,则需要降低state id。由于延迟容忍度是系统硬指标,因此其state id不能采用ladder方式下降,而应该直接降低到可满足延迟容忍度的最大 state id
(4)上一次idle驻留时间减去退出延迟小于demotion_time_ns,则表明其具有下降state的空间
(5)只有当以上条件满足demotion_count指定的次数(当前该值被定义为1)后,才会实际下降idle state
更多推荐


所有评论(0)