1. 前言
Linux電源管理中,相當(dāng)多的部分是在處理Hibernate、Suspend、Runtime PM等功能。而這些功能都基于一套相似的邏輯,即“Power management interface”。該Interface的代碼實(shí)現(xiàn)于“include/linux/pm.h”、“drivers/base/power/main.c”等文件中。主要功能是:對(duì)下,定義Device PM相關(guān)的回調(diào)函數(shù),讓各個(gè)Driver實(shí)現(xiàn);對(duì)上,實(shí)現(xiàn)統(tǒng)一的PM操作函數(shù),供PM核心邏輯調(diào)用。
因此在對(duì)Hibernate、Suspend、Runtime PM等功能解析之前,有必要先熟悉一下PM Interface,這就是本文的主要目的。
2. device PM callbacks
在一個(gè)系統(tǒng)中,數(shù)量最多的是設(shè)備,耗電最多的也是設(shè)備,因此設(shè)備的電源管理是Linux電源管理的核心內(nèi)容。而設(shè)備電源管理最核心的操作就是:在合適的時(shí)機(jī)(如不再使用,如暫停使用),將設(shè)備置為合理的狀態(tài)(如關(guān)閉,如睡眠)。這就是device PM callbacks的目的:定義一套統(tǒng)一的方式,讓設(shè)備在特定的時(shí)機(jī),步調(diào)一致的進(jìn)入類(lèi)似的狀態(tài)(可以想象一下軍訓(xùn)時(shí)的“一二一”口令)。
在舊版本的內(nèi)核中,這些PM callbacks分布在設(shè)備模型的大型數(shù)據(jù)結(jié)構(gòu)中,如struct bus_type中的suspend、suspend_late、resume、resume_late,如struct device_driver/struct class/struct device_type中的suspend、resume。很顯然這樣不具備良好的封裝特性,因?yàn)殡S著設(shè)備復(fù)雜度的增加,簡(jiǎn)單的suspend、resume已經(jīng)不能滿(mǎn)足電源管理的需求,就需要擴(kuò)充PM callbacks,就會(huì)不可避免的改動(dòng)這些數(shù)據(jù)結(jié)構(gòu)。
于是新版本的內(nèi)核,就將這些Callbacks統(tǒng)一封裝為一個(gè)數(shù)據(jù)結(jié)構(gòu)----struct dev_pm_ops,上層的數(shù)據(jù)結(jié)構(gòu)只需要包含這個(gè)結(jié)構(gòu)即可。這樣如果需要增加或者修改PM callbacks,就不用改動(dòng)上層結(jié)構(gòu)了(這就是軟件設(shè)計(jì)中抽象和封裝的生動(dòng)體現(xiàn),像藝術(shù)一樣優(yōu)雅)。當(dāng)然,內(nèi)核為了兼容舊的設(shè)計(jì),也保留了上述的suspend/resume類(lèi)型的callbacks,只是已不建議使用,本文就不再介紹它們了。
相信每一個(gè)熟悉了舊版本內(nèi)核的Linux工程師,看到struct dev_pm_ops時(shí)都會(huì)虎軀一震,這玩意也太復(fù)雜了吧!不信您請(qǐng)看:
1: /* include/linux/pm.h, line 276 in linux-3.10.29 */
2: struct dev_pm_ops {
3: int (*prepare)(struct device *dev);
4: void (*complete)(struct device *dev);
5: int (*suspend)(struct device *dev);
6: int (*resume)(struct device *dev);
7: int (*freeze)(struct device *dev);
8: int (*thaw)(struct device *dev);
9: int (*poweroff)(struct device *dev);
10: int (*restore)(struct device *dev);
11: int (*suspend_late)(struct device *dev);
12: int (*resume_early)(struct device *dev);
13: int (*freeze_late)(struct device *dev);
14: int (*thaw_early)(struct device *dev);
15: int (*poweroff_late)(struct device *dev);
16: int (*restore_early)(struct device *dev);
17: int (*suspend_noirq)(struct device *dev);
18: int (*resume_noirq)(struct device *dev);
19: int (*freeze_noirq)(struct device *dev);
20: int (*thaw_noirq)(struct device *dev);
21: int (*poweroff_noirq)(struct device *dev);
22: int (*restore_noirq)(struct device *dev);
23: int (*runtime_suspend)(struct device *dev);
24: int (*runtime_resume)(struct device *dev);
25: int (*runtime_idle)(struct device *dev);
26: };
從Linux PM Core的角度來(lái)說(shuō),這些callbacks并不復(fù)雜,因?yàn)镻M Core要做的就是在特定的電源管理階段,調(diào)用相應(yīng)的callbacks,例如在suspend/resume的過(guò)程中,PM Core會(huì)依次調(diào)用“prepare—>suspend—>suspend_late—>suspend_noirq-------wakeup--------->resume_noirq—>resume_early—>resume-->complete”。
但由于這些callbacks需要由具體的設(shè)備Driver實(shí)現(xiàn),這就要求驅(qū)動(dòng)工程師在設(shè)計(jì)每個(gè)Driver時(shí),清晰的知道這些callbacks的使用場(chǎng)景、是否需要實(shí)現(xiàn)、怎么實(shí)現(xiàn),這才是struct dev_pm_ops的復(fù)雜之處。
Linux kernel對(duì)struct dev_pm_ops的注釋已經(jīng)非常詳細(xì)了,但要弄清楚每個(gè)callback的使用場(chǎng)景、背后的思考,并不是一件容易的事情。因此蝸蝸不準(zhǔn)備在本文對(duì)它們進(jìn)行過(guò)多的解釋?zhuān)蛩憬Y(jié)合具體的電源管理行為,基于具體的場(chǎng)景,再進(jìn)行解釋。
3. device PM callbacks在設(shè)備模型中的體現(xiàn)
我們?cè)诮榻B“Linux設(shè)備模型”時(shí),曾多次提及電源管理相關(guān)的內(nèi)容,那時(shí)蝸蝸采取忽略的方式,暫不說(shuō)明。現(xiàn)在是時(shí)候回過(guò)頭再去看看了。
Linux設(shè)備模型中的很多數(shù)據(jù)結(jié)構(gòu),都會(huì)包含struct dev_pm_ops變量,具體如下:
1: struct bus_type {
2: ...
3: const struct dev_pm_ops *pm;
4: ...
5: };
6:
7: struct device_driver {
8: ...
9: const struct dev_pm_ops *pm;
10: ...
11: };
12:
13: struct class {
14: ...
15: const struct dev_pm_ops *pm;
16: ...
17: };
18:
19: struct device_type {
20: ...
21: const struct dev_pm_ops *pm;
22: };
23:
24: struct device {
25: ...
26: struct dev_pm_info power;
27: struct dev_pm_domain *pm_domain;
28: ...
29: };
bus_type、device_driver、class、device_type等結(jié)構(gòu)中的pm指針,比較容易理解,和舊的suspend/resume callbacks類(lèi)似。我們重點(diǎn)關(guān)注一下device結(jié)構(gòu)中的power和pm_domain變量。
◆power變量
power是一個(gè)struct dev_pm_info類(lèi)型的變量,也在“include/linux/pm.h”中定義。從蝸蝸一直工作于的Linux-2.6.23內(nèi)核,到寫(xiě)這篇文章所用的Linux-3.10.29內(nèi)核,這個(gè)數(shù)據(jù)結(jié)構(gòu)可是一路發(fā)展壯大,從那時(shí)的只有4個(gè)字段,到現(xiàn)在有40多個(gè)字段,簡(jiǎn)直是想起來(lái)什么就放什么啊!
power變量主要保存PM相關(guān)的狀態(tài),如當(dāng)前的power_state、是否可以被喚醒、是否已經(jīng)prepare完成、是否已經(jīng)suspend完成等等。由于涉及的內(nèi)容非常多,我們?cè)诰唧w使用的時(shí)候,順便說(shuō)明。
◆pm_domain指針
在當(dāng)前的內(nèi)核中,struct dev_pm_domain結(jié)構(gòu)只包含了一個(gè)struct dev_pm_ops ops。蝸蝸猜測(cè)這是從可擴(kuò)展性方面考慮的,后續(xù)隨著內(nèi)核的進(jìn)化,可能會(huì)在該結(jié)構(gòu)中添加其他內(nèi)容。
所謂的PM Domain(電源域),是針對(duì)“device”來(lái)說(shuō)的。bus_type、device_driver、class、device_type等結(jié)構(gòu),本質(zhì)上代表的是設(shè)備驅(qū)動(dòng),電源管理的操作,由設(shè)備驅(qū)動(dòng)負(fù)責(zé),是理所應(yīng)當(dāng)?shù)摹5趦?nèi)核中,由于各種原因,是允許沒(méi)有driver的device存在的,那么怎么處理這些設(shè)備的電源管理呢?就是通過(guò)設(shè)備的電源域?qū)崿F(xiàn)的。
4. device PM callbacks的操作函數(shù)
內(nèi)核在定義device PM callbacks數(shù)據(jù)結(jié)構(gòu)的同時(shí),為了方便使用該數(shù)據(jù)結(jié)構(gòu),也定義了大量的操作API,這些API分為兩類(lèi)。
◆通用的輔助性質(zhì)的API,直接調(diào)用指定設(shè)備所綁定的driver的、pm指針的、相應(yīng)的callback,如下
1: extern int pm_generic_prepare(struct device *dev);
2: extern int pm_generic_suspend_late(struct device *dev);
3: extern int pm_generic_suspend_noirq(struct device *dev);
4: extern int pm_generic_suspend(struct device *dev);
5: extern int pm_generic_resume_early(struct device *dev);
6: extern int pm_generic_resume_noirq(struct device *dev);
7: extern int pm_generic_resume(struct device *dev);
8: extern int pm_generic_freeze_noirq(struct device *dev);
9: extern int pm_generic_freeze_late(struct device *dev);
10: extern int pm_generic_freeze(struct device *dev);
11: extern int pm_generic_thaw_noirq(struct device *dev);
12: extern int pm_generic_thaw_early(struct device *dev);
13: extern int pm_generic_thaw(struct device *dev);
14: extern int pm_generic_restore_noirq(struct device *dev);
15: extern int pm_generic_restore_early(struct device *dev);
16: extern int pm_generic_restore(struct device *dev);
17: extern int pm_generic_poweroff_noirq(struct device *dev);
18: extern int pm_generic_poweroff_late(struct device *dev);
19: extern int pm_generic_poweroff(struct device *dev);
20: extern void pm_generic_complete(struct device *dev);
以pm_generic_prepare為例,就是查看dev->driver->pm->prepare接口是否存在,如果存在,直接調(diào)用并返回結(jié)果。
◆和整體電源管理行為相關(guān)的API,目的是將各個(gè)獨(dú)立的電源管理行為組合起來(lái),組成一個(gè)較為簡(jiǎn)單的功能,如下
1: #ifdef CONFIG_PM_SLEEP
2: extern void device_pm_lock(void);
3: extern void dpm_resume_start(pm_message_t state);
4: extern void dpm_resume_end(pm_message_t state);
5: extern void dpm_resume(pm_message_t state);
6: extern void dpm_complete(pm_message_t state);
7:
8: extern void device_pm_unlock(void);
9: extern int dpm_suspend_end(pm_message_t state);
10: extern int dpm_suspend_start(pm_message_t state);
11: extern int dpm_suspend(pm_message_t state);
12: extern int dpm_prepare(pm_message_t state);
13:
14: extern void __suspend_report_result(const char *function, void *fn, int ret);
15:
16: #define suspend_report_result(fn, ret)
17: do {
18: __suspend_report_result(__func__, fn, ret);
19: } while (0)
20:
21: extern int device_pm_wait_for_dev(struct device *sub, struct device *dev);
22: extern void dpm_for_each_dev(void *data, void (*fn)(struct device *, void *));
這些API的功能和動(dòng)作解析如下。
dpm_prepare,執(zhí)行所有設(shè)備的“->prepare() callback(s)”,內(nèi)部動(dòng)作為:
1)遍歷dpm_list,依次取出掛在該list中的device指針。?
?? 【注1:設(shè)備模型在添加設(shè)備(device_add)時(shí),會(huì)調(diào)用device_pm_add接口,將該設(shè)備添加到全局鏈表dpm_list中,以方便后續(xù)的遍歷操作。】
2)調(diào)用內(nèi)部接口device_prepare,執(zhí)行實(shí)際的prepare動(dòng)作。該接口會(huì)返回執(zhí)行的結(jié)果。
3)如果執(zhí)行失敗,打印錯(cuò)誤信息。
4)如果執(zhí)行成功,將dev->power.is_prepared(就是上面我們提到的struct dev_pm_info類(lèi)型的變量)設(shè)為T(mén)RUE,表示設(shè)備已經(jīng)prepared了。同時(shí),將該設(shè)備添加到dpm_prepared_list中(該鏈表保存了所有已經(jīng)處于prepared狀態(tài)的設(shè)備)。
內(nèi)部接口device_prepare的執(zhí)行動(dòng)作為:
1)根據(jù)dev->power.syscore,斷該設(shè)備是否為syscore設(shè)備。如果是,則直接返回(因?yàn)閟yscore設(shè)備會(huì)單獨(dú)處理)。
2)在prepare時(shí)期,調(diào)用pm_runtime_get_noresume接口,關(guān)閉Runtime suspend功能。以避免由Runtime suspend造成的不能正常喚醒的Issue。該功能會(huì)在complete時(shí)被重新開(kāi)啟。?
?? 【注2:pm_runtime_get_noresume的實(shí)現(xiàn)很簡(jiǎn)單,就是增加該設(shè)備power變量的引用計(jì)數(shù)(dev->power.usage_count),Runtime PM會(huì)根據(jù)該計(jì)數(shù)是否大于零,判斷是否開(kāi)啟Runtime PM功能。】
3)調(diào)用device_may_wakeup接口,根據(jù)當(dāng)前設(shè)備是否有wakeup source(dev->power.wakeup)以及是否允許wakeup(dev->power.can_wakeup),判定該設(shè)備是否是一個(gè)wakeup path(記錄在dev->power.wakeup_path中)。?
??? 【注3:設(shè)備的wake up功能,是指系統(tǒng)在低功耗狀態(tài)下(如suspend、hibernate),某些設(shè)備具備喚醒系統(tǒng)的功能。這是電源管理過(guò)程的一部分。】
4)根據(jù)優(yōu)先順序,獲得用于prepare的callback函數(shù)。由于設(shè)備模型有bus、driver、device等多個(gè)層級(jí),而prepare接口可能由任意一個(gè)層級(jí)實(shí)現(xiàn)。這里的優(yōu)先順序是指,只要優(yōu)先級(jí)高的層級(jí)注冊(cè)了prepare,就會(huì)優(yōu)先使用它,而不會(huì)使用優(yōu)先級(jí)低的prepare。優(yōu)先順序?yàn)椋篸ev->pm_domain->ops、dev->type->pm、dev->class->pm、dev->bus->pm、dev->driver->pm(這個(gè)優(yōu)先順序同樣適用于其它c(diǎn)allbacks)。?
5)如果得到有限的prepare函數(shù),調(diào)用并返回結(jié)果。?
dpm_suspend,執(zhí)行所有設(shè)備的“->suspend() callback(s)”,其內(nèi)部動(dòng)作和dpm_prepare類(lèi)似:
1)遍歷dpm_list,依次取出掛在該list中的device指針。?
2)調(diào)用內(nèi)部接口device_suspend,執(zhí)行實(shí)際的prepare動(dòng)作。該接口會(huì)返回執(zhí)行的結(jié)果。
3)如果suspend失敗,將該設(shè)備的信息記錄在一個(gè)struct suspend_stats類(lèi)型的數(shù)組中,并打印錯(cuò)誤錯(cuò)誤信息。
4)最后將設(shè)備從其它鏈表(如dpm_prepared_list),轉(zhuǎn)移到dpm_suspended_list鏈表中。
內(nèi)部接口device_suspend的動(dòng)作和device_prepare類(lèi)似,這里不再描述了。
dpm_suspend_start,依次執(zhí)行dpm_prepare和dpm_suspend兩個(gè)動(dòng)作。
dpm_suspend_end,依次執(zhí)行所有設(shè)備的“->suspend_late() callback(s)”以及所有設(shè)備的“->suspend_noirq() callback(s)”。動(dòng)作和上面描述的類(lèi)似,這里不再說(shuō)明了。
dpm_resume、dpm_complete、dpm_resume_start、dpm_resume_end,是電源管理過(guò)程的喚醒動(dòng)作,和dpm_suspend_xxx系列的接口類(lèi)似。不再說(shuō)明了。
?
評(píng)論
查看更多