这个系列的文章,介绍了CPU 电源管理的一个重要分支CPUidle subsystem,所谓CPU idle,就是在CPU没有事情做的时候,通过clock gating或power gating的方式降低或是关闭CPU internal某些单元的clock或power的方式,降低CPU的功耗并改善发热问题。
文章是在参考了多位大神文章的基础上,结合自己对Linux kernel cpuidle 相关code以及ACPI spec的理解形成的,记录在这里,并不断地迭代修正,实现螺旋形上升成长。
cpuidle 相关的文章分5大部分,旨在解决以下几个问题:
1)why to idle?
2)what’s idle state?
3)when to idle?
4)which idle state to enter?
5)how to enter/exit idle state?
该系列文章包括:
概述,即本文,介绍一些背景知识和 cpuidle framework in Linux;
【what‘s idle states,】从ACPI spec定义和Linux kernel实现两个方面介绍各种idle state;
【cpuidle driver】解决how to enter idle state的问题。
【cpuidle device】利用linux设备模型的概念,虚拟出来一个cpuidle device,用来管理CPU idle相关的操作,并提供一些sysfs interface供userspace来控制;
【cpuidle governor】解决which idle state to enter的问题。
Why to Idle?
计算机系统中,CPU的功能是执行程序,就是取指、译码、执行。那么问题来了,如果没有程序要执行,CPU该怎么办呢?为了省电,需要停掉CPU,但什么时候停、怎么停呢?这就需要我们仔细斟酌了,因为实际的软硬件环境是非常复杂的。
在Linux系统中,CPU被两类程序占用:一类是进程(或线程),也称为进程上下文;另一类是中断、异常的处理程序,也称中断上下文。进程的存在是用来处理事务的,比如读取用户的输入,并显示在屏幕上。但是事务总有处理完的时候,如果用户不再输入,也没有新的内容需要显示在屏幕上。此时,该进程就可以让出CPU,但是要随时准备回来(用户可能突然有按键动作)。同理,如果系统没有中断、异常事件,CPU就不会花时间在中断上下文。
在Linux kernel中,把这种CPU无所事事的状态,称作idle state,并引入了idle framework来管理这种状态。
CPUidle Framework in Linux
Linux kernel中,cpuidle framework相关的代码在drivers/cpuidle目录下,主要有cpuidle core、cpuidle driver和cpuidle governor三部分构成,再结合kernel sched 中的cpuidle,共同完成cpuidle管理。
kernel sched
主要负责实现idle线程,代码位于kernel/sched/idle.c中。
CPUidle core 模块
Cpuidle core负责实现CPUidle framework的整体框架,主要实现以下功能:
1) 抽象出cpuidle device、cpuidle driver和cpuidle governor;
2) 向cpuidle driver模块,提供统一的driver注册和管理接口;
3) 向cpuidle governor模块,提供统一的governor注册和管理接口;
4) 向上层sched提供接口,比如cpuidle_enter、cpuidle_select等;
5) 以sysfs的形式,向userspace提供接口;
CPUidle governor模块
Cpuidle governor根据当前CPU load,确定进入哪个idle state。
Linux kernel中提供了menu和ladder两种governor,根据具体的场景选择进入哪个idle state,相应的代码在drivers/cpuidle/governors目录下。
CPUidle driver 模块
负责实现CPU idle,即解决如何进入idle state的问题。
不同的arch、CPU core,会有不同的cpuidle driver。你可以在cpuidle core提供的框架下,开发自己的cpuidle driver,我这里分析使用的是acpi cpuidle driver,对应的代码主要在drivers/acpi/processor_idle.c中。
了解了CPUidle framework之后,我们来看如何利用这个framework来实现CPU在normal execution state和low power state之间的转换。
CPU idle流程
进入idle state
CPU从C0切换到low power state时,需要解决以下三个问题:when to idle state?which idle state to enter?how to enter idle state?
- when to idle state?
sched中的idle线程被调度时,表示当前没有其他任务在执行,CPU可以进入到idle state以省电。 - which idle state to enter?
这就需要cpuidle governor来根据当前的CPU load,预计会idle的时间以及系统支持的idle state状态来选择一个合适的idle state了,在满足系统响应responsibility的前提下,尽可能省电。 - how to enter idle state?
这个由cpuidle driver来负责,不同的CPU arch在进入idle state时会有不同的方式,不同的idle state也会有不同的进入方式,这个我们会在cpuidle driver中详细说明。
退出idle state
CPU退出low-power state时,需要解决以下两个问题:什么条件下退出?退回到哪个power state?退回之后需要哪些额外处理?
- 什么条件下退出?
每个low power state,都会定义一些waking event用来让CPU退出当前的power state; - 退回到哪个power state?
一般是会退回到C0 state; - 退回到C0后,CPU如何操作?
在进入idle state之前,会调用local_irq_disable关闭local irq。
因此,退出idle state时,会先退回到idle thread,调用local_irq_enable之后,重启local irq,才能去执行相应的IRQ handler。在IRQ handler里面可能会唤醒其他等待的线程,CPU去执行其他线程;如果没有调度其他线程,则继续执行idle线程。
参考文件
ACPI spec6.0
窝窝科技-cpuidle