Try   HackMD

Linux Kernel Power Management

kernel 中的電源管理可以分為以下兩類:

  • Static PM
    • 主要處理的是系統長時間處於非活動狀態時如何節省電量
    • 目前沒有主要的開發工作
  • Dynamic PM
    • 主要處理的是系統處於活動狀態時如何配置系統設備
    • 隨著 soc 的發展這個框架在不斷的發展跟調整

System Sleep States

Linux 核心最多可以支援四種系統睡眠狀態,其中包括休眠(hibernation)和最多三種不同的系統掛起(suspend)變體。以下是核心支援的睡眠狀態:

Suspend-to-Idle

這是一種通用的、純軟體實現的輕量級系統掛起變體(也稱為 S2I 或 S2Idle)。與運行時空閒(runtime idle)相比,這種方法能夠通過凍結用戶空間、暫停時間計算,並將所有 I/O 設備進一步置於低功耗狀態(其功耗可能低於工作狀態下的水平)來實現更高效的能量節省。這樣,處理器在系統掛起期間可以長時間保持在 deepest idle states ,最大限度地降低能耗。

系統會通過 in-band 中斷 從此狀態中喚醒,因此理論上,任何在工作狀態下能夠產生中斷的設備,也可以被設置為 S2Idle 的喚醒設備。

  • In-band 中斷 是指設備通過 正常的數據路徑 在執行常規任務時所產生的中斷。這種中斷與設備的 正常工作流程 相結合,用來觸發系統從 低功耗狀態(如 S0ix)中恢復。

此狀態可以用於不支援 Standby 或 Suspend-to-RAM 的平台,或者它可以與任何更深層的系統掛起變體一起使用,以提供較低的恢復延遲。如果 CONFIG_SUSPEND 內核配置選項已啟用,則始終支援此狀態。

CONFIG_SUSPEND

CONFIG_SUSPEND 是一個內核配置選項,用於啟用 系統掛起(suspend)功能。當這個選項啟用時,系統將支持掛起操作,並且能夠根據不同的硬體平台進行適當的休眠操作。

因此,S0ix 狀態的使用取決於是否啟用了 CONFIG_SUSPEND。如果沒有啟用這個選項,則 S0ix 狀態的機制將無法生效。

Standby

此狀態提供了適度但實際的能量節省,同時確保能夠輕鬆地過渡回工作狀態。由於核心邏輯仍保持供電,沒有丟失操作狀態,系統可以從掛起的位置快速恢復並繼續運行。

除了凍結用戶空間、暫停時間計算以及將所有 I/O 設備置於低功耗狀態,此狀態還會將非啟動 CPU 下線,並在過渡期間暫停所有低層次的系統功能。因此,相較於掛起至 suspend-to-idle,此狀態能夠進一步節省更多的能量,但恢復延遲通常會較長。

在此狀態下,能夠喚醒系統的設備數量通常較掛起至suspend-to-idle 為少,並且可能需要依賴平台來設置適當的喚醒功能。

當啟用 CONFIG_SUSPEND 內核配置選項,且平台已註冊對此狀態的支援時,該狀態會被支援。對於基於 ACPI(高級配置與電源接口)的系統,這個睡眠狀態會映射為 ACPI 定義的 S1 系統狀態。

Suspend-to-RAM

這個狀態(也稱為 STR 或 S2RAM),如果支援,可以提供顯著的能量節省,因為系統中的所有部分都會進入低功耗狀態,唯獨記憶體會進入自我刷新模式以保留其內容。進入 standby 時執行的所有步驟也會在過渡到 S2RAM 時執行。根據平台的能力,可能會有額外的操作進行。特別是,在基於 ACPI 的系統中,內核會在 S2RAM 過渡的最後一步將控制權交給韌體(BIOS),這通常會導致更多低階元件的關閉,這些元件並非直接由內核控制。

設備和 CPU 的狀態會被保存並保存在記憶體中。所有設備都會被 suspend 並進入低功耗狀態。在許多情況下,當進入 S2RAM 時,所有 peripheral buses 會失去電源,這些外圍總線負責連接系統內的各種設備(如 USB、PCI、網卡、顯示卡等),因此設備必須能夠處理恢復到「開啟」狀態的過渡。

在基於 ACPI 的系統中,S2RAM 需要平台固件中的一些最小啟動程式碼來從此狀態恢復系統。其他平台也可能是如此。

能喚醒系統的設備集合通常會比掛起至空閒和待機狀態少,並且可能需要依賴平台來設置適當的喚醒功能。

如果啟用了 CONFIG_SUSPEND 內核配置選項,並且平台已註冊對此狀態的支援,則支援 S2RAM。對於基於 ACPI 的系統,它映射到 ACPI 定義的 S3 系統狀態。

Hibernation

這個狀態(也稱為休眠到磁碟或 STD)提供了最大的能量節省,並且即使在平台沒有低層次的系統掛起支援的情況下,仍然可以使用。然而,它需要一些低層次的程式碼來恢復系統,這些程式碼必須針對底層的 CPU 架構存在。

