1.. include:: ../disclaimer-zh_CN.rst 2 3:Original: Documentation/core-api/cpu_hotplug.rst 4:翻译: 5 6 司延腾 Yanteng Si <siyanteng@loongson.cn> 7 8:校译: 9 10 吴想成 Wu XiangCheng <bobwxc@email.cn> 11 12.. _cn_core_api_cpu_hotplug: 13 14================= 15内核中的CPU热拔插 16================= 17 18:时间: 2016年12月 19:作者: Sebastian Andrzej Siewior <bigeasy@linutronix.de>, 20 Rusty Russell <rusty@rustcorp.com.au>, 21 Srivatsa Vaddagiri <vatsa@in.ibm.com>, 22 Ashok Raj <ashok.raj@intel.com>, 23 Joel Schopp <jschopp@austin.ibm.com> 24 25简介 26==== 27 28现代系统架构的演进已经在处理器中引入了先进的错误报告和纠正能力。有一些OEM也支 29持可热拔插的NUMA(Non Uniform Memory Access,非统一内存访问)硬件,其中物理 30节点的插入和移除需要支持CPU热插拔。 31 32这样的进步要求内核可用的CPU被移除,要么是出于配置的原因,要么是出于RAS的目的, 33以保持一个不需要的CPU不在系统执行路径。因此需要在Linux内核中支持CPU热拔插。 34 35CPU热拔插支持的一个更新颖的用途是它在SMP的暂停恢复支持中的应用。双核和超线程支 36持使得即使是笔记本电脑也能运行不支持这些方法的SMP内核。 37 38 39命令行开关 40========== 41 42``maxcpus=n`` 43 限制启动时的CPU为 *n* 个。例如,如果你有四个CPU,使用 ``maxcpus=2`` 将只能启 44 动两个。你可以选择稍后让其他CPU上线。 45 46``nr_cpus=n`` 47 限制内核将支持的CPU总量。如果这里提供的数量低于实际可用的CPU数量,那么其他CPU 48 以后就不能上线了。 49 50``additional_cpus=n`` 51 使用它来限制可热插拔的CPU。该选项设置 52 ``cpu_possible_mask = cpu_present_mask + additional_cpus`` 53 54 这个选项只限于IA64架构。 55 56``possible_cpus=n`` 57 这个选项设置 ``cpu_possible_mask`` 中的 ``possible_cpus`` 位。 58 59 这个选项只限于X86和S390架构。 60 61``cpu0_hotplug`` 62 允许关闭CPU0。 63 64 这个选项只限于X86架构。 65 66CPU位图 67======= 68 69``cpu_possible_mask`` 70 系统中可能可用CPU的位图。这是用来为per_cpu变量分配一些启动时的内存,这些变量 71 不会随着CPU的可用或移除而增加/减少。一旦在启动时的发现阶段被设置,该映射就是静态 72 的,也就是说,任何时候都不会增加或删除任何位。根据你的系统需求提前准确地调整它 73 可以节省一些启动时的内存。 74 75``cpu_online_mask`` 76 当前在线的所有CPU的位图。在一个CPU可用于内核调度并准备接收设备的中断后,它被 77 设置在 ``__cpu_up()`` 中。当使用 ``__cpu_disable()`` 关闭一个CPU时,它被清 78 空,在此之前,所有的操作系统服务包括中断都被迁移到另一个目标CPU。 79 80``cpu_present_mask`` 81 系统中当前存在的CPU的位图。它们并非全部在线。当物理热拔插被相关的子系统 82 (如ACPI)处理时,可以改变和添加新的位或从位图中删除,这取决于事件是 83 hot-add/hot-remove。目前还没有定死规定。典型的用法是在启动时启动拓扑结构,这时 84 热插拔被禁用。 85 86你真的不需要操作任何系统的CPU映射。在大多数情况下,它们应该是只读的。当设置每个 87CPU资源时,几乎总是使用 ``cpu_possible_mask`` 或 ``for_each_possible_cpu()`` 88来进行迭代。宏 ``for_each_cpu()`` 可以用来迭代一个自定义的CPU掩码。 89 90不要使用 ``cpumask_t`` 以外的任何东西来表示CPU的位图。 91 92 93使用CPU热拔插 94============= 95 96内核选项 *CONFIG_HOTPLUG_CPU* 需要被启用。它目前可用于多种架构,包括ARM、MIPS、 97PowerPC和X86。配置是通过sysfs接口完成的:: 98 99 $ ls -lh /sys/devices/system/cpu 100 total 0 101 drwxr-xr-x 9 root root 0 Dec 21 16:33 cpu0 102 drwxr-xr-x 9 root root 0 Dec 21 16:33 cpu1 103 drwxr-xr-x 9 root root 0 Dec 21 16:33 cpu2 104 drwxr-xr-x 9 root root 0 Dec 21 16:33 cpu3 105 drwxr-xr-x 9 root root 0 Dec 21 16:33 cpu4 106 drwxr-xr-x 9 root root 0 Dec 21 16:33 cpu5 107 drwxr-xr-x 9 root root 0 Dec 21 16:33 cpu6 108 drwxr-xr-x 9 root root 0 Dec 21 16:33 cpu7 109 drwxr-xr-x 2 root root 0 Dec 21 16:33 hotplug 110 -r--r--r-- 1 root root 4.0K Dec 21 16:33 offline 111 -r--r--r-- 1 root root 4.0K Dec 21 16:33 online 112 -r--r--r-- 1 root root 4.0K Dec 21 16:33 possible 113 -r--r--r-- 1 root root 4.0K Dec 21 16:33 present 114 115文件 *offline* 、 *online* 、*possible* 、*present* 代表CPU掩码。每个CPU文件 116夹包含一个 *online* 文件,控制逻辑上的开(1)和关(0)状态。要在逻辑上关闭CPU4:: 117 118 $ echo 0 > /sys/devices/system/cpu/cpu4/online 119 smpboot: CPU 4 is now offline 120 121一旦CPU被关闭,它将从 */proc/interrupts* 、*/proc/cpuinfo* 中被删除,也不应该 122被 *top* 命令显示出来。要让CPU4重新上线:: 123 124 $ echo 1 > /sys/devices/system/cpu/cpu4/online 125 smpboot: Booting Node 0 Processor 4 APIC 0x1 126 127CPU又可以使用了。这应该对所有的CPU都有效。CPU0通常比较特殊,被排除在CPU热拔插之外。 128在X86上,内核选项 *CONFIG_BOOTPARAM_HOTPLUG_CPU0* 必须被启用,以便能够关闭CPU0。 129或者,可以使用内核命令选项 *cpu0_hotplug* 。CPU0的一些已知的依赖性: 130 131* 从休眠/暂停中恢复。如果CPU0处于离线状态,休眠/暂停将失败。 132* PIC中断。如果检测到PIC中断,CPU0就不能被移除。 133 134如果你发现CPU0上有任何依赖性,请告知Fenghua Yu <fenghua.yu@intel.com>。 135 136CPU的热拔插协作 137=============== 138 139下线情况 140-------- 141 142一旦CPU被逻辑关闭,注册的热插拔状态的清除回调将被调用,从 ``CPUHP_ONLINE`` 开始,在 143``CPUHP_OFFLINE`` 状态结束。这包括: 144 145* 如果任务因暂停操作而被冻结,那么 *cpuhp_tasks_frozen* 将被设置为true。 146 147* 所有进程都会从这个将要离线的CPU迁移到新的CPU上。新的CPU是从每个进程的当前cpuset中 148 选择的,它可能是所有在线CPU的一个子集。 149 150* 所有针对这个CPU的中断都被迁移到新的CPU上。 151 152* 计时器也会被迁移到新的CPU上。 153 154* 一旦所有的服务被迁移,内核会调用一个特定的例程 ``__cpu_disable()`` 来进行特定的清 155 理。 156 157使用热插拔API 158------------- 159 160一旦一个CPU下线或上线,就有可能收到通知。这对某些需要根据可用CPU数量执行某种设置或清 161理功能的驱动程序来说可能很重要:: 162 163 #include <linux/cpuhotplug.h> 164 165 ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "X/Y:online", 166 Y_online, Y_prepare_down); 167 168*X* 是子系统, *Y* 是特定的驱动程序。 *Y_online* 回调将在所有在线CPU的注册过程中被调用。 169如果在线回调期间发生错误, *Y_prepare_down* 回调将在所有之前调用过在线回调的CPU上调 170用。注册完成后,一旦有CPU上线, *Y_online* 回调将被调用,当CPU关闭时, *Y_prepare_down* 171将被调用。所有之前在 *Y_online* 中分配的资源都应该在 *Y_prepare_down* 中释放。如果在 172注册过程中发生错误,返回值 *ret* 为负值。否则会返回一个正值,其中包含动态分配状态 173( *CPUHP_AP_ONLINE_DYN* )的分配热拔插。对于预定义的状态,它将返回0。 174 175该回调可以通过调用 ``cpuhp_remove_state()`` 来删除。如果是动态分配的状态 176( *CPUHP_AP_ONLINE_DYN* ),则使用返回的状态。在移除热插拔状态的过程中,将调用拆解回调。 177 178多个实例 179~~~~~~~~ 180 181如果一个驱动程序有多个实例,并且每个实例都需要独立执行回调,那么很可能应该使用 182``multi-state`` 。首先需要注册一个多状态的状态:: 183 184 ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, "X/Y:online, 185 Y_online, Y_prepare_down); 186 Y_hp_online = ret; 187 188``cpuhp_setup_state_multi()`` 的行为与 ``cpuhp_setup_state()`` 类似,只是它 189为多状态准备了回调,但不调用回调。这是一个一次性的设置。 190一旦分配了一个新的实例,你需要注册这个新实例:: 191 192 ret = cpuhp_state_add_instance(Y_hp_online, &d->node); 193 194这个函数将把这个实例添加到你先前分配的 ``Y_hp_online`` 状态,并在所有在线的 195CPU上调用先前注册的回调( ``Y_online`` )。 *node* 元素是你的每个实例数据结构 196中的一个 ``struct hlist_node`` 成员。 197 198在移除该实例时:: 199 200 cpuhp_state_remove_instance(Y_hp_online, &d->node) 201 202应该被调用,这将在所有在线CPU上调用拆分回调。 203 204手动设置 205~~~~~~~~ 206 207通常情况下,在注册或移除状态时调用setup和teamdown回调是很方便的,因为通常在CPU上线 208(下线)和驱动的初始设置(关闭)时需要执行该操作。然而,每个注册和删除功能也有一个 209_nocalls的后缀,如果不希望调用回调,则不调用所提供的回调。在手动设置(或关闭)期间, 210应该使用 ``get_online_cpus()`` 和 ``put_online_cpus()`` 函数来抑制CPU热插拔操作。 211 212 213事件的顺序 214---------- 215 216热插拔状态被定义在 ``include/linux/cpuhotplug.h``: 217 218* ``CPUHP_OFFLINE`` ... ``CPUHP_AP_OFFLINE`` 状态是在CPU启动前调用的。 219 220* ``CPUHP_AP_OFFLINE`` ... ``CPUHP_AP_ONLINE`` 状态是在CPU被启动后被调用的。 221 中断是关闭的,调度程序还没有在这个CPU上活动。从 ``CPUHP_AP_OFFLINE`` 开始, 222 回调被调用到目标CPU上。 223 224* ``CPUHP_AP_ONLINE_DYN`` 和 ``CPUHP_AP_ONLINE_DYN_END`` 之间的状态被保留 225 给动态分配。 226 227* 这些状态在CPU关闭时以相反的顺序调用,从 ``CPUHP_ONLINE`` 开始,在 ``CPUHP_OFFLINE`` 228 停止。这里的回调是在将被关闭的CPU上调用的,直到 ``CPUHP_AP_OFFLINE`` 。 229 230通过 ``CPUHP_AP_ONLINE_DYN`` 动态分配的状态通常已经足够了。然而,如果在启动或关闭 231期间需要更早的调用,那么应该获得一个显式状态。如果热拔插事件需要相对于另一个热拔插事 232件的特定排序,也可能需要一个显式状态。 233 234测试热拔插状态 235============== 236 237验证自定义状态是否按预期工作的一个方法是关闭一个CPU,然后再把它上线。也可以把CPU放到某 238些状态(例如 ``CPUHP_AP_ONLINE`` ),然后再回到 ``CPUHP_ONLINE`` 。这将模拟在 239``CPUHP_AP_ONLINE`` 之后的一个状态出现错误,从而导致回滚到在线状态。 240 241所有注册的状态都被列举在 ``/sys/devices/system/cpu/hotplug/states`` :: 242 243 $ tail /sys/devices/system/cpu/hotplug/states 244 138: mm/vmscan:online 245 139: mm/vmstat:online 246 140: lib/percpu_cnt:online 247 141: acpi/cpu-drv:online 248 142: base/cacheinfo:online 249 143: virtio/net:online 250 144: x86/mce:online 251 145: printk:online 252 168: sched:active 253 169: online 254 255要将CPU4回滚到 ``lib/percpu_cnt:online`` ,再回到在线状态,只需发出:: 256 257 $ cat /sys/devices/system/cpu/cpu4/hotplug/state 258 169 259 $ echo 140 > /sys/devices/system/cpu/cpu4/hotplug/target 260 $ cat /sys/devices/system/cpu/cpu4/hotplug/state 261 140 262 263需要注意的是,状态140的清除回调已经被调用。现在重新上线:: 264 265 $ echo 169 > /sys/devices/system/cpu/cpu4/hotplug/target 266 $ cat /sys/devices/system/cpu/cpu4/hotplug/state 267 169 268 269启用追踪事件后,单个步骤也是可见的:: 270 271 # TASK-PID CPU# TIMESTAMP FUNCTION 272 # | | | | | 273 bash-394 [001] 22.976: cpuhp_enter: cpu: 0004 target: 140 step: 169 (cpuhp_kick_ap_work) 274 cpuhp/4-31 [004] 22.977: cpuhp_enter: cpu: 0004 target: 140 step: 168 (sched_cpu_deactivate) 275 cpuhp/4-31 [004] 22.990: cpuhp_exit: cpu: 0004 state: 168 step: 168 ret: 0 276 cpuhp/4-31 [004] 22.991: cpuhp_enter: cpu: 0004 target: 140 step: 144 (mce_cpu_pre_down) 277 cpuhp/4-31 [004] 22.992: cpuhp_exit: cpu: 0004 state: 144 step: 144 ret: 0 278 cpuhp/4-31 [004] 22.993: cpuhp_multi_enter: cpu: 0004 target: 140 step: 143 (virtnet_cpu_down_prep) 279 cpuhp/4-31 [004] 22.994: cpuhp_exit: cpu: 0004 state: 143 step: 143 ret: 0 280 cpuhp/4-31 [004] 22.995: cpuhp_enter: cpu: 0004 target: 140 step: 142 (cacheinfo_cpu_pre_down) 281 cpuhp/4-31 [004] 22.996: cpuhp_exit: cpu: 0004 state: 142 step: 142 ret: 0 282 bash-394 [001] 22.997: cpuhp_exit: cpu: 0004 state: 140 step: 169 ret: 0 283 bash-394 [005] 95.540: cpuhp_enter: cpu: 0004 target: 169 step: 140 (cpuhp_kick_ap_work) 284 cpuhp/4-31 [004] 95.541: cpuhp_enter: cpu: 0004 target: 169 step: 141 (acpi_soft_cpu_online) 285 cpuhp/4-31 [004] 95.542: cpuhp_exit: cpu: 0004 state: 141 step: 141 ret: 0 286 cpuhp/4-31 [004] 95.543: cpuhp_enter: cpu: 0004 target: 169 step: 142 (cacheinfo_cpu_online) 287 cpuhp/4-31 [004] 95.544: cpuhp_exit: cpu: 0004 state: 142 step: 142 ret: 0 288 cpuhp/4-31 [004] 95.545: cpuhp_multi_enter: cpu: 0004 target: 169 step: 143 (virtnet_cpu_online) 289 cpuhp/4-31 [004] 95.546: cpuhp_exit: cpu: 0004 state: 143 step: 143 ret: 0 290 cpuhp/4-31 [004] 95.547: cpuhp_enter: cpu: 0004 target: 169 step: 144 (mce_cpu_online) 291 cpuhp/4-31 [004] 95.548: cpuhp_exit: cpu: 0004 state: 144 step: 144 ret: 0 292 cpuhp/4-31 [004] 95.549: cpuhp_enter: cpu: 0004 target: 169 step: 145 (console_cpu_notify) 293 cpuhp/4-31 [004] 95.550: cpuhp_exit: cpu: 0004 state: 145 step: 145 ret: 0 294 cpuhp/4-31 [004] 95.551: cpuhp_enter: cpu: 0004 target: 169 step: 168 (sched_cpu_activate) 295 cpuhp/4-31 [004] 95.552: cpuhp_exit: cpu: 0004 state: 168 step: 168 ret: 0 296 bash-394 [005] 95.553: cpuhp_exit: cpu: 0004 state: 169 step: 140 ret: 0 297 298可以看到,CPU4一直下降到时间戳22.996,然后又上升到95.552。所有被调用的回调, 299包括它们的返回代码都可以在跟踪中看到。 300 301架构的要求 302========== 303 304需要具备以下功能和配置: 305 306``CONFIG_HOTPLUG_CPU`` 307 这个配置项需要在Kconfig中启用 308 309``__cpu_up()`` 310 调出一个cpu的架构接口 311 312``__cpu_disable()`` 313 关闭CPU的架构接口,在此程序返回后,内核不能再处理任何中断。这包括定时器的关闭。 314 315``__cpu_die()`` 316 这实际上是为了确保CPU的死亡。实际上,看看其他架构中实现CPU热拔插的一些示例代 317 码。对于那个特定的架构,处理器被从 ``idle()`` 循环中拿下来。 ``__cpu_die()`` 318 通常会等待一些per_cpu状态的设置,以确保处理器的死亡例程被调用来保持活跃。 319 320用户空间通知 321============ 322 323在CPU成功上线或下线后,udev事件被发送。一个udev规则,比如:: 324 325 SUBSYSTEM=="cpu", DRIVERS=="processor", DEVPATH=="/devices/system/cpu/*", RUN+="the_hotplug_receiver.sh" 326 327将接收所有事件。一个像这样的脚本:: 328 329 #!/bin/sh 330 331 if [ "${ACTION}" = "offline" ] 332 then 333 echo "CPU ${DEVPATH##*/} offline" 334 335 elif [ "${ACTION}" = "online" ] 336 then 337 echo "CPU ${DEVPATH##*/} online" 338 339 fi 340 341可以进一步处理该事件。 342 343内核内联文档参考 344================ 345 346该API在以下内核代码中: 347 348include/linux/cpuhotplug.h 349