Linux内核机制—cpu_hotplug-1-cpu_hotplug.rst翻译
一、cpu_hotplug.rst
注: 翻译自 Documentation/core-api/cpu_hotplug.rst
==========================
内核中的 CPU 热插拔
==========================
日期:2016 年 12 月
作者: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>
1. 引言
现代系统架构的进步为处理器引入了更先进的错误报告和纠正功能。一些OEM厂商支持NUMA硬件,这些硬件也支持热插拔,而物理节点的插入和移除需要CPU热插拔支持。
这些进步要求内核移除可用的CPU,无论是出于资源配置的原因,还是出于RAS(资源可用性系统)的目的,以避免出现问题的CPU进入系统执行路径。因此,Linux内核需要支持CPU热插拔。
CPU热插拔支持的一个更新颖的应用是它如今在SMP(同步多处理器)的挂起/恢复支持中的应用。双核和超线程(HT)的支持使得即使是笔记本电脑也能运行以前不支持这些方法的SMP内核。
2. 命令行开关
(1) maxcpus=n
定义在 kernel/smp.c 中。
限制启动时使用的 CPU 数量为 *n*。例如,如果您有四个 CPU,使用 maxcpus=2 将只启动两个。您可以选择稍后启用其他 CPU。
(2) nr_cpus=n
定义在 kernel/smp.c 中。
限制内核支持的 CPU 总数。如果此处提供的数字小于物理上可用的 CPU 数量,则这些 CPU 将稍后也无法启用。
(3) additional_cpus=n
定义在 arch/ia64/kernel/acpi.c 中。
使用此选项限制可热插拔的 CPU 数量。此选项设置 cpu_possible_mask = cpu_present_mask + additional_cpus。
此选项仅限于 IA64 架构。
(4) possible_cpus=n
定义在 arch/x86/kernel/smpboot.c 和 arch/s390/kernel/smp.c 中,看起来 Arm64 并不支持。
此选项设置 cpu_possible_mask 中的 possible_cpus 位。
此选项仅限于 X86 和 S390 架构。
(5) cede_offline={"off","on"}``
定义在 arch/powerpc/platforms/pseries/hotplug-cpu.c 中。
在支持的 p 系列平台上,使用此选项可禁用/启用将离线处理器置于扩展的 H_CEDE 状态。如果未指定任何值,则 cede_offline 设置为 “on”。
此选项仅限于 PowerPC 架构。
(6) cpu0_hotplug 允许关闭 CPU0。
此选项仅限于 X86 架构。
2. CPU 映射
(1) cpu_possible_mask
系统中所有可能可用的 CPU 的位图。它用于在启动时为每个 CPU 分配一些内存,这些变量不会随着 CPU 的可用或移除而增长/缩小。一旦在启动时发现阶段设置,该映射就保持不变,即不会再添加或删除任何位。预先根据系统需求精确修剪该映射可以节省一些启动时内存。
(2) cpu_online_mask
所有当前在线 CPU 的位图。在 CPU 可供内核调度并准备好接收来自设备的中断后,它会在 __cpu_up() 中设置。当使用 __cpu_disable() 关闭 CPU 时,它会被清除,在此之前,所有操作系统服务(包括中断)都会迁移到另一个目标 CPU。
(3) cpu_present_mask
系统中当前存在的 CPU 位图。并非所有 CPU 都处于在线状态。当相关子系统(例如 ACPI)处理物理热插拔时,该位图可能会发生变化,根据事件是热添加还是热移除,可能会添加或移除新的位。目前没有锁定规则。典型用途是在启动期间初始化拓扑结构,此时热插拔功能被禁用。
实际上,您无需操作任何系统 CPU 位图。在大多数情况下,它们应该是只读的。设置每个 CPU 的资源时,几乎总是使用 cpu_possible_mask 或 for_each_possible_cpu() 进行迭代。宏 for_each_cpu() 可用于迭代自定义 CPU 位图掩码。
切勿使用 cpumask_t 以外的任何值来表示 CPU 位图。
3. 使用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
TODO: active 掩码哪里有打印,与调度的互斥状态是什么?
文件 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 重新online:
$ echo 1 > /sys/devices/system/cpu/cpu4/online
smpboot: Booting Node 0 Processor 4 APIC 0x1
CPU 恢复可用。此方法适用于所有 CPU。CPU0 通常比较特殊,不包含在 CPU 热插拔功能中####。在 X86 架构上,必须启用内核选项 CONFIG_BOOTPARAM_HOTPLUG_CPU0 才能关闭 CPU0(Arm64上没有这个)。或者,也可以使用内核命令选项 cpu0_hotplug。CPU0 的一些已知依赖项:
a. 从休眠/挂起状态恢复。如果 CPU0 离线,休眠/挂起操作将失败。
b. PIC 中断。如果检测到 PIC 中断,则无法移除 CPU0。
如果您发现 CPU0 存在任何依赖项,请联系 Fenghua Yu <fenghua.yu@intel.com>。
补充:
# ls /sys/devices/system/cpu/cpu4/ -lh drwxr-xr-x 2 root root 0 2025-12-20 11:11 core_ctl -r--r--r-- 1 root root 4.0K 2025-12-20 11:11 cpu_capacity lrwxrwxrwx 1 root root 0 2025-12-20 11:11 cpufreq -> ../cpufreq/policy4 drwxr-xr-x 2 root root 0 2025-12-20 11:10 hotplug -r--r--r-- 1 root root 4.0K 2025-12-20 11:11 isolate lrwxrwxrwx 1 root root 0 2025-12-20 11:11 of_node -> ../../../../firmware/devicetree/base/cpus/cpu@4 -rw-r--r-- 1 root root 4.0K 2025-12-20 11:11 online drwxr-xr-x 2 root root 0 2025-12-20 11:11 power drwxr-xr-x 3 root root 0 2025-12-20 11:11 regs -rw-r--r-- 1 root root 4.0K 2025-12-20 11:11 sched_load_boost lrwxrwxrwx 1 root root 0 2025-12-20 11:11 subsystem -> ../../../../bus/cpu drwxr-xr-x 2 root root 0 2025-12-20 11:11 topology -rw-r--r-- 1 root root 4.0K 2025-12-20 11:11 uevent -r--r--r-- 1 root root 4.0K 2025-12-20 11:11 waiting_for_supplier # ls -lh /sys/devices/system/cpu/cpu4/hotplug -rw-r--r-- 1 root root 4.0K 2025-12-20 11:11 fail -r--r--r-- 1 root root 4.0K 2025-12-20 11:10 state -rw-r--r-- 1 root root 4.0K 2025-12-20 11:11 target
4. CPU 热插拔协调
(1) offline 情况
一旦 CPU 逻辑 shutdown,已注册的热插拔状态的 teardown 回调函数将被调用,从 CPUHP_ONLINE 状态开始,到 CPUHP_OFFLINE 状态结束。这包括:
a. 如果任务由于挂起操作而被冻结,则 cpuhp_tasks_frozen 将被设置为 true。
b. 所有进程都将从该 CPU 迁移到新的 CPU。新的 CPU 将从每个进程当前的cpuset中选择,该 cpuset集可能是所有在线 CPU 的子集。
c. 所有指向该 CPU 的中断都将迁移到新的 CPU。
d. 定时器也将迁移到新的 CPU。
e. 所有服务迁移完成后,内核将调用特定于架构的例程 __cpu_disable() 来执行特定于架构的清理工作。
(2) 使用热插拔 API
当 CPU offline 或 onlined 时,可以接收通知。这对于某些需要根据可用 CPU 数量执行某种设置或清理功能的驱动程序来说可能很重要:
#include <linux/cpuhotplug.h> ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "X/Y:online", Y_online, Y_prepare_down);
注: 静态初始化的 cpuhp_hp_states[] 数组里面的回调还可能会变!
X 是子系统,Y 是特定驱动程序。Y_online 回调函数会在注册期间对所有 online CPU 调用。如果 online 回调函数调用期间发生错误,则会对所有之前调用过该回调函数的 CPU 调用 Y_prepare_down 回调函数。注册完成后,CPU online 时会调用 Y_online 回调函数,CPU shutdown 时会调用 Y_prepare_down 回调函数。Y_prepare_down 回调函数会释放之前在 Y_online 回调函数中分配的所有资源。
如果在注册过程中发生错误,则返回值 ret 为负数。否则,返回一个正值,其中包含动态分配状态 (CPUHP_AP_ONLINE_DYN) 的热插拔资源。对于预定义状态,返回值为零。
可以通过调用 cpuhp_remove_state() 来移除回调函数。对于动态分配的状态(CPUHP_AP_ONLINE_DYN),使用返回的状态。移除热插拔状态时,将调用 teardown 回调函数。
(3) 多个实例
如果一个驱动程序有多个实例,并且每个实例都需要独立执行回调,那么很可能需要使用“multi-state”。首先需要注册一个多状态:
ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, "X/Y:online, Y_online, Y_prepare_down); Y_hp_online = ret;
cpuhp_setup_state_multi() 的行为与 cpuhp_setup_state() 类似,区别在于前者会为多状态准备回调函数,但不会实际调用这些回调函数。这是一个一次性设置。
分配新实例后,您需要注册该新实例:
ret = cpuhp_state_add_instance(Y_hp_online, &d->node);
此函数会将此实例添加到您之前分配的 Y_hp_online 状态中,并在所有 online CPU 上调用之前注册的回调函数 (Y_online)。node 元素是您每个实例数据结构的 struct hlist_node 成员。
移除实例时:
cpuhp_state_remove_instance(Y_hp_online, &d->node)
应调用此函数,这将在所有 oneline CPU 上调用 teardown 回调函数。
(4) 手动设置
通常,在注册或移除状态时调用 setup 和 teardown 回调函数会很方便,因为通常需要在 CPU online (offline) 后以及驱动程序初始 setup (shutdown) 期间执行此操作。但是,每个注册和移除函数也都有 _nocalls 后缀,如果不需要调用回调函数,则不会调用提供的回调函数。在手动 setup (or teardown) 期间,应使用 get_online_cpus() 和 put_online_cpus() 函数来禁止 CPU 热插拔操作####。
(5) 事件顺序
热插拔状态定义在 include/linux/cpuhotplug.h 中:
a. CPUHP_OFFLINE … CPUHP_AP_OFFLINE 状态在 CPU 启动之前调用。
b. CPUHP_AP_OFFLINE … CPUHP_AP_ONLINE 状态在 CPU 启动后立即调用。此时中断已关闭,调度器尚未在该 CPU 上激活。从 CPUHP_AP_OFFLINE 开始,回调函数在目标 CPU 上调用。
c. CPUHP_AP_ONLINE_DYN 和 CPUHP_AP_ONLINE_DYN_END 之间的状态保留用于动态分配。
d. CPU 关闭时,状态按相反顺序调用,从 CPUHP_ONLINE 开始,到 CPUHP_OFFLINE 结束。此时,回调函数在即将关闭的 CPU 上调用,直到 CPUHP_AP_OFFLINE。
通常情况下,通过 CPUHP_AP_ONLINE_DYN 动态分配的状态就足够了。但是,如果需要在 bring up 或 shutdown 期间提前调用,则应显式获取状态。如果热插拔事件相对于另一个热插拔事件有特定的顺序要求,也可能需要显式获取状态。
5. 热插拔状态测试
验证自定义状态是否按预期工作的一种方法是 shutdown CPU,然后再将其重新 online。也可以将 CPU 置于某个特定状态(例如 CPUHP_AP_ONLINE),然后再回滚到 CPUHP_ONLINE。这将模拟 CPUHP_AP_ONLINE 之后的一个状态发生错误,从而导致回滚到 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 并重新 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 的清理回调已被调用。
现在重新 online:
$ 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 之前一直处于down状态,然后在时间戳 95.552 之前恢复up状态。所有调用的回调函数及其返回码都可以在跟踪记录中看到。
6. 架构要求
需要以下函数和配置:
CONFIG_HOTPLUG_CPU 需要在 Kconfig 中启用此条目
__cpu_up() 用于启动 CPU 的架构接口。
__cpu_disable() 用于关闭 CPU 的架构接口,此例程返回后内核将不再处理任何中断。这包括关闭定时器。
__cpu_die() 此函数旨在确保 CPU 完全停止运行。可以参考其他架构中实现 CPU 热插拔的示例代码。对于特定架构,处理器是从 idle() 循环中关闭的。__cpu_die() 通常会等待某个 CPU 状态被设置,以确保调用处理器 dead 例程来彻底关闭 CPU。
7. 用户空间通知
CPU 成功上线或下线后,会发送 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
可以进一步处理该事件。
8. 内核内置文档参考
.. kernel-doc:: include/linux/cpuhotplug.h
posted on 2025-12-20 11:23 Hello-World3 阅读(0) 评论(0) 收藏 举报
浙公网安备 33010602011771号