休眠(Hibernation)與任何其他系統掛起變體有顯著的不同。將系統進入休眠狀態需要三個系統狀態變更,並且恢復則需要兩個系統狀態變更

首先,當觸發休眠時,內核會停止所有系統活動並創建一個記憶體快照影像,將其寫入持久性存儲中。接著,系統會進入一個可以保存快照影像的狀態,該影像被寫入並且最終系統會進入目標低功耗狀態,其中幾乎所有硬體組件,包括內存,都會被切斷電源,只有一小部分喚醒設備仍然保持啟動。

一旦快照影像寫入完成,系統可以進入一個特殊的低功耗狀態(像是 ACPI S4),或者簡單地關閉電源。關閉電源意味著最小的功耗,這使得該機制可以在任何系統上工作。然而,進入特殊的低功耗狀態則可能允許使用額外的系統喚醒方式(例如,按下鍵盤上的某個鍵或打開筆記型電腦的蓋子)。

在喚醒後,控制權會交給平台韌體,該韌體運行一個 boot loader,並啟動一個新的內核實例(控制權也可能直接交給 boot loader ,這取決於系統配置,但無論如何都會啟動一個新的內核實例)。這個新的內核實例(稱為恢復內核)會在持久性存儲中查找休眠影像,如果找到了,就將其載入到記憶體中。接下來,系統的所有活動將停止,並且恢復內核會將自己與影像內容重寫,然後跳轉到影像中存儲的原始內核中的特殊跳板區域(稱為影像內核),這時需要特定於架構的低層次程式碼。最後,影像內核會將系統恢復到休眠前的狀態,並允許用戶空間重新運行。

如果設置了 CONFIG_HIBERNATION 內核配置選項,則支援休眠功能。然而,該選項僅能在給定的 CPU 架構支援系統恢復的低層次程式碼的情況下設置。

Basic sysfs Interfaces for System Suspend and Hibernation

電源管理子系統提供了一個統一的 sysfs 接口,用於系統睡眠,無論底層系統架構或平台如何。這個接口位於 /sys/power/ 目錄中,並包含以下屬性(文件):

state

這個文件包含一個字符串列表,表示內核支援的睡眠狀態。將這些字符串中的一個寫入該文件會使內核開始將系統轉換到該字符串所表示的睡眠狀態

  • "disk"、"freeze" 和 "standby" 分別表示hibernation、suspend-to-idle 和 standby 睡眠狀態。
  • "mem" 字符串會根據下面描述的 mem_sleep 文件的內容來解釋。
  • 如果內核不支援任何系統睡眠狀態,則該文件不會出現。
echo "disk" | sudo tee /sys/power/state
echo "freeze" | sudo tee /sys/power/state

linux/kernel/power/suspend.c 中可以看到下列這段程式碼:

const char * const pm_labels[] = {
	[PM_SUSPEND_TO_IDLE] = "freeze",
	[PM_SUSPEND_STANDBY] = "standby",
	[PM_SUSPEND_MEM] = "mem",
};
const char *pm_states[PM_SUSPEND_MAX];
static const char * const mem_sleep_labels[] = {
	[PM_SUSPEND_TO_IDLE] = "s2idle",
	[PM_SUSPEND_STANDBY] = "shallow",
	[PM_SUSPEND_MEM] = "deep",
};

mem_sleep

這個檔案包含了一個字串列表,表示支援的系統 suspend 變體,並允許使用者空間選擇與上述狀態檔案中的“mem”字串相關聯的 suspend 變體。

這個檔案中可能存在的字串有 “s2idle”、 “shallow” 和 “deep”。其中,“s2idle” 字串始終代表 suspend-to-idle,而依照慣例,“shallow” 和 “deep” 分別代表 standby 和 suspend-to-RAM 。

將其中列出的某個字串寫入此檔案,會使該 suspend 變體與狀態檔案中的“mem”字串相關聯。當前與“mem”字串相關聯的掛起變體會以方括號顯示。

如果內核不支援系統 suspend,則此檔案將不存在。

wu@wu-Pro-E500-G6-WS720T:~$ cat /sys/power/mem_sleep
[s2idle] deep
wu@wu-Pro-E500-G6-WS720T:~$ echo "deep" | sudo tee /sys/power/mem_sleep
deep
wu@wu-Pro-E500-G6-WS720T:~$ cat /sys/power/mem_sleep
s2idle [deep]
wu@wu-Pro-E500-G6-WS720T:~$ echo "s2idle" | sudo tee /sys/power/mem_sleep

disk

這個文件控制休眠的操作模式。具體來說,它告訴內核在創建休眠影像後應該執行什麼操作。

