内核功耗-idle

idle

CPU是用来计算的,如果CPU没事可干的话,Linux Kernel中称cpu这种状态为idle,而内核中的
cpuidle framework就是为了管理这种状态。

idle进程

CPU任何时候都是要执行进程的,把idle做成进程就可以和内核中当前的任务调度系统很自然的链接起来。
系统初始化之后,就会把第一个init进程变为idle进程,并把优先级设置成最低。
当idle进程被调用的时候,说明cpu已经没事可干了。

这样的话,如果要设置CPU状态的话,只需要在idle进程里面操作就可以了。
所以idle的整个逻辑就拆成了这么几步:

  • CPU支持哪些状态,这些状态通过什么途径上报给操作系统
  • 操作系统基于什么策略选择让CPU进入什么状态
  • 这些CPU状态的进入和退出,需要怎么执行

下面就围绕这个逻辑来展开。

CPU idle状态

cpu core根据功耗有不同的状态,ACPI定义了processor state,称为c state。
功耗越低唤醒延时越大。C0代表运行态,C1、C2、CX是具体的硬件实现,数字越大,睡眠程度越深。

cpuidle c-state

cpuidle c-state2

后面ACPI又引入了Low Power Idle扩展,idle的状态不仅和core相关,也可以和系统中其它组件相关。

ARM64 cpu idle

ARM上cpu idle最初是通过WFI和WFE指令来实现的。
但是随着对时延要求的提高,CX state的引入,多级idle就自然而然产生了。
16年Linux Kernel也支持了ACPI LPI扩展。

linux kernel cpuidle framework

内核中的cpuidle框架逻辑视图如下:

cpuidle framework

开发视图如下:

cpuidle framework

整体逻辑

boot阶段

系统启动后,创建idle线程

runtime阶段

  1. 判断是否有任务调度
    a. 如果有的话,就去做schedule调度

  2. 判断core有没有被offline,
    a. 如果被offline,则执行cpu die相关流程

  3. 判断有没有使用cpuidle framework
    a. 如果没有使用,则直接走default idle函数,执行wfi

  4. 如果使用了cpuidle framework,那接下来要调用cpuidle_select(),获取target state
    a. cpuidle_select(),根据当前使用的governor,根据系统的pm qos,预期驻留时间/唤醒延时,判断target state

  5. call_cpuidle()调用注册的driver进入target state
    a. driver主要做的事情是,在初始化时,从ACPI/DTS中获取c state信息;调用psci、pcc等协议和通信接口

  6. driver根据target state进入低功耗状态,有可能是idle、retention、shutdown等状态
    a. 如果是retention,会处于wfi,上下文不丢失,唤醒后执行下一条指令
    b. 如果是shutdown,会重新走resume流程

对于支持ACPI Low power idle的arm64来说,最后会调用acpi_idle_lpi_ender进入相应的idle状态,进而调用PSIC规范相关的函数psci_ops.cpu_suspend
之后通过SMC指令调用UEFI中ARM TrustFirmware中的psci_cpu_suspend函数,从而进入厂商的plat_psci_ops_t中定义的相关函数。
完整调用栈如下:

linux kernel:
  do_idle
    cpuidle_idle_call
      cpuidle_select
        cpuidle_enter
      acpi_idle_lpi_enter
        acpi_processor_ffh_lpi_enter
          psci_cpu_suspend_enter
            psci_ops->cpu_suspend
          psci_0_2_cpu_suspend
            smc CPU_SUSPEND


trust firmware:
  psci_smc_handler
    psci_cpu_suspend
      cpu_standby
        plat_psci_ops_t->cpu_standby

测试验证

可以通过sysfs接口查看当前支持idle方式:

root@localhost:~# grep "" /sys/devices/system/cpu/cpuidle/*
/sys/devices/system/cpu/cpuidle/available_governors:ladder menu teo
/sys/devices/system/cpu/cpuidle/current_driver:none
/sys/devices/system/cpu/cpuidle/current_governor:menu
/sys/devices/system/cpu/cpuidle/current_governor_ro:menu

如果支持了LPI,则会看到:

sys/devices/system/cpu/cpuidle/low_power_idle_cpu_residency_us
sys/devices/system/cpu/cpuidle/low_power_idle_system_residency_us

负载测试

带负载的测试,可以参考ARM Neoverse平台的LPI测试方法。

参考

知道是不会有人点的,但万一有人呢:)