淺述內核中「掛起到空閑」的實現
這篇博文主要介紹 掛起到空閑 的實現。如上所說,它主要通過軟體實現。一般平台的掛起過程包括凍結用戶空間並將外圍設備調至低耗電模式。但是,系統並不是直接關閉和熱插拔掉 CPU,而是靜靜地強制將 CPU 進入 空閑 狀態。隨著外圍設備進入了低耗電模式,除了喚醒相關的中斷外不應有其他中斷產生。喚醒中斷包括那些設置用於喚醒系統的計時器(比如 RTC,普通計時器等)、或者電源開關、USB 和其它外圍設備等。
在凍結過程中,當系統進入空閑狀態時會調用一個特殊的 cpu 空閑函數。這個 enter_freeze()
函數可以和調用使 cpu 空閑的 enter()
函數一樣簡單,也可以複雜得多。該函數複雜的程度由將 SoC 置為低耗電模式的條件和方法決定。
先決條件
platform_suspend_ops
一般情況,為了支持 S2I,系統必須實現 platform_suspend_ops
並提供最低限度的掛起支持。這意味著至少要完成 platform_suspend_ops
中的 valid()
函數。如果 掛起到空閑 和 掛起到內存 都要支持,valid 函數中應使用 suspend_valid_only_mem
。
不過,最近內核增加了對 S2I 的自動支持。Sudeep Holla 提出了一個變更,可以讓系統不需要滿足 platform_suspend_ops
條件也能提供 S2I 支持。這個補丁已經被接收並將合併在 4.9 版本中,該補丁可從這裡獲取: https://lkml.org/lkml/2016/8/19/474。
如果定義了 suspend_ops
,那麼可以通過查看 /sys/power/state
文件得知系統具體支持哪些掛起狀態。如下操作:
# cat /sys/power/state
freeze mem
這個示例的結果顯示該平台支持 S0( 掛起到空閑 )和 S3( 掛起到內存 )。按 Sudeep 的變更,那些沒有實現 platform_suspend_ops
的平台將只顯示 freeze 狀態。
喚醒中斷
一旦系統處於某種睡眠狀態,系統必須要接收某個喚醒事件才能恢復系統。這些喚醒事件一般由系統的設備產生。因此一定要確保這些設備驅動使用喚醒中斷,並且將自身配置為接收喚醒中斷後產生喚醒事件。如果沒有正確識別喚醒設備,系統收到中斷後會繼續保持睡眠狀態而不會恢復。
一旦設備正確實現了喚醒介面的調用,就可用來生成喚醒事件。請確保 DT 文件正確配置了喚醒源。下面是一個配置喚醒源示例,該文件來自(arch/arm/boot/dst/am335x-evm.dts
):
gpio_keys: volume_keys@0 {
compatible = 「gpio-keys」;
#address-cells = <1>;
#size-cells = <0>;
autorepeat;
switch@9 {
label = 「volume-up」;
linux,code = <115>;
gpios = <&gpio0 2 GPIO_ACTIVE_LOW>;
wakeup-source;
};
switch@10 {
label = 「volume-down」;
linux,code = <114>;
gpios = <&gpio0 3 GPIO_ACTIVE_LOW>;
wakeup-source;
};
};
如上所示,有兩個 gpio 鍵被配置為喚醒源,在系統掛起期間,其中任何一個鍵被按下都會產生一個喚醒事件。
可替代 DT 文件配置的另一個喚醒源配置就是設備驅動,如果設備驅動自身在代碼裡面配置了喚醒支持,那麼就會使用該默認喚醒配置。
實施
凍結功能
如果系統希望能夠充分使用 掛起到空閑 ,那麼應該在 CPU 空閑驅動代碼中定義 enter_freeze()
函數。enter_freeze()
與 enter()
的函數原型略有不同。因此,不能將 enter()
同時指定給 enter
和 enter_freeze
。至少,系統會直接調用 enter()
。如果沒有定義 enter_freeze()
,系統會掛起,但是不會觸發那些只有當 enter_freeze()
定義了才會觸發的函數,比如 tick_freeze()
和 stop_critical_timing()
都不會發生。這會導致計時器中斷喚醒系統,但不會導致系統恢復,因為系統處理完中斷後會繼續掛起。
在掛起過程中,中斷越少越好(最好一個也沒有)。
下圖顯示了能耗和時間的對比。圖中的兩個尖刺分別是掛起和恢復。掛起前後的能耗尖刺是系統退出空閑態進行記錄操作,進程調度,計時器處理等。因延遲的緣故,系統進入更深層次空閑狀態需要花費一段時間。
能耗使用時序圖
下圖為 ftrace 抓取的 4 核 CPU 在系統掛起和恢復操作之前、之中和之後的活動。可以看到,在掛起期間,沒有請求或者中斷被處理。
Ftrace 抓取的掛起/恢復活動圖
空閑狀態
你必須確定哪個空閑狀態支持凍結。在凍結期間,電源相關代碼會決定用哪個空閑狀態來實現凍結。這個過程是通過在每個空閑狀態中查找誰定義了 enter_freeze()
來決定的。CPU 空閑驅動代碼或者 SoC 掛起相關代碼必須確定哪種空閑狀態實現凍結操作,並通過給每個 CPU 的可應用空閑狀態指定凍結功能來進行配置。
例如, Qualcomm 會在平台掛起代碼的掛起初始化函數處定義 enter_freeze
函數。這個工作是在 CPU 空閑驅動已經初始化後進行,以便所有結構已經定義就位。
掛起/恢復相關驅動支持
你可能會在第一次成功掛起操作後碰到驅動相關的 bug。很多驅動開發者沒有精力完全測試掛起和恢復相關的代碼。你甚至可能會發現掛起操作並沒有多少工作可做,因為 pm_runtime
已經做了你要做的掛起相關的一切工作。由於用戶空間已經被凍結,設備此時已經處於休眠狀態並且 pm_runtime
已經被禁止。
測試相關
測試 掛起到空閑 可以手動進行,也可以使用腳本/進程等實現自動掛起、自動睡眠,或者使用像 Android 中的 wakelock
來讓系統掛起。如果手動測試,下面的操作會將系統凍結。
/ # echo freeze > /sys/power/state
[ 142.580832] PM: Syncing filesystems … done.
[ 142.583977] Freezing user space processes … (elapsed 0.001 seconds) done.
[ 142.591164] Double checking all user space processes after OOM killer disable… (elapsed 0.000 seconds)
[ 142.600444] Freezing remaining freezable tasks … (elapsed 0.001 seconds) done.
[ 142.608073] Suspending console(s) (use no_console_suspend to debug)
[ 142.708787] mmc1: Reset 0x1 never completed.
[ 142.710608] msm_otg 78d9000.phy: USB in low power mode
[ 142.711379] PM: suspend of devices complete after 102.883 msecs
[ 142.712162] PM: late suspend of devices complete after 0.773 msecs
[ 142.712607] PM: noirq suspend of devices complete after 0.438 msecs
< system suspended >
….
< wake irq triggered >
[ 147.700522] PM: noirq resume of devices complete after 0.216 msecs
[ 147.701004] PM: early resume of devices complete after 0.353 msecs
[ 147.701636] msm_otg 78d9000.phy: USB exited from low power mode
[ 147.704492] PM: resume of devices complete after 3.479 msecs
[ 147.835599] Restarting tasks … done.
/ #
在上面的例子中,需要注意 MMC 驅動的操作佔了 102.883ms 中的 100ms。有些設備驅動在掛起的時候有很多工作要做,比如將數據刷出到硬碟,或者其他耗時的操作等。
如果系統定義了 凍結 ,那麼系統將嘗試掛起操作,如果沒有凍結功能,那麼你會看到下面的提示:
/ # echo freeze > /sys/power/state
sh: write error: Invalid argument
/ #
未來的發展
目前在 ARM 平台上的 掛起到空閑 有兩方面的工作需要做。第一方面工作在前面 platform_suspend_ops
小節中提到過,是總允許接受凍結狀態以及合併到 4.9 版本內核中的工作。另一方面工作是凍結功能的支持。
如果你希望設備有更好的響應及表現,那麼應該繼續完善凍結功能的實現。然而,由於很多 SoC 會使用 ARM 的 CPU 空閑驅動,這使得 ARM 的 CPU 空閑驅動完善它自己的通用凍結功能的工作更有意義了。而事實上,ARM 正在嘗試添加此通用支持。如果 SoC 供應商希望實現他們自己的 CPU 空閑驅動或者需要在進入更深層次的凍結休眠狀態時提供額外的支持,那麼只有實現自己的凍結功能。
via: http://www.linaro.org/blog/suspend-to-idle/
作者:Andy Gross 譯者:beyondworld 校對:jasminepeng
本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive