1, 介绍
PM QOS(power management quality of service,服务质量)表示linux内核电源管理的质量。在很多产品及场景功耗和性能是一个tradeoff的艺术。Linux提供了众多的功耗管理机制用来尽可能减少不必要的功耗:cpu idle、cpu hotplug、cpu dvfs、系统休眠唤醒、runtime power management、device dvfs、clock gate等(这些机制以往文章有介绍,大家感兴趣的话,可以往前翻阅)。但是这些机制实施的时候,会有性能的开销。
对性能的影响主要两方面:
- 延时(latency)增加:时间的开销,尤其是在恢复的过程中需要时间。比如,系统唤醒需要经过各驱动的恢复,power domain的上电过程也有时间开销。
- 吞吐量(Throughput)减少:低功耗也会带来算力的影响,会降低算力及网路的吞吐量。比如,cpu dvfs、cpu hotplug、cpu idle等会影响到cpu算力。
这些时间及性能开销,最终会影响到用户的体验,比如界面操作不流畅、卡顿,响应时间过长。怎么减少功耗管理对性能的影响呢?于是PM QOS就有了,它定义了一套框架,用于系统各个模块(进程、设备驱动等)对服务质量的期待目标。也就是说,系统在做具体的低功耗管理时,不是当前想做就直接做了,还要考虑系统对服务质量的要求,只有满足要求了,才能做clock gate、power gate、cpu dvfs等。
2, 框架
linux pm qos框图
PM QOS使用constraint(约束)作为指标,用于各模块对PM的诉求及限制。当前系统的指标主要有两类,分别对应两个PM QOS framework。
- 系统级constraint:包括cpu&dma latency(5.4内核),它的实际意义是,当产生一个事件之后(如一个中断),CPU或DMA的响应延迟。例如有些CPU的串口控制器,只有几个byte的FIFO,当接收数据时,CPU或DMA必须在FIFO填满前,将数据读走,否则就可能丢失数据或者降低数据的传输速率。由PM QoS classes framework管理。
- 设备级constraint:包括从低功耗状态resume的latency、active状态的latency和一些QoS flag(如是否允许power off)。由per-device PM QoS framework管理。(需要进一步完善)
整个PM QOS框架分为三部分:
- 需求方:各service、各driver。他们根据自己的功能需求,提出系统或某些功能的QOS约束,比如cpu&dma latency。
- 框架层:PM QOS framework,包含PM QOS classes、per device PM QOS。向需求方提供request的add、modify、remove等接口,用于管理QoS requests。对需求方的约束进行分类,计算出极值,比如cpu&dma latency不小于某个值。向执行方提供request value的查询接口。PM QoS classes framework位于kernel/power/qos.c中,负责系统级别的PM QoS管理,通过misc设备(/dev/cpu_dma_latency),向用户空间程序提供PM QoS的request、modify、remove功能,以便满足各service对PM QoS的需求。per-device PM QoS framework位于drivers/base/power/qos.c中,负责per-device的PM QoS管理。
- 执行方:power management的机制,比如cpuidle、cpu dvfs等。需要满足由框架层根据需求方提供的约束计算的极值,才能执行相应的低功耗机制。
3, 数据结构
Linux pm qos数据结构
4, 流程
4.1 初始化流程
linux pm qos初始化流程
4.2 工作流程
1)PM QoS class framework的函数接口
PM QoS class framework提供的API有两类:
- 以函数调用的形式,为kernel space的driver、service等提供的。
- 以misc设备的形式,为用户空间进程提供的。
(1) 向kernel其它driver提供的,用于提出PM QoS需求的API
void pm_qos_add_request(struct pm_qos_request *req, int pm_qos_class, s32 value);
void pm_qos_update_request(structpm_qos_request *req, s32 new_value);
void pm_qos_update_request_timeout(struct pm_qos_request *req, unsigned long timeout_us);
void pm_qos_remove_request(struct pm_qos_request *req);
int pm_qos_request_active(struct pm_qos_request *req);
系统pm qos请求接口
(2) 向kernel PM有关的模块提供的,用于获取、跟踪指定PM QoS需求的API
每当有新的QoS请求时,framework会根据该QoS class的含义,计算出满足所有请求的一个极值(如最大值、最小值等等)。该值可以通过pm_qos_request接口获得。例如cpuidle framework在选择C state时,会通过该接口获得系统对CPU&DMA latency的需求,并保证从C state返回时的延迟小于该value。
另外,如果某个实体在意某一个class的QoS value变化,可以通过pm_qos_add_notifier接口添加一个notifier,这样当value变化时,framework便会通过notifier的回调函数,通知该实体。
获取系统pm qos约束的接口
(3) 向per-device PM QoS framework提供,底层的PM QoS操作API
QoS class和per-device PM QoS都是基于底层的pm qos constraint封装而来的。对QoS class的使用者而言,可以不用关心这些底层细节。对per-device PM QoS framework而言,则需要利用它们实现自身的功能。
int pm_qos_update_target(struct pm_qos_constraints *c, struct plist_node *node, qos_req_action action, int value);
bool pm_qos_update_flags(struct pm_qos_flags *pqf, qos_flags_request *req, qos_req_action action, s32 val);
s32 pm_qos_read_value(struct pm_qos_constraints *c);
(4) 向用户空间service提供的,用于提出QoS需求的API
/dev/cpu_dma_latency
打开文件,将会使用默认值,向PM QoS framework添加一个QoS请求。
关闭文件,会移除相应的请求。
写入value,更改请求的值。
读取文件,将会获取QoS的极值。
2)per-devicePM QoS framework的函数接口
(1)向kernel其它driver提供的,用于提出per-device PM QoS需求的API
int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, enum dev_pm_qos_req_type type, s32 value);
int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value);
int dev_pm_qos_remove_request(struct dev_pm_qos_request *req);
per device pm qos请求接口
(2)向kernel PM有关的模块(例如power domain)提供的,用于获取、跟踪指定PM QoS需求的API
enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev, s32 mask);
s32 dev_pm_qos_read_value(struct device *dev);
int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier);
int dev_pm_qos_remove_notifier(struct device *dev,struct notifier_block *notifier);
per device pm qos约束获取接口
(3)向用户空间service提供的,用于提出per-device PM QoS需求的API
通过sysfs文件,kernel允许用户空间程序对某个设备提出QoS需求,这些sysfs文件位于各个设备的sysf目录下,默认情况下,PM QoS framework不会创建这些文件,需要具体设备驱动调用dev_pm_qos_expose_*系列接口,主动创建。