Linux PM QoS framework(2)_PM QoS class

2023-02-23 18:52:45 浏览数 (2)

1. 前言

回顾上一篇文章(Linux PM QoS framework(1)_概述和软件架构),PM QoS framework抽象出4个系统级别的QoS constraint(统称为PM QoS class),分别是cpu&dma latency、network latency、network throughput和memory bandwidth。并提供一系列的接口,动态的搜集、整理系统对这些constraint的需求情况。

2. API汇整

PM QoS class framework提供的API有2类:一类是以函数调用的形式,为kernel space的driver、service等提供的;另一类是以misc设备的形式,为用户空间进程提供的。

2.1 向kernel其它driver提供的,用于提出PM QoS需求的API

代码语言:javascript复制
void pm_qos_add_request(struct pm_qos_request *req, int pm_qos_class,
                        s32 value);
void pm_qos_update_request(struct pm_qos_request *req,
                           s32 new_value);
void pm_qos_update_request_timeout(struct pm_qos_request *req,
                                   s32 new_value, unsigned long timeout_us);
void pm_qos_remove_request(struct pm_qos_request *req);

int pm_qos_request_active(struct pm_qos_request *req);

1)pm_qos_add_request

该接口用于向PM QoS framework添加一个QoS请求,pm_qos_class为QoS请求的类型(kernel称作pm qos class),value为期望的QoS值,不同的class有不同的含义。pm qos class的定义如下:

代码语言:javascript复制
   1: /* include/linux/pm_qos.h */
   2: enum {
   3:         PM_QOS_RESERVED = 0,
   4:         PM_QOS_CPU_DMA_LATENCY,
   5:         PM_QOS_NETWORK_LATENCY,
   6:         PM_QOS_NETWORK_THROUGHPUT,
   7:         PM_QOS_MEMORY_BANDWIDTH,
   8:  
   9:         /* insert new class ID */
  10:         PM_QOS_NUM_CLASSES,
  11: };

PM_QOS_CPU_DMA_LATENCY,CPU和DMA的延迟(单位为us),它的实际意义是,当产生一个事件之后(如一个中断),CPU或DMA的响应延迟。例如有些CPU的串口控制器,只有几个byte的FIFO,当接收数据时,CPU或DMA必须在FIFO填满前,将数据读走,否则就可能丢失数据或者降低数据的传输速率。 后面几个class,不再详细说明。

以PM_QOS_CPU_DMA_LATENCY为例,pm_qos_add_request的逻辑可以总结为:我要求CPU&DMA的latency不大于‘value’个us。

另外,为了便于对已添加请求的维护(修改、移除等),framework会将该请求保存在一个句柄中,就是第一个参数--struct pm_qos_request指针。调用者不需要知道该结构的具体内容,只要定义一个变量,并把指针传给pm_qos_add_request接口,以后使用该指针进行修改、移除等操作。

2)pm_qos_update_request/pm_qos_update_request_timeout

如果应用场景改变(如串口波特率变大,相应的响应延迟就要变小),可以通过该接口更新QoS请求。req为句柄指针,new_value为新的value。

pm_qos_update_request_timeout多了一个timeout参数,用于需要在一段时间(timeout时间)内修改QoS value的场景。framework会在timeout后,自动将QoS value修改为一个默认值(一般为无效值,代表不再对该QoS有要求)

3)pm_qos_remove_request

如果对该pm qos class不再有要求,则可以调用该接口将请求移除。

4)pm_qos_request_active

该接口可以获取某一个QoS请求的active状态。

2.2 向kernel PM有关的service提供的,用于获取、跟踪指定PM QoS需求的API

代码语言:javascript复制
int pm_qos_request(int pm_qos_class);
int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier);
int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier);

每当有新的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_remove_notifier用于删除notifier。

2.3 向per-device PM QoS framework提供,low level的PM QoS操作API

代码语言:javascript复制
int pm_qos_update_target(struct pm_qos_constraints *c, struct plist_node *node,
                         enum pm_qos_req_action action, int value);
bool pm_qos_update_flags(struct pm_qos_flags *pqf,
                         struct pm_qos_flags_request *req,
                         enum pm_qos_req_action action, s32 val);

s32 pm_qos_read_value(struct pm_qos_constraints *c);

QoS class和per-device PM QoS都是基于底层的pm qos constraint封装而来的。对QoS class的使用者而言,可以不用关心这些底层细节。对per-device PM QoS framework而言,则需要利用它们实现自身的功能。

这些接口就是提供给per-device PM QoS framework的low level接口,后面再详细介绍。

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

根据不同的PM QoS class,包括(cpu&dma latency、network latency等等):

代码语言:javascript复制
/dev/cpu_dma_latency
/dev/network_latency
/dev/network_throughput
/dev/memory_bandwidth

