核心中的 CPU 熱插拔¶
- 日期:
2021 年 9 月
- 作者:
Sebastian Andrzej Siewior <bigeasy@linutronix.de>, Rusty Russell <rusty@rustcorp.com.au>, Srivatsa Vaddagiri <vatsa@in.ibm.com>, Ashok Raj <ashok.raj@intel.com>, Joel Schopp <jschopp@austin.ibm.com>, Thomas Gleixner <tglx@linutronix.de>
簡介¶
系統架構的現代進步在處理器中引入了高階錯誤報告和糾正功能。有一些 OEM 廠商支援同樣支援熱插拔的 NUMA 硬體,其中物理節點的插入和移除需要 CPU 熱插拔支援。
這些進步要求可以從核心中移除可用的 CPU,無論是出於配置原因,還是為了 RAS 目的,以使故障 CPU 遠離系統執行路徑。因此,Linux 核心中需要 CPU 熱插拔支援。
CPU 熱插拔支援的一個更具新穎性的用途是它目前在 SMP 的掛起恢復支援中的應用。雙核和超執行緒(HT)支援使得即使是筆記型電腦也能執行 SMP 核心,而這些方法以前是不支援的。
命令列開關¶
maxcpus=n將啟動時 CPU 限制為 *n* 個。例如,如果您有四個 CPU,使用
maxcpus=2將只啟動兩個。您可以選擇稍後將其他 CPU 上線。nr_cpus=n限制核心將支援的 CPU 總數。如果此處提供的數字低於物理可用 CPU 的數量,那麼這些 CPU 將無法在以後上線。
possible_cpus=n此選項在
cpu_possible_mask中設定possible_cpus位。此選項僅限於 X86 和 S390 架構。
cpu0_hotplug允許關閉 CPU0。
此選項僅限於 X86 架構。
CPU 對映¶
cpu_possible_mask系統中可能可用的 CPU 點陣圖。這用於為 per_cpu 變數分配一些啟動時記憶體,這些變數不是為了隨 CPU 的可用或移除而增長/收縮而設計的。一旦在啟動時發現階段設定,該對映就是靜態的,即任何時候都不會新增或移除位。根據您的系統需求提前精確裁剪可以節省一些啟動時記憶體。
cpu_online_mask所有當前線上 CPU 的點陣圖。在 CPU 可用於核心排程並準備好接收來自裝置的 interrupts 後,它會在
__cpu_up()中設定。當使用__cpu_disable()關閉 CPU 時,它會被清除,在此之前所有作業系統服務(包括 interrupts)都已遷移到另一個目標 CPU。cpu_present_mask系統中當前存在的 CPU 點陣圖。並非所有 CPU 都可能線上。當相關子系統(例如 ACPI)處理物理熱插拔時,根據事件是熱新增/熱移除,對映中的新位可以新增或移除。目前沒有鎖定規則。典型用法是在啟動期間初始化拓撲,此時熱插拔被停用。
你實際上不需要操作任何系統 CPU 對映。它們對於大多數用途來說應該是隻讀的。在設定每 CPU 資源時,幾乎總是使用 cpu_possible_mask 或 for_each_possible_cpu() 進行迭代。宏 for_each_cpu() 可用於迭代自定義 CPU 掩碼。
除了 cpumask_t 之外,不要使用任何其他東西來表示 CPU 點陣圖。
使用 CPU 熱插拔¶
核心選項 CONFIG_HOTPLUG_CPU 需要啟用。它目前在多種架構上可用,包括 ARM、MIPS、PowerPC 和 X86。配置透過 sysfs 介面完成。
$ ls -lh /sys/devices/system/cpu
total 0
drwxr-xr-x 9 root root 0 Dec 21 16:33 cpu0
drwxr-xr-x 9 root root 0 Dec 21 16:33 cpu1
drwxr-xr-x 9 root root 0 Dec 21 16:33 cpu2
drwxr-xr-x 9 root root 0 Dec 21 16:33 cpu3
drwxr-xr-x 9 root root 0 Dec 21 16:33 cpu4
drwxr-xr-x 9 root root 0 Dec 21 16:33 cpu5
drwxr-xr-x 9 root root 0 Dec 21 16:33 cpu6
drwxr-xr-x 9 root root 0 Dec 21 16:33 cpu7
drwxr-xr-x 2 root root 0 Dec 21 16:33 hotplug
-r--r--r-- 1 root root 4.0K Dec 21 16:33 offline
-r--r--r-- 1 root root 4.0K Dec 21 16:33 online
-r--r--r-- 1 root root 4.0K Dec 21 16:33 possible
-r--r--r-- 1 root root 4.0K Dec 21 16:33 present
檔案 offline、online、possible、present 代表 CPU 掩碼。每個 CPU 資料夾都包含一個 online 檔案,用於控制邏輯開啟 (1) 和關閉 (0) 狀態。要邏輯關閉 CPU4
$ echo 0 > /sys/devices/system/cpu/cpu4/online
smpboot: CPU 4 is now offline
一旦 CPU 關閉,它將從 /proc/interrupts、/proc/cpuinfo 中移除,並且不應由 top 命令顯示。要將 CPU4 重新上線
$ echo 1 > /sys/devices/system/cpu/cpu4/online
smpboot: Booting Node 0 Processor 4 APIC 0x1
CPU 再次可用。這應該適用於所有 CPU,但 CPU0 通常是特殊的,並被排除在 CPU 熱插拔之外。
CPU 熱插拔協調¶
離線情況¶
一旦 CPU 被邏輯關閉,已註冊熱插拔狀態的 teardown 回撥將被呼叫,從 CPUHP_ONLINE 開始,到狀態 CPUHP_OFFLINE 結束。這包括:
如果任務因掛起操作而凍結,則 cpuhp_tasks_frozen 將設定為 true。
所有程序都從這個即將離線的 CPU 遷移到新的 CPU。新 CPU 從每個程序的當前 cpuset 中選擇,該 cpuset 可能是所有線上 CPU 的子集。
針對此 CPU 的所有中斷都遷移到新的 CPU
定時器也遷移到新的 CPU
一旦所有服務都遷移完成,核心會呼叫一個特定於架構的例程
__cpu_disable()來執行架構特定的清理。
CPU 熱插拔 API¶
CPU 熱插拔狀態機¶
CPU 熱插拔使用一個簡單的狀態機,其狀態空間從 CPUHP_OFFLINE 到 CPUHP_ONLINE 呈線性。每個狀態都有一個啟動回撥和一個關閉回撥。
當 CPU 上線時,啟動回撥會按順序呼叫,直到達到狀態 CPUHP_ONLINE。當設定狀態回撥或將例項新增到多例項狀態時,也可以呼叫它們。
當 CPU 離線時,關閉回撥會按相反順序呼叫,直到達到狀態 CPUHP_OFFLINE。當移除狀態回撥或從多例項狀態中移除例項時,也可以呼叫它們。
如果使用方只需要熱插拔操作的單向回撥(CPU 上線或 CPU 離線),那麼在設定狀態時,可以將不需要的回撥設定為 NULL。
狀態空間分為三個部分
PREPARE 階段
PREPARE 階段覆蓋從 CPUHP_OFFLINE 到 CPUHP_BRINGUP_CPU 的狀態空間。
此階段的啟動回撥在 CPU 上線操作期間,CPU 啟動之前呼叫。關閉回撥在 CPU 離線操作期間,CPU 失效後呼叫。
回撥在控制 CPU 上呼叫,因為它們顯然不能在尚未啟動或已經失效的熱插拔 CPU 上執行。
啟動回撥用於設定成功使 CPU 上線所需的資源。關閉回撥用於釋放資源,或在熱插拔 CPU 失效後將待處理的工作移動到線上 CPU。
啟動回撥允許失敗。如果回撥失敗,CPU 上線操作將被中止,並且 CPU 將再次回到先前的狀態(通常是 CPUHP_OFFLINE)。
此階段的關閉回撥不允許失敗。
STARTING 階段
STARTING 階段覆蓋 CPUHP_BRINGUP_CPU + 1 和 CPUHP_AP_ONLINE 之間的狀態空間。
此階段的啟動回撥在 CPU 上線操作期間,在早期 CPU 設定程式碼中,在熱插拔 CPU 上且中斷停用時呼叫。關閉回撥在 CPU 離線操作期間,在 CPU 完全關閉前不久,在熱插拔 CPU 上且中斷停用時呼叫。
此階段的回撥不允許失敗。
回撥用於底層硬體初始化/關閉和核心子系統。
ONLINE 階段
ONLINE 階段覆蓋 CPUHP_AP_ONLINE + 1 和 CPUHP_ONLINE 之間的狀態空間。
此階段的啟動回撥在 CPU 上線操作期間在熱插拔 CPU 上呼叫。關閉回撥在 CPU 離線操作期間在熱插拔 CPU 上呼叫。
回撥在每 CPU 熱插拔執行緒的上下文中呼叫,該執行緒被固定在熱插拔 CPU 上。回撥在中斷和搶佔啟用時呼叫。
回撥允許失敗。當回撥失敗時,熱插拔操作中止,CPU 返回到先前的狀態。
CPU 上線/離線操作¶
成功的上線操作如下所示
[CPUHP_OFFLINE]
[CPUHP_OFFLINE + 1]->startup() -> success
[CPUHP_OFFLINE + 2]->startup() -> success
[CPUHP_OFFLINE + 3] -> skipped because startup == NULL
...
[CPUHP_BRINGUP_CPU]->startup() -> success
=== End of PREPARE section
[CPUHP_BRINGUP_CPU + 1]->startup() -> success
...
[CPUHP_AP_ONLINE]->startup() -> success
=== End of STARTUP section
[CPUHP_AP_ONLINE + 1]->startup() -> success
...
[CPUHP_ONLINE - 1]->startup() -> success
[CPUHP_ONLINE]
成功的離線操作如下所示
[CPUHP_ONLINE]
[CPUHP_ONLINE - 1]->teardown() -> success
...
[CPUHP_AP_ONLINE + 1]->teardown() -> success
=== Start of STARTUP section
[CPUHP_AP_ONLINE]->teardown() -> success
...
[CPUHP_BRINGUP_ONLINE - 1]->teardown()
...
=== Start of PREPARE section
[CPUHP_BRINGUP_CPU]->teardown()
[CPUHP_OFFLINE + 3]->teardown()
[CPUHP_OFFLINE + 2] -> skipped because teardown == NULL
[CPUHP_OFFLINE + 1]->teardown()
[CPUHP_OFFLINE]
失敗的上線操作如下所示
[CPUHP_OFFLINE]
[CPUHP_OFFLINE + 1]->startup() -> success
[CPUHP_OFFLINE + 2]->startup() -> success
[CPUHP_OFFLINE + 3] -> skipped because startup == NULL
...
[CPUHP_BRINGUP_CPU]->startup() -> success
=== End of PREPARE section
[CPUHP_BRINGUP_CPU + 1]->startup() -> success
...
[CPUHP_AP_ONLINE]->startup() -> success
=== End of STARTUP section
[CPUHP_AP_ONLINE + 1]->startup() -> success
---
[CPUHP_AP_ONLINE + N]->startup() -> fail
[CPUHP_AP_ONLINE + (N - 1)]->teardown()
...
[CPUHP_AP_ONLINE + 1]->teardown()
=== Start of STARTUP section
[CPUHP_AP_ONLINE]->teardown()
...
[CPUHP_BRINGUP_ONLINE - 1]->teardown()
...
=== Start of PREPARE section
[CPUHP_BRINGUP_CPU]->teardown()
[CPUHP_OFFLINE + 3]->teardown()
[CPUHP_OFFLINE + 2] -> skipped because teardown == NULL
[CPUHP_OFFLINE + 1]->teardown()
[CPUHP_OFFLINE]
失敗的離線操作如下所示
[CPUHP_ONLINE]
[CPUHP_ONLINE - 1]->teardown() -> success
...
[CPUHP_ONLINE - N]->teardown() -> fail
[CPUHP_ONLINE - (N - 1)]->startup()
...
[CPUHP_ONLINE - 1]->startup()
[CPUHP_ONLINE]
遞迴失敗無法合理處理。請看以下由於離線操作失敗導致的遞迴失敗示例
[CPUHP_ONLINE]
[CPUHP_ONLINE - 1]->teardown() -> success
...
[CPUHP_ONLINE - N]->teardown() -> fail
[CPUHP_ONLINE - (N - 1)]->startup() -> success
[CPUHP_ONLINE - (N - 2)]->startup() -> fail
CPU 熱插拔狀態機在此停止,不再嘗試再次下線,因為那很可能導致無限迴圈
[CPUHP_ONLINE - (N - 1)]->teardown() -> success
[CPUHP_ONLINE - N]->teardown() -> fail
[CPUHP_ONLINE - (N - 1)]->startup() -> success
[CPUHP_ONLINE - (N - 2)]->startup() -> fail
[CPUHP_ONLINE - (N - 1)]->teardown() -> success
[CPUHP_ONLINE - N]->teardown() -> fail
反覆操作。在這種情況下,CPU 停留在狀態
[CPUHP_ONLINE - (N - 1)]
這至少能讓系統繼續執行,併為使用者提供除錯甚至解決問題的機會。
分配狀態¶
有兩種方法可以分配 CPU 熱插拔狀態
靜態分配
當子系統或驅動程式對其他 CPU 熱插拔狀態有排序要求時,必須使用靜態分配。例如,在 CPU 上線操作期間,PERF 核心啟動回撥必須在 PERF 驅動程式啟動回撥之前呼叫。在 CPU 離線操作期間,驅動程式關閉回撥必須在核心關閉回撥之前呼叫。靜態分配的狀態由 `cpuhp_state` 列舉中的常量描述,該列舉可在 `include/linux/cpuhotplug.h` 中找到。
將狀態插入列舉的適當位置,以滿足排序要求。狀態常量必須用於狀態設定和移除。
當狀態回撥不是在執行時設定,並且是 `kernel/cpu.c` 中 CPU 熱插拔狀態陣列初始化器的一部分時,也需要靜態分配。
動態分配
當狀態回撥沒有排序要求時,動態分配是首選方法。狀態號由設定函式分配,並在成功時返回給呼叫者。
只有 PREPARE 和 ONLINE 部分提供動態分配範圍。STARTING 部分不提供動態分配範圍,因為該部分中的大多數回撥都有明確的排序要求。
設定 CPU 熱插拔狀態¶
核心程式碼提供了以下函式來設定狀態
cpuhp_setup_state(state, name, startup, teardown)
cpuhp_setup_state_nocalls(state, name, startup, teardown)
cpuhp_setup_state_cpuslocked(state, name, startup, teardown)
cpuhp_setup_state_nocalls_cpuslocked(state, name, startup, teardown)
對於驅動程式或子系統有多個例項且需要為每個例項呼叫相同的 CPU 熱插拔狀態回撥的情況,CPU 熱插拔核心提供了多例項支援。相對於驅動程式特定的例項列表,其優勢在於例項相關函式與 CPU 熱插拔操作完全序列化,並提供在新增和移除時自動呼叫狀態回撥的功能。要設定這樣的多例項狀態,可以使用以下函式
cpuhp_setup_state_multi(state, name, startup, teardown)
@state 引數是靜態分配的狀態,或者是動態分配狀態的常量之一——CPUHP_BP_PREPARE_DYN、CPUHP_AP_ONLINE_DYN——這取決於應為其分配動態狀態的狀態部分(PREPARE、ONLINE)。
@name 引數用於 sysfs 輸出和檢測。命名約定是 “subsys:mode” 或 “subsys/driver:mode”,例如 “perf:mode” 或 “perf/x86:mode”。常見的模式名稱是
prepare |
用於 PREPARE 階段的狀態 |
dead |
用於 PREPARE 階段中不提供啟動回撥的狀態 |
starting |
用於 STARTING 階段的狀態 |
dying |
用於 STARTING 階段中不提供啟動回撥的狀態 |
online |
用於 ONLINE 階段的狀態 |
offline |
用於 ONLINE 階段中不提供啟動回撥的狀態 |
由於 @name 引數僅用於 sysfs 和檢測,如果其他模式描述符能比通用模式更好地描述狀態的性質,也可以使用。
@name 引數示例:“perf/online”、“perf/x86:prepare”、“RCU/tree:dying”、“sched/waitempty”
@startup 引數是指向回撥函式的函式指標,該回調函式應在 CPU 上線操作期間呼叫。如果使用方不需要啟動回撥,則將指標設定為 NULL。
@teardown 引數是指向回撥函式的函式指標,該回調函式應在 CPU 離線操作期間呼叫。如果使用方不需要關閉回撥,則將指標設定為 NULL。
這些函式在處理已安裝回調的方式上有所不同
cpuhp_setup_state_nocalls()、cpuhp_setup_state_nocalls_cpuslocked()和cpuhp_setup_state_multi()只安裝回調
cpuhp_setup_state()和cpuhp_setup_state_cpuslocked()安裝回調併為所有當前狀態大於新安裝狀態的線上 CPU 呼叫 @startup 回撥(如果非 NULL)。根據狀態部分,回撥要麼在當前 CPU 上呼叫(PREPARE 部分),要麼在每個線上 CPU 上呼叫(ONLINE 部分),在 CPU 的熱插拔執行緒上下文中。如果 CPU N 的回撥失敗,則呼叫 CPU 0 .. N-1 的關閉回撥以回滾操作。狀態設定失敗,狀態回撥未安裝,並且在動態分配的情況下,已分配的狀態被釋放。
狀態設定和回撥呼叫與 CPU 熱插拔操作序列化。如果設定函式必須從 CPU 熱插拔讀鎖定區域呼叫,則必須使用 _cpuslocked() 變體。這些函式不能在 CPU 熱插拔回調內部使用。
- 函式返回值
0
靜態分配的狀態已成功設定
>0
動態分配的狀態已成功設定。
返回的數字是已分配的狀態號。如果稍後需要移除狀態回撥(例如模組移除),則呼叫者必須儲存此數字,並將其用作狀態移除函式的 @state 引數。對於多例項狀態,動態分配的狀態號也需要作為例項新增/移除操作的 @state 引數。
<0
操作失敗
移除 CPU 熱插拔狀態¶
為了移除先前設定的狀態,提供了以下函式
cpuhp_remove_state(state)
cpuhp_remove_state_nocalls(state)
cpuhp_remove_state_nocalls_cpuslocked(state)
cpuhp_remove_multi_state(state)
@state 引數是靜態分配的狀態,或由 cpuhp_setup_state*() 在動態範圍內分配的狀態號。如果狀態在動態範圍內,則狀態號將被釋放並再次可用於動態分配。
這些函式在處理已安裝回調的方式上有所不同
cpuhp_remove_state_nocalls()、cpuhp_remove_state_nocalls_cpuslocked()和cpuhp_remove_multi_state()只移除回撥。
cpuhp_remove_state()移除回撥併為所有當前狀態大於已移除狀態的線上 CPU 呼叫關閉回撥(如果非 NULL)。根據狀態部分,回撥要麼在當前 CPU 上呼叫(PREPARE 部分),要麼在每個線上 CPU 上呼叫(ONLINE 部分),在 CPU 的熱插拔執行緒上下文中。為了完成移除,關閉回撥不應失敗。
狀態移除和回撥呼叫與 CPU 熱插拔操作序列化。如果移除函式必須從 CPU 熱插拔讀鎖定區域呼叫,則必須使用 _cpuslocked() 變體。這些函式不能在 CPU 熱插拔回調內部使用。
如果移除了多例項狀態,則呼叫者必須首先移除所有例項。
多例項狀態管理¶
一旦設定了多例項狀態,就可以向該狀態新增例項
cpuhp_state_add_instance(state, node)
cpuhp_state_add_instance_nocalls(state, node)
@state 引數是靜態分配的狀態,或由 cpuhp_setup_state_multi() 在動態範圍內分配的狀態號。
@node 引數是指向 hlist_node 的指標,該 hlist_node 嵌入在例項的資料結構中。該指標會傳遞給多例項狀態回撥,回撥可以使用它透過 container_of() 獲取例項。
這些函式在處理已安裝回調的方式上有所不同
cpuhp_state_add_instance_nocalls()只將例項新增到多例項狀態的節點列表中。
cpuhp_state_add_instance()新增例項併為所有當前狀態大於 @state 的線上 CPU 呼叫與 @state 關聯的啟動回撥(如果非 NULL)。回撥僅為待新增的例項呼叫。根據狀態部分,回撥要麼在當前 CPU 上呼叫(PREPARE 部分),要麼在每個線上 CPU 上呼叫(ONLINE 部分),在 CPU 的熱插拔執行緒上下文中。如果 CPU N 的回撥失敗,則呼叫 CPU 0 .. N-1 的關閉回撥以回滾操作,函式失敗且例項未新增到多例項狀態的節點列表中。
要從狀態的節點列表中移除例項,可以使用以下函式
cpuhp_state_remove_instance(state, node)
cpuhp_state_remove_instance_nocalls(state, node)
引數與上述 cpuhp_state_add_instance*() 變體相同。
這些函式在處理已安裝回調的方式上有所不同
cpuhp_state_remove_instance_nocalls()只從狀態的節點列表中移除例項。
cpuhp_state_remove_instance()移除例項併為所有當前狀態大於 @state 的線上 CPU 呼叫與 @state 關聯的關閉回撥(如果非 NULL)。回撥僅為待移除的例項呼叫。根據狀態部分,回撥要麼在當前 CPU 上呼叫(PREPARE 部分),要麼在每個線上 CPU 上呼叫(ONLINE 部分),在 CPU 的熱插拔執行緒上下文中。為了完成移除,關閉回撥不應失敗。
節點列表的新增/移除操作和回撥呼叫與 CPU 熱插拔操作序列化。這些函式不能在 CPU 熱插拔回調和 CPU 熱插拔讀鎖定區域內部使用。
示例¶
在 STARTING 階段設定和關閉一個靜態分配的狀態,用於線上和離線操作的通知
ret = cpuhp_setup_state(CPUHP_SUBSYS_STARTING, "subsys:starting", subsys_cpu_starting, subsys_cpu_dying);
if (ret < 0)
return ret;
....
cpuhp_remove_state(CPUHP_SUBSYS_STARTING);
在 ONLINE 階段設定和關閉一個動態分配的狀態,用於離線操作的通知
state = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "subsys:offline", NULL, subsys_cpu_offline);
if (state < 0)
return state;
....
cpuhp_remove_state(state);
在 ONLINE 階段設定和關閉一個動態分配的狀態,用於線上操作的通知,而不呼叫回撥
state = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, "subsys:online", subsys_cpu_online, NULL);
if (state < 0)
return state;
....
cpuhp_remove_state_nocalls(state);
在 ONLINE 階段設定、使用和關閉一個動態分配的多例項狀態,用於線上和離線操作的通知
state = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, "subsys:online", subsys_cpu_online, subsys_cpu_offline);
if (state < 0)
return state;
....
ret = cpuhp_state_add_instance(state, &inst1->node);
if (ret)
return ret;
....
ret = cpuhp_state_add_instance(state, &inst2->node);
if (ret)
return ret;
....
cpuhp_remove_instance(state, &inst1->node);
....
cpuhp_remove_instance(state, &inst2->node);
....
cpuhp_remove_multi_state(state);
熱插拔狀態測試¶
驗證自定義狀態是否按預期工作的一種方法是關閉 CPU,然後再次將其上線。也可以將 CPU 置於特定狀態(例如 CPUHP_AP_ONLINE),然後再返回到 CPUHP_ONLINE。這將模擬 CPUHP_AP_ONLINE 之後一個狀態的錯誤,從而導致回滾到線上狀態。
所有已註冊的狀態都列舉在 /sys/devices/system/cpu/hotplug/states 中
$ tail /sys/devices/system/cpu/hotplug/states
138: mm/vmscan:online
139: mm/vmstat:online
140: lib/percpu_cnt:online
141: acpi/cpu-drv:online
142: base/cacheinfo:online
143: virtio/net:online
144: x86/mce:online
145: printk:online
168: sched:active
169: online
要將 CPU4 回滾到 lib/percpu_cnt:online 並重新上線,只需執行
$ cat /sys/devices/system/cpu/cpu4/hotplug/state
169
$ echo 140 > /sys/devices/system/cpu/cpu4/hotplug/target
$ cat /sys/devices/system/cpu/cpu4/hotplug/state
140
重要的是要注意,狀態 140 的關閉回撥已被呼叫。現在重新上線
$ echo 169 > /sys/devices/system/cpu/cpu4/hotplug/target
$ cat /sys/devices/system/cpu/cpu4/hotplug/state
169
啟用跟蹤事件後,各個步驟也可見
# TASK-PID CPU# TIMESTAMP FUNCTION
# | | | | |
bash-394 [001] 22.976: cpuhp_enter: cpu: 0004 target: 140 step: 169 (cpuhp_kick_ap_work)
cpuhp/4-31 [004] 22.977: cpuhp_enter: cpu: 0004 target: 140 step: 168 (sched_cpu_deactivate)
cpuhp/4-31 [004] 22.990: cpuhp_exit: cpu: 0004 state: 168 step: 168 ret: 0
cpuhp/4-31 [004] 22.991: cpuhp_enter: cpu: 0004 target: 140 step: 144 (mce_cpu_pre_down)
cpuhp/4-31 [004] 22.992: cpuhp_exit: cpu: 0004 state: 144 step: 144 ret: 0
cpuhp/4-31 [004] 22.993: cpuhp_multi_enter: cpu: 0004 target: 140 step: 143 (virtnet_cpu_down_prep)
cpuhp/4-31 [004] 22.994: cpuhp_exit: cpu: 0004 state: 143 step: 143 ret: 0
cpuhp/4-31 [004] 22.995: cpuhp_enter: cpu: 0004 target: 140 step: 142 (cacheinfo_cpu_pre_down)
cpuhp/4-31 [004] 22.996: cpuhp_exit: cpu: 0004 state: 142 step: 142 ret: 0
bash-394 [001] 22.997: cpuhp_exit: cpu: 0004 state: 140 step: 169 ret: 0
bash-394 [005] 95.540: cpuhp_enter: cpu: 0004 target: 169 step: 140 (cpuhp_kick_ap_work)
cpuhp/4-31 [004] 95.541: cpuhp_enter: cpu: 0004 target: 169 step: 141 (acpi_soft_cpu_online)
cpuhp/4-31 [004] 95.542: cpuhp_exit: cpu: 0004 state: 141 step: 141 ret: 0
cpuhp/4-31 [004] 95.543: cpuhp_enter: cpu: 0004 target: 169 step: 142 (cacheinfo_cpu_online)
cpuhp/4-31 [004] 95.544: cpuhp_exit: cpu: 0004 state: 142 step: 142 ret: 0
cpuhp/4-31 [004] 95.545: cpuhp_multi_enter: cpu: 0004 target: 169 step: 143 (virtnet_cpu_online)
cpuhp/4-31 [004] 95.546: cpuhp_exit: cpu: 0004 state: 143 step: 143 ret: 0
cpuhp/4-31 [004] 95.547: cpuhp_enter: cpu: 0004 target: 169 step: 144 (mce_cpu_online)
cpuhp/4-31 [004] 95.548: cpuhp_exit: cpu: 0004 state: 144 step: 144 ret: 0
cpuhp/4-31 [004] 95.549: cpuhp_enter: cpu: 0004 target: 169 step: 145 (console_cpu_notify)
cpuhp/4-31 [004] 95.550: cpuhp_exit: cpu: 0004 state: 145 step: 145 ret: 0
cpuhp/4-31 [004] 95.551: cpuhp_enter: cpu: 0004 target: 169 step: 168 (sched_cpu_activate)
cpuhp/4-31 [004] 95.552: cpuhp_exit: cpu: 0004 state: 168 step: 168 ret: 0
bash-394 [005] 95.553: cpuhp_exit: cpu: 0004 state: 169 step: 140 ret: 0
可以看到,CPU4 在時間戳 22.996 之前下線,然後又在 95.552 之前上線。所有呼叫的回撥,包括它們的返回碼,都可以在跟蹤中看到。
架構要求¶
需要以下函式和配置
CONFIG_HOTPLUG_CPU此條目需要在 Kconfig 中啟用
__cpu_up()啟動 CPU 的架構介面
__cpu_disable()關閉 CPU 的架構介面,例程返回後核心不再處理中斷。這包括定時器的關閉。
__cpu_die()這實際上是為了確保 CPU 的死亡。實際上可以看看其他實現 CPU 熱插拔的架構中的一些示例程式碼。處理器從該特定架構的
idle()迴圈中被移除。__cpu_die()通常等待設定某些 `per_cpu` 狀態,以確保處理器死亡例程被呼叫,從而確鑿無疑。
使用者空間通知¶
CPU 成功上線或離線後,會發送 udev 事件。類似於這樣的 udev 規則
SUBSYSTEM=="cpu", DRIVERS=="processor", DEVPATH=="/devices/system/cpu/*", RUN+="the_hotplug_receiver.sh"
將接收所有事件。類似於這樣的指令碼
#!/bin/sh
if [ "${ACTION}" = "offline" ]
then
echo "CPU ${DEVPATH##*/} offline"
elif [ "${ACTION}" = "online" ]
then
echo "CPU ${DEVPATH##*/} online"
fi
可以進一步處理事件。
當系統中 CPU 發生變化時,如果核心自身更新 kdump 捕獲核心的 CPU 列表(透過 elfcorehdr 和其他相關的 kexec 段),則 sysfs 檔案 /sys/devices/system/cpu/crash_hotplug 包含 '1';如果使用者空間必須更新 kdump 捕獲核心的 CPU 列表,則包含 '0'。
可用性取決於 CONFIG_HOTPLUG_CPU 核心配置選項。
為了跳過使用者空間對 kdump 的 CPU 熱插拔/拔事件的處理(即透過解除安裝再重新載入獲取當前 CPU 列表),可以在 udev 規則中按如下方式使用此 sysfs 檔案
SUBSYSTEM==”cpu”, ATTRS{crash_hotplug}==”1”, GOTO=”kdump_reload_end”
對於 CPU 熱插拔/拔事件,如果架構支援核心更新 elfcorehdr(其中包含 CPU 列表)和其他相關的 kexec 段,則該規則會跳過 kdump 捕獲核心的解除安裝再重新載入。
核心內聯文件參考¶
-
int cpuhp_setup_state(enum cpuhp_state state, const char *name, int (*startup)(unsigned int cpu), int (*teardown)(unsigned int cpu))¶
設定熱插拔狀態回撥,並呼叫 **startup** 回撥
引數
enum cpuhp_state state安裝回調的狀態
const char *name回撥的名稱(將用於除錯輸出)
int (*startup)(unsigned int cpu)啟動回撥函式,如果不需要則為 NULL
int (*teardown)(unsigned int cpu)關閉回撥函式,如果不需要則為 NULL
描述
安裝回調函式,並在已達到該**狀態**的線上 CPU 上呼叫 **startup** 回撥。
-
int cpuhp_setup_state_cpuslocked(enum cpuhp_state state, const char *name, int (*startup)(unsigned int cpu), int (*teardown)(unsigned int cpu))¶
從 `cpus_read_lock()` 保持區域呼叫 **startup** 回撥來設定熱插拔狀態回撥
引數
enum cpuhp_state state安裝回調的狀態
const char *name回撥的名稱(將用於除錯輸出)
int (*startup)(unsigned int cpu)啟動回撥函式,如果不需要則為 NULL
int (*teardown)(unsigned int cpu)關閉回撥函式,如果不需要則為 NULL
描述
與 cpuhp_setup_state() 相同,但必須在 `cpus_read_lock()` 保持區域內呼叫。
-
int cpuhp_setup_state_nocalls(enum cpuhp_state state, const char *name, int (*startup)(unsigned int cpu), int (*teardown)(unsigned int cpu))¶
設定熱插拔狀態回撥,但不呼叫 **startup** 回撥
引數
enum cpuhp_state state安裝回調的狀態
const char *name回撥的名稱。
int (*startup)(unsigned int cpu)啟動回撥函式,如果不需要則為 NULL
int (*teardown)(unsigned int cpu)關閉回撥函式,如果不需要則為 NULL
描述
與 cpuhp_setup_state() 相同,但安裝期間不呼叫 **startup** 回撥。如果 SMP=n 或 HOTPLUG_CPU=n 則為無操作。
-
int cpuhp_setup_state_nocalls_cpuslocked(enum cpuhp_state state, const char *name, int (*startup)(unsigned int cpu), int (*teardown)(unsigned int cpu))¶
從 `cpus_read_lock()` 保持區域回撥設定熱插拔狀態回撥,但不呼叫 **startup** 回撥
引數
enum cpuhp_state state安裝回調的狀態
const char *name回撥的名稱。
int (*startup)(unsigned int cpu)啟動回撥函式,如果不需要則為 NULL
int (*teardown)(unsigned int cpu)關閉回撥函式,如果不需要則為 NULL
描述
與 cpuhp_setup_state_nocalls() 相同,但必須在 `cpus_read_lock()` 保持區域內呼叫。
-
int cpuhp_setup_state_multi(enum cpuhp_state state, const char *name, int (*startup)(unsigned int cpu, struct hlist_node *node), int (*teardown)(unsigned int cpu, struct hlist_node *node))¶
為多狀態添加回調
引數
enum cpuhp_state state安裝回調的狀態
const char *name回撥的名稱。
int (*startup)(unsigned int cpu, struct hlist_node *node)啟動回撥函式,如果不需要則為 NULL
int (*teardown)(unsigned int cpu, struct hlist_node *node)關閉回撥函式,如果不需要則為 NULL
描述
設定內部 `multi_instance` 標誌,並準備一個狀態作為多例項回撥工作。此時不呼叫任何回撥。一旦透過 cpuhp_state_add_instance() 或 cpuhp_state_add_instance_nocalls() 註冊了此狀態的例項,就會呼叫回撥。
-
int cpuhp_state_add_instance(enum cpuhp_state state, struct hlist_node *node)¶
為狀態新增例項並呼叫啟動回撥。
引數
enum cpuhp_state state安裝例項的狀態
struct hlist_node *node此單個狀態的節點。
描述
安裝**狀態**的例項,並在已達到該**狀態**的線上 CPU 上呼叫已註冊的啟動回撥。該**狀態**必須之前已被 cpuhp_setup_state_multi() 標記為多例項。
-
int cpuhp_state_add_instance_nocalls(enum cpuhp_state state, struct hlist_node *node)¶
為狀態新增例項,但不呼叫啟動回撥。
引數
enum cpuhp_state state安裝例項的狀態
struct hlist_node *node此單個狀態的節點。
描述
安裝**狀態**的例項。該**狀態**必須之前已被 `cpuhp_setup_state_multi` 標記為多例項。如果 SMP=n 或 HOTPLUG_CPU=n 則為無操作。
-
int cpuhp_state_add_instance_nocalls_cpuslocked(enum cpuhp_state state, struct hlist_node *node)¶
從 `cpus_read_lock()` 保持區域為狀態新增例項,但不呼叫啟動回撥。
引數
enum cpuhp_state state安裝例項的狀態
struct hlist_node *node此單個狀態的節點。
描述
與 cpuhp_state_add_instance_nocalls() 相同,但必須在 `cpus_read_lock()` 保持區域內呼叫。
-
void cpuhp_remove_state(enum cpuhp_state state)¶
移除熱插拔狀態回撥並呼叫關閉
引數
enum cpuhp_state state移除回撥的狀態
描述
移除回撥函式,並在已達到該**狀態**的線上 CPU 上呼叫關閉回撥。
-
void cpuhp_remove_state_nocalls(enum cpuhp_state state)¶
移除熱插拔狀態回撥,但不呼叫關閉回撥
引數
enum cpuhp_state state移除回撥的狀態
-
void cpuhp_remove_state_nocalls_cpuslocked(enum cpuhp_state state)¶
從 `cpus_read_lock()` 保持區域移除熱插拔狀態回撥,但不呼叫關閉。
引數
enum cpuhp_state state移除回撥的狀態
描述
與 cpuhp_remove_state_nocalls() 相同,但必須在 `cpus_read_lock()` 保持區域內呼叫。
-
void cpuhp_remove_multi_state(enum cpuhp_state state)¶
移除熱插拔多狀態回撥
引數
enum cpuhp_state state移除回撥的狀態
描述
從多狀態中移除回撥函式。這是 cpuhp_setup_state_multi() 的反向操作。在呼叫此函式之前,所有例項都應已移除。
-
int cpuhp_state_remove_instance(enum cpuhp_state state, struct hlist_node *node)¶
從狀態中移除熱插拔例項並呼叫關閉回撥
引數
enum cpuhp_state state移除例項的狀態
struct hlist_node *node此單個狀態的節點。
描述
移除例項,並在已達到**狀態**的線上 CPU 上呼叫關閉回撥。
-
int cpuhp_state_remove_instance_nocalls(enum cpuhp_state state, struct hlist_node *node)¶
從狀態中移除熱插拔例項,但不呼叫關閉回撥
引數
enum cpuhp_state state移除例項的狀態
struct hlist_node *node此單個狀態的節點。
描述
移除例項,但不呼叫關閉回撥。