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
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的定義如下:
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
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
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等等):
/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等操作
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
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,這裏不再詳細介紹):
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。