概述
RPM即Runtime power management的缩写,之Linux提供的一套电源管理框架。核心思想是分而治之的管理思想,将具体的控制策略和控制权力下放到各个驱动。 为所有device提供一种相对独立的电源管理方案,驱动可以自行决定何时打开或者关闭电源。Linux的suspend/resume机制也可以在合适的时机统一执行设备的休眠和唤醒动作,进而形成系统级电源管理方案。
软件框图
device driver(或者driver所在的bus、class等)需要提供3个回调函数,runtime_suspend、runtime_resume和runtime_idle,分别用于suspend device、resume device和idle device。它们一般由RPM core在合适的时机调用,以便降低device的power consumption。
而调用的时机,最终是由device driver决定的。driver会在适当的操作点,调用RPM core提供的put和get系列的helper function,汇报device的当前状态。RPM core会为每个device维护一个引用计数,get时增加计数值,put时减少计数值,当计数为0时,表明device不再被使用,可以立即或一段时间后suspend,以节省功耗。
除了RPM之外,Android上提出一种电源管理机制Opportunistic suspend。但目前Linux kernel用的比较多的还是RPM。
核心机制
- 为每个设备维护一个引用计数(device->power.usage_count),用于指示该设备的使用状态。
- 需要使用设备时,device driver调用pm_runtime_get(或pm_runtime_get_sync)接口,增加引用计数;不再使用设备时,device driver调用pm_runtime_put(或pm_runtime_put_sync)接口,减少引用计数。
- 每一次put,RPM core都会判断引用计数的值。如果为零,表示该设备不再使用(idle)了,则使用异步(ASYNC)或同步(SYNC)的方式,调用设备的.runtime_idle回调函数。
- runtime_idle的存在,是为了在idle和suspend之间加一个缓冲,避免频繁的suspend/resume操作。因此它的职责是:判断设备是否具备suspend的条件,如果具备,在合适的时机,suspend设备。
1 可以不提供,RPM core会使用异步(ASYNC)或同步(SYNC)的方式,调用设备的.runtime_suspend回调函数,suspend设备,同时记录设备的PM状态,
2 可以调用RPM core提供helper函数(pm_runtime_autosuspend_expiration、pm_runtime_autosuspend、pm_request_autosuspend),要求在指定的时间后,suspend设备。
- pm_runtime_autosuspend、pm_request_autosuspend等接口,会起一个timer,并在timer到期后,使用异步(ASYNC)或同步(SYNC)的方式,调用设备的.runtime_suspend回调函数,suspend设备,同时记录设备的PM状态。
- 每一次get,RPM core都会判断设备的PM状态,如果不是active,则会使用异步(ASYNC)或同步(SYNC)的方式,调用设备的.runtime_resume回调函数,resume设备。
注1 :Runtime PM中的“suspend”,不一定要求设备必须进入低功耗状态,而是要求设备在suspend后,不再处理数据,不再和CPUs、RAM进行任何的交互,直到设备的.runtime_resume被调用。因为此时设备的parent(如bus controller)、CPU是、RAM等,都有可能因为suspend而不再工作,如果设备再有任何动作,都会造成不可预期的异常。下面是“Documentation\power\runtime_pm.txt”中的解释,供大家参考:
1 * Once the subsystem-level suspend callback (or the driver suspend callback,
2 if invoked directly) has completed successfully for the given device, the PM
3 core regards the device as suspended, which need not mean that it has been
4 put into a low power state. It is supposed to mean, however, that the
5 device will not process data and will not communicate with the CPU(s) and
6 RAM until the appropriate resume callback is executed for it. The runtime
7 PM status of a device after successful execution of the suspend callback is
8 'suspended'.
注2 :回忆一下wakeup events和wakeup lock,Runtime PM和它们在本质上是一样的,都是实时的向PM core报告“我不工作了,可以睡了”、“我要工作了,不能睡(或醒来吧)”。不同的是:wakeup events和RPM的报告者是内核空间drivers,而wakeup lock是用户空间进程;wakeup events和wakelock涉及的睡眠对象是整个系统,包括CPU和所有的devices,而RPM是一个一个独立的device(CPU除外,它由cpu idle模块处理,可看作RPM的特例)。
同步和异步
级联设备PM
PM的API汇总
到目前为止,linux kernel的runtime PM接口定义在“include\linux\pm_runtime.h”中,有将近50个接口。实际使用中并非全部会用到。
RPM提供的API位于“include/linux/pm_runtime.h”中,常用的接口如下:
1 extern int __pm_runtime_idle(struct device *dev, int rpmflags);
2 extern int __pm_runtime_suspend(struct device *dev, int rpmflags);
3 extern int __pm_runtime_resume(struct device *dev, int rpmflags);
这三个函数是RPM的idle、put/suspend、get/resume等操作的基础,根据rpmflag,有着不同的操作逻辑。后续很多API,都是基于它们三个。一般不会在设备驱动中直接使用。
1 extern int **pm_schedule_suspend(**struct **device *dev, unsigned** int **delay);**
在指定的时间后(delay,单位是ms),suspend设备。该接口为异步调用,不会更改设备的引用计数,可在driver的.rpm_idle中调用,免去driver自己再启一个timer的烦恼。
1 extern void **pm_runtime_enable(**struct **device *dev);**
2 extern void **pm_runtime_disable(**struct **device *dev);**
设备RPM功能的enable/disable,可嵌套调用,会使用一个变量(dev->power.disable_depth)记录disable的深度。只要disable_depth大于零,就意味着RPM功能不可使用,很多的API调用(如suspend/reesume/put/get等)会返回失败。RPM初始化时,会将所有设备的disable_depth置为1,也就是disable状态,driver初始化完毕后,要根据设备的时机状态,调用这两个函数,将RPM状态设置正确。
1 extern void pm_runtime_allow(struct device *dev);
2 extern void pm_runtime_forbid(struct device *dev);
RPM core通过sysfs(drivers/base/power/sysfs.c),为每个设备提供一个“/sys/devices/.../power/control”文件,通过该文件可让用户空间程序直接访问device的RPM功能。这两个函数用来控制是否开启该功能(默认开启)。
1 extern int **pm_runtime_barrier(**struct **device *dev);**
这名字起的!!!由3.3的描述可知,很多RPM请求都是异步的,这些请求会挂到一个名称为“pm_wq”的工作队列上,这个函数的目的,就是清空这个队列,另外如果有resume请求,同步等待resume完成。好复杂,希望driver永远不要用到它!!
1 extern int pm_generic_runtime_idle(struct device *dev);
2 extern int pm_generic_runtime_suspend(struct device *dev);
3 extern int pm_generic_runtime_resume(struct device *dev);
几个通用的函数,一般给subsystem的RPM driver使用,直接调用devie driver的相应的callback函数。
1 extern void **pm_runtime_no_callbacks(**struct *device dev);
告诉RPM core自己没有回调函数,不用再调用了(或者调用都是成功的),真啰嗦。
1 extern void **pm_runtime_irq_safe(**struct *device dev);
告诉RPM core,如下函数可以在中断上下文调用: pm_runtime_idle() pm_runtime_suspend() pm_runtime_autosuspend() pm_runtime_resume() pm_runtime_get_sync() pm_runtime_put_sync() pm_runtime_put_sync_suspend() pm_runtime_put_sync_autosuspend()
1 static inline int pm_runtime_idle(struct device *dev)
2 static inline int pm_runtime_suspend(struct device *dev)
3 static inline int pm_runtime_resume(struct device *dev)
直接使用同步的方式,尝试idle/suspend/resume设备,如果条件许可,就会执行相应的callback函数。driver尽量不要使用它们。
1 static inline int pm_request_idle(struct device *dev)
2 static inline int pm_request_resume(struct device *dev)
和上面类似,不过调用方式为异步。尽量不要使用它们。
1 static inline int pm_runtime_get(struct device *dev)
2 static inline int pm_runtime_put(struct device *dev)
增加/减少设备的使用计数,并判断是否为0,如果为零,尝试调用设备的idle callback,如果不为零,尝试调用设备的resume callback。这两个接口是RPM的正统接口啊,多多使用!
1 static inline int pm_runtime_get_sync(struct device *dev)
2 static inline int pm_runtime_put_sync(struct device *dev)
3 static inline int pm_runtime_put_sync_suspend(struct device *dev)
和上面类似,只不过为同步调用。另外提供了一个可直接调用suspend的put接口,何必的!
1 static inline int pm_runtime_autosuspend(struct device *dev)
2 static inline int pm_request_autosuspend(struct device *dev)
3 static inline int pm_runtime_put_autosuspend(struct device *dev)
4 static inline int pm_runtime_put_sync_autosuspend(struct device *dev)
autosuspend相关接口。所谓的autosuspend,就是在suspend的基础上,增加一个timer,还是觉得有点啰嗦。不说了。
1 static inline void pm_runtime_use_autosuspend(struct device *dev)
2 static inline void pm_runtime_dont_use_autosuspend(struct device *dev)
3 extern void pm_runtime_set_autosuspend_delay(struct device *dev, int delay);
4 extern unsigned long pm_runtime_autosuspend_expiration(struct device *dev);
控制是否使用autosuspend功能,以及设置/获取autosuspend的超时值。
总结一下:总觉得这些API所提供的功能有些重叠,重叠的有点啰嗦。可能设计者为了提供更多的便利,可过渡的便利和自由,反而是一种束缚和烦恼!
20220602
标签:suspend,struct,PM,dev,Linux,device,机制,runtime,pm From: https://www.cnblogs.com/545235abc/p/16338050.html