Linux PM QoS framework(3)_per-device PM QoS

2023-02-23 18:53:11 浏览数 (2)

1. 前言

per-device PM QoS是针对指定设备的QoS framework,背后的思考如下:

1)resume_latency

在Runtime PM的框架下,当device的引用计数减为0的时候,RPM会suspend该device。不过,device进入suspend状态以及从suspend状态resume是需要消耗时间的(相关信息保存在pm domain中),而系统其它实体(如用户空间程序)可能对该设备的响应时间有要求,这就是一种形式的QoS request,称作resume_latency。

per-device PM QoS framework会提供相应的接口,收集指定设备的resume_latency request,并提供给Runtime PM,它在suspend设备时,会考虑这种需求,并决定是否suspend设备。

2)latency_tolerance

一些复杂的设备,在运行状态(active)时,为了节省功耗,也有可能自行进入某些省电状态,相应的,设备的响应速度可能降低。如果该设备足够智能,可能会提供一个回调函数(.set_latency_tolerance,位于dev_pm_info结构中),以便设置最大的延迟容忍时间。这称作latency_tolerance。

对per-device PM QoS来说,需要提供一种机制,收集所有的、针对某个设备的latency_tolerance需求,并汇整出可以满足所有需求的latency_tolerance,通过设备的回调函数告知设备。

3)no power off/remote wakeup

在Runtime PM的框架下,设备suspend之后,还可以进一步通过pm domain关闭该设备的供电,以节省功耗。但关闭供电时,除了要考虑对设备resume_latency的需求之外,还要考虑该设备是否允许关闭供电,以及该设备是否需要作为一个唤醒源(remote wakeup)。

这是另一种形式的QoS request,称作per-device PM QoS flag,表示系统其它实体对该设备的一些特定行为的需求。当前的flag有两种:

  • PM_QOS_FLAG_NO_POWER_OFF,表示不允许设备断电
  • PM_QOS_FLAG_REMOTE_WAKEUP,表示设备应具备唤醒功能

这两个flag可以通过或操作,同时生效。

因此,per-device PM QoS framework的功能,就是抽象上面两类需求,包括:向requestor提供QoS request的add、update、remove等API,包括内核空间API和用户空间API;汇整、整理这些request;向电源管理有关的service(主要是pm domain framework)提供汇整后的request信息,以便这些service可以做出正确的决定。

下面将会结合source code(位于drivers/base/power/qos.c中),介绍上面的实现逻辑。

2. API汇整

2.1 struct dev_pm_qos数据结构

每个设备的per-device pm qos信息,都保存在设备的qos指针中,即:

代码语言:javascript复制
   1: struct device {
   2:         ...
   3:         struct dev_pm_info      power;
   4:         ...
   5: }
   6:  
   7: struct dev_pm_info {
   8:         ...
   9:         struct dev_pm_qos       *qos;
  10:         ...
  11: }

该指针的数据类型struct dev_pm_qos是per-device pm qos的核心数据结构,定义如下:

代码语言:javascript复制
   1: struct dev_pm_qos {
   2:         struct pm_qos_constraints resume_latency;
   3:         struct pm_qos_constraints latency_tolerance;
   4:         struct pm_qos_flags flags;
   5:         struct dev_pm_qos_request *resume_latency_req;
   6:         struct dev_pm_qos_request *latency_tolerance_req;
   7:         struct dev_pm_qos_request *flags_req;
   8: };

resume_latency,为第一种QoS request,表示其它实体对该设备从suspend状态返回的延迟的要求。struct pm_qos_constraints为pm qos要求的具体抽象,可参考“Linux PM QoS framework(2)_PM QoS class”中的描述;

latency_tolerance,为第二种QoS request,和resume_latency类似;

flags,为第三种QoS request,主要包括一个链表头,用于保存具体的flag要求,以及汇整后的、当前所有有效的flags;

resume_latency_req、latency_tolerance_req、flags_req,三个不同类型的request指针,用于保存用户空间对设备pm QoS的request请求。struct dev_pm_qos_request结构类似上一篇文章所描述的struct pm_qos_request结构,用于抽象一个具体的request。

代码语言:javascript复制
   1: struct dev_pm_qos_request {
   2:         enum dev_pm_qos_req_type type;
   3:         union {
   4:                 struct plist_node pnode;
   5:                 struct pm_qos_flags_request flr;
   6:         } data;
   7:         struct device *dev;
   8: };

type,request的类型,当前共三类,包括在前言部分所描述的三种request(DEV_PM_QOS_RESUME_LATENCY、DEV_PM_QOS_LATENCY_TOLERANCE、DEV_PM_QOS_FLAGS); data,不同类型的request,有不同的数据,因此这里是一个联合体。当为DEV_PM_QOS_RESUME_LATENCY、DEV_PM_QOS_LATENCY_TOLERANCE时,为一个plist_node,类似PM QoS class。为DEV_PM_QOS_FLAGS时,为struct pm_qos_flags_request类型的变量; dev,保存了设备指针。

2.2 向kernel其它driver提供的,用于提出per-device PM QoS需求的API

代码语言:javascript复制
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);

类似PM QoS class中的pm_qos_*接口,不过操作对象为设备,因而需要提供相应的设备指针。

2.3 向kernel PM有关的service(例如PM domain)提供的,用于获取、跟踪指定PM QoS需求的API

代码语言:javascript复制
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);
int dev_pm_qos_add_global_notifier(struct notifier_block *notifier);
int dev_pm_qos_remove_global_notifier(struct notifier_block *notifier);

由于DEV_PM_QOS_FLAGS特殊性,kernel提供了单独的API,以获取相应的flags。对于其它两个类型的QoS,和PM QoS class中的pm_qos_*接口类似。

2.4 向用户空间process提供的,用于提出per-device PM QoS需求的API

通过sysfs文件,kernel允许用户空间程序对某个设备提出QoS需求,这些sysfs文件位于各个设备的sysf目录下,默认情况下,PM QoS framework不会创建这些文件,除非具体设备驱动调用dev_pm_qos_expose_*系列接口,主动创建。具体可参考代码,这里不再详细说明。

3. 实现思路和内部逻辑

和PM QoS class类似,不再描述。

0 人点赞