本文介紹Linux運行時(shí)I/O設備的電源管理框架。屬于Linux內核文檔的翻譯。
原文:http://www.kernel.org/doc/Documentation/power/runtime_pm.txt
翻譯:CoryXie <wenxue.xie@windriver.com>
對I/O設備的運行時(shí)電源管理(運行時(shí)PM)的支持,是在電源管理的核心(PM core)下借助于以下方式實(shí)現的:
下面描述在“struct dev_pm_ops” 中存在的運行時(shí)PM回調函數,設備運行時(shí)PM字段“struct dev_pm_info”,以及運行時(shí)PM核心輔助函數。
在“struct dev_pm_ops”中有三個(gè)設備運行時(shí)PM回調函數:
struct dev_pm_ops {...int (*runtime_suspend)(struct device *dev);int (*runtime_resume)(struct device *dev);int (*runtime_idle)(struct device *dev);...};
->runtime_suspend(), -> runtime_resume()和 ->runtime_idle()回調函數會(huì )被PM核心針對下列類(lèi)型執行:
這就允許設備類(lèi)型覆蓋總線(xiàn)類(lèi)型或類(lèi)所提供的回調函數,如果有必要的話(huà)。
下面的文檔中,總線(xiàn)類(lèi)型,設備類(lèi)型和類(lèi)的回調函數都被稱(chēng)為子系統級的回調函數(subsystem-level callbacks)。
默認情況下,回調函數是在進(jìn)程上下文中,允許中斷的情況下被調用的。然而,子系統可以使用pm_runtime_irq_safe()輔助函數告訴PM核心,設備的 -> runtime_suspend()和 -> runtime_resume()回調函數應該在禁止中斷的原子上下文中被調用(-> runtime_idle()仍然使用默認的方式調用)。這意味著(zhù),這些回調例程不得block 或者sleep;但同時(shí)也意味著(zhù)在第4節末尾列出的同步輔助函數(synchronous helper functions),可以在中斷處理程序或原子上下文中被使用。
子系統的掛起回調函數(suspend callback)_完全_負責_恰當地處理設備的掛起。它可以(但不是必須)包括執行自己的設備驅動(dòng)程序的->runtime_suspend()回調(從PM核心的角度看,并不是必須要設備驅動(dòng)實(shí)現 ->runtime_suspend()回調函數,只要子系統級的掛起回調函數知道怎么去處理設備就行)。
特別的,如果為了能夠適當地工作,驅動(dòng)程序需要遠程喚醒功能(即,設備請求使其電源狀態(tài)變化的硬件機制,如PCI PME), 而device_run_wake()返回“false”的設備,則->runtime_suspend()應該返回-EBUSY。另一方面,對于device_run_wake()返回“true”的設備,且在子系統級掛起回調的執行過(guò)程中該設備進(jìn)入了低功耗狀態(tài),我們期望設備的遠程喚醒就已經(jīng)被啟動(dòng)。一般情況下,所有在運行時(shí)被設置進(jìn)入低功耗狀態(tài)的輸入設備應該啟用遠程喚醒。
子系統級的恢復回調函數(resume callback)要_完全_負責_處理設備的恢復,這可能(但不一定)包括執行自己的設備驅動(dòng)程序的 ->runtime_resume()回調(從PM核心的角度看,并不是必須要在設備驅動(dòng)程序中實(shí)現->runtime_resume()回調函數,只要子系統級的恢復回調知道怎樣能處理設備就行)。 每當設備看起來(lái)空閑的時(shí)候【這是通過(guò)兩個(gè)計數器來(lái)向PM核心指示的,設備使用計數(usage counter),以及設備的“活躍子設備”(active children)計數】,子系統級的空閑回調函數(idle callback)就會(huì )被PM核心執行。
子系統級的空閑回調(idle callback)執行的操作是完全依賴(lài)于子系統本身的,但期望和建議的操作是,檢查設備是否可以?huà)炱穑磼炱鹪撛O備的所有必要條件是否滿(mǎn)足),且在這種情況下,為該設備排隊一個(gè)掛起請求(queue up a suspend request)。這個(gè)回調函數返回的值將被PM核心忽略。
在第4節所述的PM核心所提供的輔助函數,保證對總線(xiàn)類(lèi)型的運行時(shí)PM回調滿(mǎn)足以下約束:
- 回調是互斥的(例如,對于同一個(gè)設備,禁止并行執行->runtime_suspend()和->runtime_resume(),或另一個(gè)->runtime_suspend()的實(shí)例);例外的情形是,-> runtime_suspend()或 -> runtime_resume()可以和-> runtime_idle()并行執行(雖然對同一設備,-> runtime_idle()將不會(huì )在任何其他回調正在執行時(shí)啟動(dòng))。
- -> runtime_idle() 和-> runtime_suspend()只能對 “活躍的(active)”設備執行(即PM核心只會(huì )對運行時(shí)PM狀態(tài)是“活躍的(active)” 的設備執行 ->runtime_idle() 和-> runtime_suspend())。
- ->runtime_idle()和->runtime_suspend()只能對其使用計數(usage counter)是零,且其“活躍子設備”(‘a(chǎn)ctive' children)個(gè)數是零或“power.ignore_children”標志被設置的設備執行。
- ->runtime_resume()只能對“掛起(suspended)”狀態(tài)的設備執行(即PM核心只會(huì )對運行時(shí)PM狀態(tài)是“掛起(suspended)”的設備執行->runtime_resume())。
此外,由PM核心提供的輔助函數遵循以下規則:
執行->runtime_resume()請求,將取消任何對同一設備的等待中的(pending)或已被調度的(scheduled)回調執行請求,除了已被調度的自動(dòng)掛起(autosuspend)。
以下是在'struct dev_pm_info'中的設備的運行時(shí)PM字段,定義在include / linux/ pm.h:
用于調度(延遲的)掛起和自動(dòng)休眠(suspend and autosuspend)請求的定時(shí)器。
定時(shí)器到期時(shí)間,單位是jiffies(如果這異于零,則定時(shí)器正在運行,并將于該時(shí)間到期;否則定時(shí)器未運行)。
用于請求排隊的工作結構(即pm_wq中工作項)。
等待隊列,當任何輔助函數需要等待另一個(gè)完成的時(shí)候使用。
用于同步。
設備的使用計數。
“活躍的(active)”的子設備的個(gè)數。
如果置位,child_count的值將被忽略(但仍然要被更新)
用于禁用輔助函數(如果該值等于零,它們正常工作),它的初始值是1(即運行時(shí)PM最初對所有設備都是禁用的)
如果該值被設置,就表明有致命錯誤(在第2節中所述的回調函數返回的錯誤代碼之一),因此輔助函數直到這個(gè)標志被清除之前將無(wú)法正常工作,這是失敗的回調函數返回的錯誤代碼。
如果該值被設置,則->runtime_idle()正在被執行。
如果該值被設置,則有掛起的請求(即有工作項被排隊在pm_wq中)
掛起的請求類(lèi)型(如果request_pending被設置時(shí)有效)。
當設備正在執行-> runtime_suspend()的時(shí)候,如果->runtime_resume()將要運行,而等待掛起操作完成并不實(shí)際,就會(huì )設置該值;這里的意思是“一旦你掛起完成,我就開(kāi)始恢復”。
如果設備能夠生成運行時(shí)喚醒事件,該值就被設置。
設備的運行時(shí)PM狀態(tài); 此字段的初始值是RPM_SUSPENDED,這意味著(zhù)PM核心認為每個(gè)設備最初都處于'掛起',不論其實(shí)際的硬件狀態(tài)如何。
表示該設備不使用運行時(shí)PM回調(參見(jiàn)第8節),它只可能會(huì )被輔助函數pm_runtime_no_callbacks()修改。
表示->runtime_suspend()和->runtime_resume()回調函數將在持有自旋鎖并禁止中斷的情況下被調用。
表明該設備的驅動(dòng)程序支持延遲的自動(dòng)休眠功能(見(jiàn)第9節),它只可能被輔助函數pm_runtime{_dont}_use_autosuspend()修改。
表明PM核心應該在定時(shí)器到期時(shí)嘗試進(jìn)行自動(dòng)休眠(autosuspend),而不是一個(gè)常規的掛起(normal suspend)。
延遲時(shí)間(以毫秒為單位),可用于自動(dòng)休眠功能。
所有上述字段都是“structdevice”的成員“power”中的成員。
以下的運行時(shí)PM輔助函數被定義在drivers/base/power/runtime.c以及 include/linux/pm_runtime.h中:
初始化dev_pm_info結構中的設備運行時(shí)PM字段。
確保設備的運行時(shí)PM在該設備從設備層次刪除后將被禁用。
執行子系統級的設備空閑回調,返回0成功,或失敗的錯誤代碼,其中的-EINPROGRESS 表示->runtime_idle()已經(jīng)在執行。
對設備執行子系統級的掛起回調;返回0表示成功;如果設備的運行時(shí)PM狀態(tài)已經(jīng)是“掛起”則返回1;或失敗時(shí)返回錯誤代碼,其中,-EAGAIN或-EBUSY意味著(zhù)企圖在未來(lái)再次掛起設備是安全的。
與pm_runtime_suspend()相同,除了考慮了自動(dòng)休眠延遲時(shí)間;如果pm_runtime_autosuspend_expiration()說(shuō)該延遲尚未到期,那么就會(huì )調度適當時(shí)間的自動(dòng)休眠功能,并返回0。
對設備執行子系統級的恢復回調;返回0表示成功;如果設備的運行時(shí)PM狀態(tài)已經(jīng)是“活躍的(active)”就返回1;或失敗時(shí)錯誤代碼,其中-EAGAIN意味著(zhù)在未來(lái)試圖恢復設備可能是安全的;但應附加對‘power.runtime_error’進(jìn)行檢查。
對設備提交一個(gè)執行子系統級的空閑回調的請求(請求由一個(gè)pm_wq的工作項代表);返回0表示成功,或如果請求沒(méi)有排隊成功就返回錯誤代碼。
調度子系統級的掛起回調函數,使其在設備的自動(dòng)休眠延遲(autosuspend delay)過(guò)期時(shí)執行;如果延遲已過(guò)期,則工作項立即被排隊。
調度在未來(lái)執行設備的子系統級的掛起回調,其中“delay”是在pm_wq上排隊掛起回調工作項之前等待的時(shí)間,以毫秒為單位(如果“delay”是零,工作項馬上進(jìn)行排隊);返回0表示成功;如果該設備的運行時(shí)PM狀態(tài)已經(jīng)是“掛起”時(shí)返回1;或在當請求沒(méi)有被調度(或 “delay”為0時(shí)被排隊)時(shí)返回錯誤代碼;如果->runtime_suspend()的執行已經(jīng)被調度但尚未到期,則“delay”的新值將被用來(lái)作為等待的時(shí)間。
對設備提交一個(gè)執行子系統級恢復回調的請求(該請求由一個(gè)pm_wq中的工作項代表);成功返回0;如果設備的運行時(shí)PM狀態(tài)已經(jīng)是”活躍的(active)“則返回1;或當請求沒(méi)有被排上隊時(shí)返回錯誤代碼。
遞增設備的使用計數。
遞增設備的使用計數,運行pm_request_resume(dev),并返回其結果。
遞增設備的使用計數,運行pm_runtime_resume(dev),并返回其結果。
遞減設備的使用計數。
設備的使用計數減1,如果結果是0,則運行pm_request_idle(dev)并返回其結果。
設備的使用計數減1,如果結果是0,則運行pm_request_autosuspend(dev)并返回其結果。
設備的使用計數減1,如果結果是0,則運行pm_runtime_idle(dev)并返回其結果。
設備的使用計數減1,如果結果是0,則運行pm_runtime_suspend(dev)并返回其結果。
設備的使用計數減1,如果結果是0,則運行pm_runtime_autosuspend(dev)并返回其結果。
使能運行時(shí)PM的輔助函數,使其能運行第2節中所描述的設備的總線(xiàn)類(lèi)型的運行時(shí)PM回調。
防止運行時(shí)PM輔助函數運行設備的子系統級的運行時(shí)PM回調,確保設備的所有等待中的運行時(shí)PM操作已完成或取消;如果有一個(gè)恢復請求正在等待,且為了滿(mǎn)足該請求而執行設備的子系統級的恢復回調是必要的,則返回1;否則返回0。
設置/取消設備的power.ignore_children標志。
清除設備的“power.runtime_error”標志,設置設備的運行時(shí)PM狀態(tài)為”活躍的(active)“,并更新其父設備的”活躍子設備“計數(唯一有效的使用此函數的條件是,如果“power.runtime_error”被設置,或者“power.disable_depth”大于零);如果設備的父設備是不活躍的,且其“power.ignore_children”標志沒(méi)有設置,該函數就會(huì )失敗并返回錯誤代碼。
清除設備的“power.runtime_error”標志,設置設備的運行時(shí)PM狀態(tài)為“掛起”,并恰當更新其父設備的“活躍的子設備”計數(此函數唯一有效的使用條件是,如果“power.runtime_error”被設置,或“power.disable_depth”大于零)。
如果該設備的運行時(shí)PM狀態(tài)為“掛起”且其“power.disable_depth”字段等于0,返回true;否則返回false。
設置設備的power.runtime_auto標志,并遞減其使用計數(用于/sys/devices/.../power/control接口,實(shí)際上允許使設備在運行時(shí)被電源管理)。
取消設置設備的power.runtime_auto標志,并遞增其使用計數(用于/sys/devices/.../power/control接口,實(shí)際上禁止設備在運行時(shí)被電源管理)。
設置設備的power.no_callbacks標志,并從/sys/devices/.../power中刪除運行時(shí)PM屬性(或防止設備在注冊時(shí)添加他們)。
設置設備的power.irq_safe標志,造成運行時(shí)PM掛起和恢復回調在禁止中斷的情況下被調用(但不包括空閑回調)。
設置power.last_busy字段為當前時(shí)間。
設置power.use_autosuspend標志,使能自動(dòng)休眠延遲。
清除power.use_autosuspend標志,禁用自動(dòng)休眠延遲。
設置power.autosuspend_delay的值為“delay”(以毫秒為單位),如果“delay”是負的,則防止運行時(shí)掛起。
基于power.last_busy和power.autosuspend_delay計算當前自動(dòng)休眠延遲的到期時(shí)間;如果延遲時(shí)間是1000毫秒或更大,則到期時(shí)間四舍五入精確到秒(rounded up);如果延遲時(shí)間已經(jīng)過(guò)期或power.use_autosuspend沒(méi)有設置,則返回0;否則返回以jiffies計的過(guò)期時(shí)間。
從中斷上下文中執行以下輔助函數是安全的:
如果pm_runtime_irq_safe()為設備調用,則以下輔助函數也可以在中斷上下文中使用:
最初,所有設備的運行時(shí)PM被禁用,這意味著(zhù)第4節中描述的大部分的運行時(shí)PM輔助函數將返回-EAGAIN,直到為設備調用pm_runtime_enable()之后。
此外,所有設備的運行時(shí)PM的初始狀態(tài)都是'掛起(suspended)',但它不一定反映實(shí)際的物理設備狀態(tài)。因此,如果設備最初是活躍的(即,它能夠處理I/O),其運行時(shí)PM狀態(tài)必須在pm_runtime_set_active()的幫助之下,在為設備調用pm_runtime_enable()之前,被改變?yōu)椤盎钴S”。
然而,如果該設備有父設備且其父設備的運行時(shí)PM是啟用的,為設備調用pm_runtime_set_active()會(huì )影響其父設備,除非其父設備的“power.ignore_children”標志位被設置。也就是說(shuō),在這種情況下,使用PM核心的輔助函數,父設備不能在運行時(shí)被掛起,只要子設備的狀態(tài)是“活躍的”,即使子設備的運行時(shí)PM還是禁用的(即pm_runtime_enable ()尚未對該子設備調用,或對該子設備已調用pm_runtime_disable())。出于這個(gè)原因,一旦pm_runtime_set_active()被為設備調用,pm_runtime_enable()也應該被盡早調用;否則其運行時(shí)PM狀態(tài)應該在pm_runtime_set_suspended()的幫助下改回為“掛起”。
如果設備的默認初始運行時(shí)PM狀態(tài)(即“掛起”)反映了實(shí)際設備狀態(tài),它的總線(xiàn)類(lèi)型或它的驅動(dòng)程序的->probe()回調函數將可能需要使用在第4節描述的PM核心的輔助函數喚醒它。在這種情況下,應使用pm_runtime_resume()。當然,為達此目的,在此之前,設備的運行時(shí)PM應通過(guò)調用pm_runtime_enable()被啟動(dòng)。
如果設備的總線(xiàn)類(lèi)型或驅動(dòng)程序的->probe()回調運行pm_runtime_suspend()或pm_runtime_idle()或與之對應的異步函數(asynchronous counterparts),他們將失敗返回-EAGAIN,因為該設備的使用計數已經(jīng)被驅動(dòng)程序核心遞增,然后再執行->probe()。盡管如此,可能仍然比較想要設備在->probe()完成后盡快被掛起,所以驅動(dòng)那時(shí)候會(huì )核心采用pm_runtime_put_sync()來(lái)調用子系統級的設備空閑回調。
此外,在__device_release_driver()中,驅動(dòng)核心可以防止運行時(shí)PM回調與總線(xiàn)通知(notifier)回調競爭,這是必要的,因為一些子系統使用通知(notifier)來(lái)進(jìn)行影響運行時(shí)PM的操作。這是通過(guò)在driver_sysfs_remove()和BUS_NOTIFY_UNBIND_DRIVER通知之前調用pm_runtime_get_sync()來(lái)實(shí)現該目的的。如果設備已經(jīng)處于掛起狀態(tài),這將恢復該設備,并會(huì )防止在這些例程正在執行時(shí)再次被掛起。
為了讓總線(xiàn)類(lèi)型和驅動(dòng)程序在其->remove()例程中調用pm_runtime_suspend()將設備放到掛起狀態(tài),在__ ??device_release_driver()中驅動(dòng)程序核心在運行BUS_NOTIFY_UNBIND_DRIVER通知后執行pm_runtime_put_sync()。這就需要總線(xiàn)類(lèi)型和驅動(dòng)程序避免其->remove()回調函數與運行時(shí)PM直接競爭,但也讓驅動(dòng)程序在處理設備的移除過(guò)程中有更多的靈活性。
通過(guò)將/sys/devices/.../power/control屬性值改變?yōu)椤皁n”,用戶(hù)空間可以有效地禁止設備驅動(dòng)程序進(jìn)行運行時(shí)電源管理,這會(huì )導致pm_runtime_forbid()被調用。原則上,也可以使用這個(gè)機制有效地關(guān)閉運行時(shí)設備電源管理,直到用戶(hù)空間打開(kāi)它。也就是說(shuō),在初始化時(shí),驅動(dòng)程序可以確保設備的運行時(shí)PM狀態(tài)是“活躍的(active)”,并調用pm_runtime_forbid()。應該指出的是,如果用戶(hù)空間已經(jīng)有意改變/sys/devices/.../power/control 的值為“自動(dòng)”,讓驅動(dòng)在運行時(shí)進(jìn)行設備的電源管理,驅動(dòng)程序這樣用pm_runtime_forbid()可能會(huì )讓用戶(hù)空間產(chǎn)生混淆。
運行時(shí)PM和系統休眠(即,系統掛起和休眠,也被稱(chēng)為掛起到RAM和掛起到磁盤(pán))以多種方式互相交互。如果系統休眠開(kāi)始時(shí)設備處于活躍狀態(tài),那么一切都簡(jiǎn)單。但如果設備已掛起,會(huì )發(fā)生什么呢?
對于運行時(shí)PM和系統休眠,設備可能有不同的喚醒設置。例如,遠程喚醒可能會(huì )在運行時(shí)PM中啟用,但不允許系統休眠時(shí)啟用(device_may_wakeup(dev)返回“false”)。當發(fā)生這種情況時(shí),子系統級系統掛起回調(system suspend callback)負責改變設備的喚醒設定(它可能將這個(gè)責任交給設備驅動(dòng)器的系統掛起例程)。為了做到這一點(diǎn),可能需要先恢復設備,再掛起它。如果驅動(dòng)程序對運行時(shí)掛起和系統休眠使用不同的電源級別或其他設置,也是如此。
在系統恢復時(shí),設備一般應恢復到全功率狀態(tài),即使他們在系統休眠開(kāi)始前已經(jīng)被掛起。這有幾個(gè)原因,包括:
如果系統睡眠開(kāi)始前設備已經(jīng)被掛起,那么它的運行時(shí)PM狀態(tài)將必須被更新,以反映實(shí)際的系統睡眠后的狀態(tài)。做到這一點(diǎn)的方法是:
子系統可能希望通過(guò)使用PM核心提供的一套通用的,定義在driver/base/power/generic_ops.c中的電源管理回調函數,以節省代碼空間:
調用此設備的驅動(dòng)程序提供的->runtime_idle()回調函數(如果有定義的話(huà)),并在該回調返回值是0或者回調沒(méi)有定義的情況下,調用pm_runtime_suspend()。
調用此設備的驅動(dòng)程序提供的 ->runtime_suspend()回調函數,并返回其結果,或如果該回調函數沒(méi)有定義時(shí)返回-EINVAL。
調用此設備的驅動(dòng)程序提供的->runtime_resume()回調函數,并返回其結果,或如果該回調函數沒(méi)有定義時(shí)返回-EINVAL。
如果該設備未在運行時(shí)被掛起,調用此設備的驅動(dòng)程序提供的->suspend()回調函數,并 返回其結果,或如果該回調函數沒(méi)有定義時(shí)返回-EINVAL。
調用此設備的驅動(dòng)程序提供的->resume()回調函數,且如果成功的話(huà),改變設備的運行時(shí)PM狀態(tài)為“活躍的”。
如果該設備未在運行時(shí)被掛起,調用此設備的驅動(dòng)程序提供的-> freeze ()回調函數,并 返回其結果,或如果該回調函數沒(méi)有定義時(shí)返回-EINVAL。
如果該設備未在運行時(shí)被掛起,調用此設備的驅動(dòng)程序提供的-> thaw ()回調函數,并 返回其結果,或如果該回調函數沒(méi)有定義時(shí)返回-EINVAL。
如果該設備未在運行時(shí)被掛起,調用此設備的驅動(dòng)程序提供的-> poweroff ()回調函數,并返回其結果,或如果該回調函數沒(méi)有定義時(shí)返回-EINVAL。
調用此設備的驅動(dòng)程序提供的-> restore()回調函數,且如果成功的話(huà),改變設備的運行時(shí)PM狀態(tài)為“活躍的”。
這些函數可以被賦值給系統級dev_pm_ops結構體的下列回調函數指針:
如果子系統希望同時(shí)使用所有的這些函數,可以簡(jiǎn)單地將GENERIC_SUBSYS_PM_OPS宏(定義在include/linux/pm.h)賦值給其dev_pm_ops結構的指針。
希望使用相同的函數作為系統掛起(system suspend), 凍結(freeze),斷電(poweroff)以及運行時(shí)掛起(run-time suspend),以及類(lèi)似的,系統恢復(system resume),解凍(thaw),恢復(restore)和運行時(shí)恢復(run-timeresume)等回調函數的設備驅動(dòng)程序,可以在定義在include/linux/pm.h中的UNIVERSAL_DEV_PM_OPS宏的幫助下做到這一點(diǎn)(可能是其最后一個(gè)參數設置為NULL)。
改變設備的電源狀態(tài)并不是免費的,它也需要時(shí)間和能耗。只有當有理由認為設備將保持在這種狀態(tài)下大量的時(shí)間時(shí),才應將設備置入低功耗狀態(tài)。一個(gè)通常的啟發(fā)式說(shuō)法,一直沒(méi)有怎么用的設備很可能繼續保持在未使用狀態(tài),按照這個(gè)建議,驅動(dòng)程序不應該允許設備在運行時(shí)掛起,直到他們處于非活躍狀態(tài)已經(jīng)有一段最低限度的時(shí)間。即使該啟發(fā)式說(shuō)法最終并非最佳,它仍然會(huì )阻止設備在低功耗和全功率狀態(tài)之間迅速“反彈”。
術(shù)語(yǔ)“自動(dòng)休眠(autosuspend)”是一個(gè)歷史遺留下來(lái)的名字。這并不意味著(zhù)該設備就會(huì )自動(dòng)掛起(子系統或驅動(dòng)程序仍然需要調用適當的PM例程),然而這意味著(zhù)運行時(shí)掛起(run-time suspends)將自動(dòng)被延遲,直到所需的一段時(shí)間空閑后。
不活躍(Inactivity)是根據power.last_busy字段來(lái)確定的。驅動(dòng)程序應該在進(jìn)行I/O后調用pm_runtime_mark_last_busy()來(lái)更新這個(gè)字段,通常是在剛要調用pm_runtime_put_autosuspend()之前。所需的空閑時(shí)間長(cháng)度是一個(gè)策略問(wèn)題。子系統可以在最初調用pm_runtime_set_autosuspend_delay()設置該長(cháng)度,但設備注冊后該長(cháng)度應由用戶(hù)空間控制,使用/sys/devices/.../power/autosuspend_delay_ms屬性。
為了使用自動(dòng)休眠(autosuspend),子系統或驅動(dòng)程序必須調用pm_runtime_use_autosuspend()(最好是在注冊設備之前),此后他們應該使用各種*_autosuspend()輔助函數,來(lái)代替非自動(dòng)休眠的對應函數(non-autosuspend counterparts):
驅動(dòng)程序可以繼續使用非自動(dòng)休眠功能輔助函數,他們會(huì )表現正常,而不把自動(dòng)休眠延遲考慮進(jìn)來(lái)。同樣,如果power.use_autosuspend字段沒(méi)有被設置,則自動(dòng)休眠的輔助函數,使用起來(lái)就像是非自動(dòng)休眠的對應函數(non-autosuspend counterparts)。
該實(shí)現非常適合用于異步中斷上下文中。然而,這樣的使用不可避免地涉及到競爭,這是由于PM核心不能同步 ->runtime_suspend()回調與I/O請求的到來(lái)。該同步必須由驅動(dòng)程序使用其私有鎖來(lái)完成。這里是一個(gè)原理性的偽代碼示例:
最重要的一點(diǎn)是,在foo_io_completion()要求自動(dòng)休眠之后,foo_runtime_suspend()回調可能與foo_read_or_write()競爭。因此foo_runtime_suspend()必須檢查是否有任何掛起的I/O請求(在持有私有鎖的情況下),然后才允許掛起進(jìn)行。
此外,power.autosuspend_delay字段可以由用戶(hù)空間在任何時(shí)間改變。如果驅動(dòng)程序關(guān)心這個(gè),它可以在持有其私有鎖的情況下在->runtime_suspend()回調內調用pm_runtime_autosuspend_expiration()。如果該函數返回非零值,那么該延誤尚未過(guò)期,則該回調應該返回-EAGAIN。
聯(lián)系客服