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指针中,即:
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的核心数据结构,定义如下:
struct dev_pm_qos {
struct pm_qos_constraints resume_latency;
struct pm_qos_constraints latency_tolerance;
struct freq_constraints freq;
struct pm_qos_flags flags;
struct dev_pm_qos_request *resume_latency_req;
struct dev_pm_qos_request *latency_tolerance_req;
struct dev_pm_qos_request *flags_req;
};
struct pm_qos_constraints resume_latency:这是一个 pm_qos_constraints 结构体,用于表示设备的恢复延迟(resume latency)的限制和要求。设备在从低功耗模式恢复到活动状态时,需要满足一定的延迟要求,以确保系统性能和响应性。这个结构体包含了与恢复延迟相关的约束信息。
struct pm_qos_constraints latency_tolerance:这是一个 pm_qos_constraints 结构体,用于表示设备的延迟容忍度(latency tolerance)的限制和要求。它指定了设备能够容忍的最大延迟,这在某些情况下可以影响设备的功耗优化策略。
struct freq_constraints freq:这是一个 freq_constraints 结构体,用于表示设备的频率约束。它指定了设备可以使用的频率范围,以便在功耗和性能之间进行权衡。
struct pm_qos_flags flags:这是一个 pm_qos_flags 结构体,用于表示设备的一些特殊标志和选项,例如设备是否支持异步操作。
struct dev_pm_qos_request *resume_latency_req:这是一个指向 dev_pm_qos_request 结构体的指针,用于跟踪设备的恢复延迟请求。当设备需要设置特定的恢复延迟时,会创建一个请求并与该指针关联。
struct dev_pm_qos_request *latency_tolerance_req:这是一个指向 dev_pm_qos_request 结构体的指针,用于跟踪设备的延迟容忍度请求。类似地,当设备需要设置特定的延迟容忍度时,会创建一个请求并与该指针关联。
struct dev_pm_qos_request *flags_req:这是一个指向 dev_pm_qos_request 结构体的指针,用于跟踪设备的特殊标志和选项请求。当设备需要设置特定的标志和选项时,会创建一个请求并与该指针关联。
这个结构体 struct dev_pm_qos 是用于在Linux内核中管理设备的功耗和性能要求的数据结构。它允许内核和设备驱动程序协调设备的电源管理和性能优化策略。通过这个结构体,设备可以请求特定的功耗和性能参数,而内核可以根据这些请求来调整系统的操作,以实现最佳的功耗和性能平衡。
示例可能包括一个网络接口设备,它可能需要在高网络负载时降低功耗以延长电池寿命,但在需要高性能时恢复到最高性能状态。该设备可以使用 struct dev_pm_qos 来请求延迟容忍度和频率要求,以满足这些需求。驱动程序可以创建相应的请求,并与设备的 struct dev_pm_qos 相关联,以实现所需的电源管理和性能优化。
struct dev_pm_qos_request {
enum dev_pm_qos_req_type type; // 请求类型,表示请求的是哪种参数
union {
struct plist_node pnode; // 用于链表操作的节点
struct pm_qos_flags_request flr; // 请求设备特殊标志和选项
struct freq_qos_request freq; // 请求设备的频率
} data; // 请求的具体数据
struct device *dev; // 与请求相关联的设备
};
enum dev_pm_qos_req_type type:这是一个枚举类型,表示请求的类型。它指示了这个请求是用于请求设备的哪种参数,例如延迟容忍度、特殊标志、频率等
enum dev_pm_qos_req_type {
DEV_PM_QOS_RESUME_LATENCY = 1,
DEV_PM_QOS_LATENCY_TOLERANCE,
DEV_PM_QOS_MIN_FREQUENCY,
DEV_PM_QOS_MAX_FREQUENCY,
DEV_PM_QOS_FLAGS,
};
union:这个联合体(union)包含了不同类型的请求数据,具体的类型由 type 字段确定。
struct plist_node pnode:用于链表操作的节点,通常用于将请求添加到链表中以跟踪多个请求。
struct pm_qos_flags_request flr:请求设备的特殊标志和选项。这可以包括设备是否支持异步操作等。
struct freq_qos_request freq:请求设备的频率参数,用于管理设备的电源和性能状态。
struct device *dev:这是一个指向设备结构体的指针,表示与请求相关联的设备。该请求将影响与此设备相关的电源管理和性能优化。
这个结构体 struct dev_pm_qos_request 用于在Linux内核中表示设备对电源管理和性能要求的请求。它允许设备驱动程序向内核提出特定的需求,以调整设备的功耗和性能策略。
这个结构体的作用是在设备和内核之间建立一种通信机制,以实现设备的电源管理和性能优化。设备可以通过创建和关联 struct dev_pm_qos_request 请求特定的功耗、性能或特殊标志,然后内核可以根据这些请求来调整系统的操作以满足这些需求。这有助于实现系统的功耗优化、性能调整和响应特殊设备需求。
示例可能包括一个CPU频率调节器,它可能需要在高负载时提高CPU频率以获得更好的性能,而在低负载时将频率降低以降低功耗。该频率调节器可以使用 struct dev_pm_qos_request 来请求频率变化,而内核可以根据这些请求来调整CPU频率以满足性能和功耗要求。
2.2 向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);
类似PM QoS class中的pm_qos_*接口,不过操作对象为设备,因而需要提供相应的设备指针。
drivers/thermal/devfreq_cooling.c
struct thermal_cooling_device *
of_devfreq_cooling_register_power(struct device_node *np, struct devfreq *df,
struct devfreq_cooling_power *dfc_power)
{
...
err = dev_pm_qos_add_request(dev, &dfc->req_max_freq,
DEV_PM_QOS_MAX_FREQUENCY,
PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE);
...
}
dev_pm_qos_add_request 函数的作用是向内核请求特定的电源管理 QoS(Quality of Service,服务质量)参数。在这个上下文中,它用于添加一个请求来管理设备频率。现在让我们详细分析 dev_pm_qos_add_request 的参数:
dev:这是与请求相关联的设备,即 devfreq 控制器所管理的设备。
&dfc->req_max_freq:这是一个 struct dev_pm_qos_request 结构的指针,用于跟踪设备的最大频率请求。这个请求可能会在内核中引发相应的频率调整,以满足设备的性能和功耗需求。
DEV_PM_QOS_MAX_FREQUENCY:这是请求的类型,指示请求的是设备的最大频率。它告诉内核该请求与设备频率相关。
PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE:这是请求的值,通常是默认值,表示请求的最大频率。内核可能根据这个请求来控制设备的频率以满足功耗和性能需求。
通过调用 dev_pm_qos_add_request 函数并传递这些参数,驱动程序可以请求内核根据设备频率需求来调整设备的性能。例如,如果设备需要在高负载时提高频率以获得更好的性能,那么通过这个请求,内核可以根据该需求调整设备的频率。
总之,这个函数的作用是将设备的最大频率请求添加到内核的电源管理QoS请求中,以便根据设备频率需求来管理性能和功耗。
2.3 向kernel PM有关的service(例如PM 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);
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_*接口类似。
/**
* dev_pm_qos_add_notifier - sets notification entry for changes to target value
* of per-device PM QoS constraints
*
* @dev: target device for the constraint
* @notifier: notifier block managed by caller.
* @type: request type.
*
* Will register the notifier into a notification chain that gets called
* upon changes to the target value for the device.
*
* If the device's constraints object doesn't exist when this routine is called,
* it will be created (or error code will be returned if that fails).
*/
int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier,
enum dev_pm_qos_req_type type)
{
int ret = 0;
mutex_lock(&dev_pm_qos_mtx);
if (IS_ERR(dev->power.qos))
ret = -ENODEV;
else if (!dev->power.qos)
ret = dev_pm_qos_constraints_allocate(dev);
if (ret)
goto unlock;
switch (type) {
case DEV_PM_QOS_RESUME_LATENCY:
ret = blocking_notifier_chain_register(dev->power.qos->resume_latency.notifiers,
notifier);
break;
case DEV_PM_QOS_MIN_FREQUENCY:
ret = freq_qos_add_notifier(&dev->power.qos->freq,
FREQ_QOS_MIN, notifier);
break;
case DEV_PM_QOS_MAX_FREQUENCY:
ret = freq_qos_add_notifier(&dev->power.qos->freq,
FREQ_QOS_MAX, notifier);
break;
default:
WARN_ON(1);
ret = -EINVAL;
}
unlock:
mutex_unlock(&dev_pm_qos_mtx);
return ret;
}
EXPORT_SYMBOL_GPL(dev_pm_qos_add_notifier);
这个函数的作用是为设备的QoS约束添加通知器,以便在目标值更改时接收通知。通知器可以用于响应与设备功耗和性能管理相关的事件。通常,驱动程序或模块可以使用这些通知器来实时调整设备的操作以满足性能、功耗或其他要求。
示例可能包括一个CPU频率管理模块,它需要知道设备的最小频率是否发生变化,以便根据需要调整CPU频率。通过使用 dev_pm_qos_add_notifier 函数并将通知块注册到 DEV_PM_QOS_MIN_FREQUENCY 类型的通知链中,该模块可以在设备频率变化时及时获得通知,并采取适当的措施来调整CPU频率。
drivers/devfreq/devfreq.c
struct devfreq *devfreq_add_device(struct device *dev,
struct devfreq_dev_profile *profile,
const char *governor_name,
void *data)
{
devfreq->nb_min.notifier_call = qos_min_notifier_call;
err = dev_pm_qos_add_notifier(dev, &devfreq->nb_min,
DEV_PM_QOS_MIN_FREQUENCY);
}
dev_pm_qos_add_notifier 函数用于向内核注册通知器,以在与设备的QoS(Quality of Service,服务质量)最小频率相关的目标值更改时接收通知。这个通知器将允许 devfreq 设备在最小频率更改时进行通知和相应调整。
@dev:这是与QoS约束相关的目标设备,通常是某个设备驱动程序或组件。
&devfreq->nb_min:这是通知块的指针,其中包含通知器回调函数等信息。该通知块将被注册到与最小频率相关的通知链中。
DEV_PM_QOS_MIN_FREQUENCY:这是请求类型,指示通知是与设备的最小频率相关联的。它告诉内核该请求与设备频率的最小值相关。
通过调用 dev_pm_qos_add_notifier 函数并传递这些参数,驱动程序可以注册一个通知器,以在设备的最小频率更改时接收通知。这使得 devfreq 设备可以根据需要进行调整,以满足功耗和性能需求。
这个函数的典型示例可能包括一个设备频率管理器,它需要知道与设备的最小频率相关的任何更改,以便根据这些更改来调整设备频率。通过使用 dev_pm_qos_add_notifier 函数并将通知块注册到 DEV_PM_QOS_MIN_FREQUENCY 类型的通知链中,该管理器可以在最小频率更改时接收通知,并采取适当的措施来调整设备的频率。
2.4 向用户空间process提供的,用于提出per-device PM QoS需求的API
通过sysfs文件,kernel允许用户空间程序对某个设备提出QoS需求,这些sysfs文件位于各个设备的sysf目录下,默认情况下,PM QoS framework不会创建这些文件,除非具体设备驱动调用dev_pm_qos_expose_*系列接口,主动创建。具体可参考代码,这里不再详细说明。
3. 实现思路和内部逻辑
和PM QoS class类似,不再描述。