打开文件,将会使用默认值向PM QoS framework添加一个QoS请求;关闭文件,会移除相应的请求;写入value,更改请求的值;读取文件,将会获取QoS的极值。

具体和2.1中的各个接口类似,不再详细说明了。

3. 实现思路和内部逻辑

3.1 主要数据结构

1)struct pm_qos_request,pm qos request句柄,用于request的add、update、remove等操作

代码语言:javascript复制
   1: struct pm_qos_request {
   2:         struct plist_node node;
   3:         int pm_qos_class;
   4:         struct delayed_work work; /* for pm_qos_update_request_timeout */
   5: };

node,一个struct plist_node类型的节点,在保存request的value值(node.prio)的同时,可以将request按照一定的顺序,保存在一个全局的链表中;

pm_qos_class,该request对应的qos class,可以为PM_QOS_CPU_DMA_LATENCY、PM_QOS_NETWORK_LATENCY、PM_QOS_NETWORK_THROUGHPUT、PM_QOS_MEMORY_BANDWIDTH中的一种;

一个delay work,用于实现pm_qos_update_request_timeout接口。

struct plist_node是一个按照优先级(prio)降序排列的双向链表(Descending-priority-sorted double-linked),除了常规链表所具备的head和tail之外,有一个prio字段,刚好可以应用在PM QoS class的场景中。

2)struct pm_qos_constraints,pm qos的内部抽象,用于抽象某一特定的PM QoS class

代码语言:javascript复制
   1: struct pm_qos_constraints {
   2:         struct plist_head list;
   3:         s32 target_value;       /* Do not change to 64 bit */
   4:         s32 default_value;
   5:         s32 no_constraint_value;
   6:         enum pm_qos_type type;
   7:         struct blocking_notifier_head *notifiers;
   8: };

list,链表头,所有该class的request,都会挂到该list上; target_value,该constraint的目标值,即可以满足所有该class的request那个value。通常情况下,根据request的类型(enum pm_qos_type),可以是所有request中的最大值,所有request中的最小值,或者所有request的和; default_value,该constraint的默认值,通常为0,表示没有限制(或没有要求); no_constraint_value,当该class的qos不存在请求时,pm_qos_get_value返回的值,通常为默认值,表示没有限制(或没有要求); type,该constraint的类型,具体请参考下面的描述; notifiers,用于constraint value改变时通知其它driver。

enum pm_qos_type包括PM_QOS_MAX、PM_QOS_MIN和PM_QOS_SUM。PM_QOS_MAX表示在所有的request中取最大值,即可满足所有的request,如network_throughput;PM_QOS_MIN表示在所有的request中取最小值,即可满足所有的request,如cpu_dma_latency;PM_QOS_SUM表示在所有的request中取和,才能满足所有的request,如memory_bandwidth。

当调用pm_qos_get_value接口时,framework会更具qos type,从list head中,取最小值、最大值或者所有值的和。

3.2 实现逻辑

QoS class framework为每个class定义了一个全局的struct pm_qos_constraints变量,用于保存所有该class的request。同时为每个class定义一个misc device变量,用于向用户空间提供接口。最终,将这些信息组织在一个内部的数据结构中(struct pm_qos_object),如下(具体内容可参考kernel/power/qos.c,这里不再详细介绍):

代码语言:javascript复制
   1: struct pm_qos_object {
   2:         struct pm_qos_constraints *constraints;
   3:         struct miscdevice pm_qos_power_miscdev;
   4:         char *name;
   5: };
   6:  
   7: ...
   8:  
   9: static struct pm_qos_object *pm_qos_array[] = {
  10:         &null_pm_qos,
  11:         &cpu_dma_pm_qos,
  12:         &network_lat_pm_qos,
  13:         &network_throughput_pm_qos,
  14:         &memory_bandwidth_pm_qos,
  15: };

1)pm_qos_add_request

request add接口会以qos class为index,从qos array中取出constraint指针(pm_qos_array[pm_qos_class]->constraints),并指针和request的value为参数,调用pm_qos_update_target接口。

pm_qos_update_target会将该request添加到constraint的list中,并根据qos type,计算处该class qos的target value。

pm_qos_update_request/pm_qos_update_request_timeout的逻辑类似,不再详细描述

2)pm_qos_request

直接从该class对应的constraint变量中,获取target value(已经在新的request被add之后更新)。

3)misc设备注册

当kernel会在QoS class framework的初始化接口(pm_qos_power_init)中,调用misc_register,将各个class的miscdevice变量,注册到kernel中。misc设备提供了open、release、read、write等接口(pm_qos_power_fops,具体可参考源文件),用于qos的request、update和remove。

0 人点赞