# 從 Linux Kernel 閱讀 Power Management
## Reference
- [Power Management](https://docs.kernel.org/admin-guide/pm/index.html)
- [Runtime Power Management Framework for I/O Devices](https://docs.kernel.org/power/runtime_pm.html)
- [Power Management - Deeper view](https://docs.kernel.org/power/index.html)
- [Device Power Management Basics](https://www.kernel.org/doc/html/v4.19/driver-api/pm/devices.html)
- [Introduction to Kernel Power Management - presentation](http://events17.linuxfoundation.org/sites/events/files/slides/Intro_Kernel_PM.pdf)
- [Power Management In The Linux Kernel Current Status And Future - presentation](http://events17.linuxfoundation.org/sites/events/files/slides/kernel_PM_plain.pdf)
## 1. Overview - Power Management Strategies
Linux Kernel 支援兩種主要的 Power Management (PM) 策略:
1. **system-wide power management**: (so called static PM) 由 user space 發出要求,將整個系統的狀態做統一調整,進入到低功耗的 sleep states;而後直到在收到特定的訊號時,恢復到 working state。
- 此 PM 根據休眠程度分成好幾種類別。
- 由於此 PM 是統一的狀態影響整個系統,故名為 system-wide。
2. **working-state power management**: (so called dynamic PM) 在 working state 的狀態下根據個別硬體元件狀態進行功耗上的動態調整。若元件處於 active (in use),則需要允許軟體存取使用,反之若為 inactive (idle),則不可被存取。
## 2. system-wide power management
> - [System Sleep States](https://docs.kernel.org/admin-guide/pm/sleep-states.html)
> - [System Suspend Code Flows](https://docs.kernel.org/admin-guide/pm/suspend-flows.html)
根據設定與需要,linux kernel 可以支援四種 system sleep states,由淺到深分別為:suspend-to-Idle, standby, suspend-to-RAM, hibernation
### 2.1 suspend-to-Idle
這是最普遍、輕量、軟體層級的 suspend 模式(freeze, S2I, S2Idle)。他比起 runtime idle (freezing userspace) 更省功耗,因為他也同時會 suspend timekeeping 並將 I/O devices 置於低功耗模式。由於整個系統都 suspend 了,也讓 CPU 能夠處在最深的 idle state。
相比以下提到的 standby 或 suspend-to-RAM,他是更普遍支援的功能,只要系統支援 `CONFIG_SUSPEND` 就需要有此功能。
#### suspend flow:
1. Invoking system-wide suspend notifiers:
- 用 callback 的方式告知各個 subsystem 去做狀態轉換準備。
2. Freezing tasks
- 從 userspace 開始 freeze,
- 接著換 kernel thread。然而 kernel thread 不可被 intercepted 的 (不是很確定這個詞的表達),於是 kernel thread 需要定時確認自己需不需要要被 froze,然後自發的、 uninterruptible 的進入 sleep state。
3. Suspending devices and reconfiguring IRQs.
- Device suspending 的四階段:Prepare, Suspend, Late suspend, Noirq suspend.
- 跟 system wakeup devices 有關的 IRQs (interrupt requests) 被準備好,以備隨時收到喚醒訊號。
4. Freezing the scheduler tick and suspending timekeeping.
- 最終,CPU 終於能進入最深的 idle state。scheduler tick 和 timekeeping 也被凍結。以至於最終 CPU 只能透過 non-timer hardware interrupt 來喚醒。
#### resume flow:
基本上就是依相反順序重啟:
1. Resuming timekeeping and unfreezing the scheduler tick.
2. Resuming devices and restoring the working-state configuration of IRQs.
3. Thawing tasks.
4. Invoking system-wide resume notifiers.
![Screenshot 2024-07-01 at 11.40.47 PM](https://hackmd.io/_uploads/SkCWAHeDA.png)
### 2.2 platform-dependent suspend - standby and suspend-to-RAM
standby 與 suspend-to-RAM 又被稱為 **platform-dependent suspend**,需要端看平台是否提供此功能。
#### standby
除了上述的 freezing userspace, suspend timekeeping 和 將 I/O device 設置 low-power states 外,standby 更將 suspend 了 nonboot CPUs (通常 boot CPU 即為 CPU0) 和更多 low-level system function。
依據 `CONFIG_SUSPEND` 是否設定決定能否使用此功能。
#### suspend-to-RAM
在這個狀態下 (STR or S2RAM) 幾乎將所有系統都轉換成 low-power state,除了 memory (RAM)。 memory 會保留電源以保存運行前狀態,且保留可以傳遞 "on" state 的 bus 通電。
依據 `CONFIG_SUSPEND` 是否設定決定能否使用此功能。
#### suspend and resume flow:
基本上就是先做 S2Idle 的前後步驟,最後增加 nonboot CPU offline, System Core Offline,然後 STR 會再將非必需的裝置轉換成 low-power mode。接著就是反著重新啟動。
![Screenshot 2024-06-30 at 10.35.52 PM](https://hackmd.io/_uploads/SkYL6yyDR.png)
### 2.3 Hibernation
在這個階段 (冬眠、Suspend-to-Disk or STD) 擁有最高的能源保存率,許多低階平台也可以支援。然而要進入 STD 狀態,也需要 low-level code 來重啟整個系統。
#### suspend flow
這個階段的 suspend 與上述三種模式在實現上有很大的差別。他需要三個階段的狀態更動,而上述的只需要一次的狀態更動。
首先,若使用 STD,kernel 會停止運作並生成一個快速映像檔 (snapshot image),以便在斷電後還能存儲在 non-volatile disk storage。再來,部分的系統會啟動,只是為了讓這個 image 能被存入到 disk。最後,整個系統就會斷電(除了一些 wakeup devices,好比說今天我手指按了鍵盤、打開筆電螢幕、開機鍵,保留這些被視為重新啟動訊號的裝置)。
![Screenshot 2024-06-30 at 10.13.33 PM](https://hackmd.io/_uploads/ByxQOk1PR.png)
#### resume flow
Resume 的階段有兩個(相比上述模式只需要一個)。首先會用 bootloader 重啟,restore kernel 會去尋找 hibernation image 並存入到 memory。第二步驟,整個系統會根據這個 image 原本的狀態重啟以恢復到原本的運作中。
![Screenshot 2024-06-30 at 10.14.45 PM](https://hackmd.io/_uploads/rJOwOkJwA.png)
### 2.4 Configuration in linux kernel
PM subsystem 使用了 `sysfs` 的統一介面寫成,可見 `/sys/power`。
- `state`: [`disk`, `freeze`, `standby`] 分別是 hibernation, S2Idle, standby.
- `mem_sleep`: [`s2idle`, `shallow`, `deep`] 分別是 S2Idle, standby, STR.
- `disk`: 這個參數告知系統在存好 hibernation image 後,該如何做處理。(`platform`: 進入特殊的低功耗模式(需有平台支援)。 `shutdown`: 關機。 `suspend`: 改用 `mem_sleep` 的模式 suspend,如果順利醒來則捨棄 image,否則就使用此 image 啟動。 `reboot`, `test_resume`: 後兩者主要是在測試功能才會用到。)
## 3. Working-State Power Management
- Idle PM: CPU idle, Device idle (Runtime PM)
- Active PM: CPUFreq
### 3.1 [CPU Idle Time Management](https://docs.kernel.org/admin-guide/pm/cpuidle.html)
作業系統會跑一個 Idle task,當執行單元 (cpu, core) 沒有在執行任務時,則會換上 Idle task 運行,若有其他任務,則會切換給其他任務作業。
這些 Idle task 是由 scheduler 來做安排的,Linux 中有一個 subsystem, CPU idle time management, 來管理 C-states (ACPI 術語)。
當作業系統執行 Idle task 的時候,會根據平台設計來決定處理器 low-power 的狀態和持續多久。(如果平台並未特別設定,他就像是一個 task 一樣 loop,直到被 scheduler 打斷。)
關於處理器需要執行什麼樣的低功耗設定,也需要端看硬體狀況。如果只是 single core cpu,則可以直接將 cpu 設為低功耗;若是多核或更複雜的執行組建,則需要考量各核心之間共用資源的關係(比如 cache)。
#### Idle Loop
在每一次 Idle loop,會有兩個主要行為:
1. 由 `governor` 為處理器(硬體)決定該進入哪種 idle state
2. 由 `driver` 來實際請處理器進入 idle state。
各個不同 idle state 會決定兩件事:
1. target residency: 該處理器進入此狀態最短需要待的時間。
2. exit latency: 紀錄該處理器離開該狀態最久需要花的時間(紀錄 worst case)。
`governor` 需要自行去調配處理器 idle 的時間。如果有些 timer events 是可以預知他何時會觸發,那這個情況下很容易去設定本次休眠可以的時間。然而有些 non-timer events 是沒有辦法預測的,`governor` 只能根據過去的經驗來推判下次可以休眠的時間為多久來進行設定。在這個基礎上,便又發展出了數個不同的演算法的 `governor` subsystem。
可參考 `/sys/devices/system/cpu/cpuidle/` 中 `available_governors`, `current_governor`, `current_driver`.
#### Idle CPUs and The Scheduler Tick
Scheduler Tick 是排程器管理的一個機制,讓正在運行的 task 能夠公平分配到 CPU 運行時間。會有一個 Periodiacal Tick,週期一到便會重新考量 CPU task 排程。
然而如果將 Idle CPU 放入一起考量是不合理的,這會讓 Idle CPU 需要一同納入考量,而頻繁重啟運行。
在這個基礎上 Governer 會停止 Scheduler Tick 將 Idle CPU 納入考量,讓處理器能夠進入穩定且長得休眠時間裡。
然而,如果遇到 Non-tick Timer wakeups, Expected Non-timer Wakeup 時候, Governer 會讓 ticker 重新考量 idle CPU 以進行協助進行 CPU 排程。
#### The menu Governor
#### The Timer Events Oriented (TEO) Governor
### 3.2 Device idle: Runtime PM
> [Runtime Power Management Framework for I/O Devices](https://docs.kernel.org/power/runtime_pm.html)
> [Linux 核心設計: Power Management(2): Runtime Power Management model](https://hackmd.io/@RinHizakura/SkziZRa9a)
- per-device idle
- single device at a time
```c
struct dev_pm_ops {
int (*runtime_suspend)(struct device *dev);
int (*runtime_resume)(struct device *dev);
int (*runtime_idle)(struct device *dev);
}
```
是否執行會由 PM core 來管理執行。這些 callback 可能會被定義在(依序檢查):
1. 該 device 屬於特定 domain:`dev->pm_domain`,優先採用此 callback。
2. 該 device 屬於特定 `dev->type`,且有定義 `dev->type->pm`
3. 該 device 屬於特定 `dev->class`,且有定義 `dev->class->pm`
4. 該 device 屬於特定 `dev->bus`,且有定義 `dev->bus->pm`
5. 如果皆未定義在 subsystem 中或無實作 callback,則檢查 `dev->driver->pm` 來執行。
#### 使用情境:
- Tell PM core the device is in use:
1. `pm_runtime_get()`, `_sync()`
2. Increment use count
3. 如果裝置原本為 suspend,執行 `pm_runtime_resume()` 先恢復執行
- Tell PM core the device is not in use:
1. `pm_runtime_put()`, `_sync()`
2. Decrement use count
- 如果 count 1->0:
1. 同時檢查 device usage counter 和該 device 的 ‘active’ children counter. 是否皆為 0
2. 若是則可以執行 `pm_runtime_idle()`
- `pm_runtime_idle()` 所作的行為取決於所對應的 subsystem 或 device,但總體目的是為了去為了檢查該裝置是否滿足 suspend 的條件。如果滿足,則執行 `suspend`。
- `pm_runtime_suspend()`
- prepare for low-power state
- ensure wakeups enabled
- save context
- 如果 `resume`, 則將 context 復原。
以下是各功能的執行條件:
- `runtime_resume/idle/suspend` 是 mutual exclusive 的。
- `idle/suspend` 只可執行於原本就是 active 的裝置
- `idle/suspend` 只能對上述提到兩種 counter 為 0 的裝置執行
- `resume` 只可對 suspend 的裝置執行。
#### Autosuspend
```c
pm_runtime_set_autosuspend_delay()
pm_runtime_mark_last_busy()
pm_runtime_put_autosuspend()
```
#### Grouping PM Domains (genpd)
Devices are often grouped into domains
- power gated as a group
Linux PM domains:
- override ops where PM domain present, PM core uses domain callbacks
```c
struct dev_pm_domain {
struct dev_pm_ops ops;
...
}
```
- The implementations is based on runtime PM
- When all devices in domain are runtime suspended: `genpd->power_off()`
- When first device in domain in runtime resume: `genpd->power_on()`
Since the power on/off can also need certain planning, genpd governors is required.
### 3.3 [CPU Performance Scaling: CPU Freq](https://docs.kernel.org/admin-guide/pm/cpufreq.html)
現代處理器可以選擇多個不同 clock frequency 或 voltage configuration 的選擇(ACPI術語:Operating Performance Points (OPP), P-states)。越高的 clk freq 與 voltage,便可以執行更多指令,卻也更加耗能。
透過調整 cpu freq (調整 P-states),亦可以達到功耗調節的效益。
#### `CPUFreq`
在 `CPUFreq` 的 subsystem 由 `core`, `scaling governors` 和 `scaling drivers` 組成。
- `core` 提供一個統一的架構與介面,讓其他組建若也支持 CPU performance scaling ,也能自己實現。
- `governors` 一個能夠預估 CPU 所需調節的演算法
- `dirvers` 實際去調節硬體的驅動。