從這個文件讀取會返回一個編碼的支援選項列表,具體如下:

  • platform
    • 將系統置於一個特殊的低功耗狀態(例如 ACPI S4),以便提供額外的喚醒選項,並可能允許平台韌體在喚醒後執行簡化的初始化路徑。
      僅當平台提供了一個特殊機制,在創建休眠影像後將系統置於休眠狀態時,這個選項才可用(例如支持 ACPI 的平台通常會這樣做)。
  • shutdown
    • 關閉系統電源。
  • reboot
    • 重啟系統(主要用於診斷)。
  • suspend
    • Hybrid system suspend.將系統置於通過上述 mem_sleep 文件選擇的 suspend 睡眠狀態。如果系統成功從該狀態喚醒,則丟棄休眠影像並繼續運行。否則,使用休眠影像來恢復系統的先前狀態。
      如果支持系統 suspend,則此選項可用。
  • test_resume
    • 診斷操作。將影像加載,並假設系統剛剛從休眠中喚醒,當前運行的內核實例是恢復內核,然後進行完整的系統恢復。

當前選中的選項將以方括號顯示,這表示當寫入「disk」到 /sys/power/state 時,對應的操作會在創建並保存影像後執行。

如果內核不支持休眠,則此文件不會出現

wu@wu-Pro-E500-G6-WS720T:~$ cat /sys/power/disk
[platform] shutdown reboot suspend test_resume 

image_size

此文件控制休眠影像的大小。

它可以被寫入一個表示非負整數的字符串,該數字將作為影像大小的最大限制(以字節為單位)。休眠核心會盡力確保影像大小不會超過這個數字,但如果這無法達到,休眠影像仍然會創建,並且其大小會儘可能小。具體來說,寫入 "0" 會將休眠影像的大小設置為最小。

從該文件讀取會返回當前的影像大小限制,默認設置為可用記憶體大小的約 2/5。

pm_trace

這個檔案控制著「PM 追蹤」機制,將最後一次的 suspend 或 resume 事件點儲存到 RTC 記憶體中,並且能夠在重新啟動後仍然保留。這有助於更有效地調試由於裝置驅動程式在系統 suspend 或 resume 過程中失敗所引起的硬鎖死或重新啟動(這是較為常見的情況)。

如果它的值為 "1",則每個 suspend /resume 事件的指紋會依序儲存在 RTC 記憶體中(覆蓋實際的 RTC 資訊)。這樣,即使在存儲指紋後系統崩潰,它仍然能夠保留並在之後用來識別造成崩潰的驅動程式。

預設情況下,它的值為 "0",但可以通過寫入代表非零整數的字串來將其改為 "1"。

wakeup

閱讀了 Linux 核心設計: Power Management(1): System Sleep model 發現到還有這個檔案:

不同於上述可見的 sysfs 節點是對於整個 PM 子系統的控制方式。每個由 PM 子系統管理的裝置可以有各異的 PM 功能支援,可由 /sys/devices/.../power/ 路徑下的檔案取得相關資訊或更改配置。

有些裝置可以被用來觸發喚醒系統的事件,使系統退出睡眠狀態。例如透過對鍵盤輸入或者按下開機按鍵,讓系統從睡眠中恢復。與此功能相關的檔案是 /sys/devices/.../power/wakeup

struct dev_pm_info {
	unsigned int		can_wakeup:1;
	...
	struct wakeup_source	*wakeup;

要在 Linux 核心中使裝置能被用來喚醒系統的睡眠狀態,驅動程式需先使用 device_set_wakeup_capable() 來設置裝置對應的 struct device 下的 power.can_wakeup 欄位。這表示裝置首先在物理上可支援此功能。

power.wakeup 則是一個 struct wakeup_source 的指標,定義了當將裝置作為喚醒系統的觸發來源時,如何把喚醒事件通知到 PM core 的程式碼實作,使後者能夠將整個系統都恢復到運作狀態。

當裝置在硬體上能夠支援系統喚醒,且在核心中也提供 power.wakeup 的實作並由 device_set_wakeup_capable 啟用,在 userspace 的 /sys/devices/.../power/ 路徑下就可以看到 wakeup 檔案。在 userspace 可以向其中寫入 "enabled" 或者 "disabled" 來決定是否要將該裝置作為喚醒訊號的提供者。

留意到系統的喚醒與 runtime PM 時談論的 remote wakeup 不同。雖然有些情況兩者在物理上是透過一樣的機制,但 Remote wakeup 主要是指在低功耗狀態的裝置可以觸發特定中斷,以通知系統應將其置於全速狀態的功能。但這些中斷在系統進入睡眠時不一定能夠觸發。

可以輸入下列指令來找尋 wakeup 檔案

$ find /sys/devices/ -name wakeup

當找到後能輸入來查看是位於 /sys/devices// 路徑下的設備)當系統處於休眠狀態時,能否用來喚醒系統。

cat /sys/devices/.../power/wakeup

例子( disabled ):

wu@wu-Pro-E500-G6-WS720T:~$ cat /sys/devices/pci0000:00/0000:00:1f.3/power/wakeup
disabled

例子( enabled ):

wu@wu-Pro-E500-G6-WS720T:~$ cat /sys/devices/platform/rtc_cmos/rtc/rtc0/alarmtimer.0.auto/power/wakeup
enabled