CPU热插拔在内核中的支持 【ChatGPT】

发布时间 2023-12-09 17:38:41作者: 摩斯电码

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

简介

在现代系统架构中,处理器引入了先进的错误报告和修正功能。有一些支持NUMA硬件的OEM,这些硬件也支持热插拔,物理节点的插入和移除需要对CPU进行热插拔的支持。

这些先进技术要求内核中的CPU可以被移除,无论是出于配置原因,还是出于RAS目的,以保持有问题的CPU不会影响系统执行路径。因此,Linux内核需要支持CPU热插拔。

CPU热插拔支持的一个更新颖的用途是它在今天用于SMP的挂起恢复支持。双核和超线程支持使得即使是笔记本电脑也可以运行不支持这些方法的SMP内核。

命令行开关

  • maxcpus=n:限制启动时的CPU数量为n。例如,如果有四个CPU,使用maxcpus=2将只启动两个CPU。您可以选择稍后在线启动其他CPU。
  • nr_cpus=n:限制内核支持的CPU总数。如果此处提供的数字低于物理可用的CPU数量,则这些CPU将无法稍后在线启动。
  • additional_cpus=n:用于限制可热插拔的CPU数量。此选项设置cpu_possible_mask = cpu_present_mask + additional_cpus。此选项仅适用于IA64架构。
  • possible_cpus=n:此选项设置cpu_possible_mask中的可能CPU位。此选项仅适用于X86和S390架构。
  • cpu0_hotplug:允许关闭CPU0。此选项仅适用于X86架构。

CPU映射

  • cpu_possible_mask:可能在系统中可用的CPU的位图。这用于为不会随着CPU的可用性或移除而增长/缩小的per_cpu变量分配一些启动时内存。一旦在启动时发现阶段设置,该映射就是静态的,即任何时候都不会添加或删除位。在系统启动时准确地调整它以满足您的系统需求可以节省一些启动时内存。
  • cpu_online_mask:当前在线的所有CPU的位图。在__cpu_up()中设置,表示CPU已经可用于内核调度并准备好接收来自设备的中断。在使用__cpu_disable()将CPU关闭之前,它会在将CPU带下线之前清除,此时所有操作系统服务,包括中断,都会迁移到另一个目标CPU。
  • cpu_present_mask:当前存在于系统中的CPU的位图。并非所有CPU都可能在线。当相关子系统(例如ACPI)处理物理热插拔时,可以根据事件是热插入还是热拔出来更改并从映射中添加或删除新位。目前没有锁定规则。典型的用法是在启动时初始化拓扑,此时热插拔被禁用。

您实际上不需要操作任何系统CPU映射。对于大多数用途,它们应该是只读的。在设置per-cpu资源时,几乎总是使用cpu_possible_maskfor_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

文件offlineonlinepossiblepresent表示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逻辑关闭,将调用已注册的热插拔状态的拆卸回调,从CPUHP_ONLINE开始,直到状态CPUHP_OFFLINE终止。这包括:

  • 如果由于挂起操作而冻结任务,则cpuhp_tasks_frozen将设置为true。
  • 将所有进程从此即将下线的CPU迁移到新的CPU。新的CPU是从每个进程的当前cpuset中选择的,这可能是所有在线CPU的子集。
  • 将所有定向到此CPU的中断迁移到新的CPU。
  • 定时器也迁移到新的CPU。
  • 一旦所有服务都迁移完成,内核调用特定于体系结构的例程__cpu_disable()执行特定于体系结构的清理。

CPU热插拔API

CPU热插拔状态机

CPU热插拔使用一个简单的状态机,其状态空间从CPUHP_OFFLINE到CPUHP_ONLINE。每个状态都有一个启动和一个拆卸回调。

当CPU上线时,启动回调按顺序调用,直到达到状态CPUHP_ONLINE。它们也可以在设置状态的回调或将实例添加到多实例状态时调用。

当CPU下线时,拆卸回调按相反顺序顺序调用,直到达到状态CPUHP_OFFLINE。它们也可以在移除状态的回调或从多实例状态中移除实例时调用。

如果使用站点仅需要在热插拔操作的一个方向(CPU上线或CPU下线)中的回调,则当设置状态时,另一个不需要的回调可以设置为NULL。

状态空间分为三个部分:

  • 准备部分

    • 准备部分覆盖了从CPUHP_OFFLINE到CPUHP_BRINGUP_CPU的状态空间。
    • 在CPU上线操作期间,此部分中的启动回调在CPU启动之前被调用。在CPU下线操作期间,拆卸回调在CPU变得不正常之后被调用。
    • 由于它们显然无法在热插拔的CPU上运行(该CPU要么尚未启动,要么已经变得不正常),这些回调在控制CPU上调用。
    • 启动回调用于设置成功将CPU在线所需的资源。拆卸回调用于释放资源或在热插拔的CPU变得不正常后将挂起的工作移动到在线CPU。
    • 启动回调允许失败。如果回调失败,CPU上线操作将中止,并将CPU带回到先前的状态(通常是CPUHP_OFFLINE)。
    • 此部分中的拆卸回调不允许失败。
  • 启动部分

    • 启动部分覆盖了从CPUHP_BRINGUP_CPU + 1到CPUHP_AP_ONLINE的状态空间。
    • 在早期CPU设置代码中,在CPU上线操作期间,此部分中的启动回调在关闭中断的热插拔的CPU上被调用。在CPU下线操作期间,在热插拔的CPU上完全关闭之前,拆卸回调在关闭中断的热插拔的CPU上被调用。
    • 此部分中的回调不允许失败。
    • 这些回调用于低级硬件初始化/关闭和核心子系统。
  • 在线部分

    • 在线部分覆盖了从CPUHP_AP_ONLINE + 1到CPUHP_ONLINE的状态空间。
    • 在CPU上线操作期间,在热插拔的CPU上调用此部分中的启动回调。在CPU下线操作期间,在热插拔的CPU上调用此部分中的拆卸回调。
    • 回调在每个CPU热插拔线程的上下文中调用,该线程被固定在热插拔的CPU上。回调在启用中断和抢占的情况下调用。
    • 回调允许失败。当回调失败时,热插拔操作将中止,并且CPU将返回到先前的状态。

CPU上线/下线操作

成功的上线操作如下:

[CPUHP_OFFLINE]
[CPUHP_OFFLINE + 1]->startup()       -> 成功
[CPUHP_OFFLINE + 2]->startup()       -> 成功
[CPUHP_OFFLINE + 3]                  -> 跳过,因为startup == NULL
...
[CPUHP_BRINGUP_CPU]->startup()       -> 成功
=== 准备部分结束
[CPUHP_BRINGUP_CPU + 1]->startup()   -> 成功
...
[CPUHP_AP_ONLINE]->startup()         -> 成功
=== 启动部分结束
[CPUHP_AP_ONLINE + 1]->startup()     -> 成功
...
[CPUHP_ONLINE - 1]->startup()        -> 成功
[CPUHP_ONLINE]

成功的下线操作如下:

[CPUHP_ONLINE]
[CPUHP_ONLINE - 1]->teardown()       -> 成功
...
[CPUHP_AP_ONLINE + 1]->teardown()    -> 成功
=== 启动部分开始
[CPUHP_AP_ONLINE]->teardown()        -> 成功
...
[CPUHP_BRINGUP_ONLINE - 1]->teardown()
...
=== 准备部分开始
[CPUHP_BRINGUP_CPU]->teardown()
[CPUHP_OFFLINE + 3]->teardown()
[CPUHP_OFFLINE + 2]                  -> 跳过,因为teardown == NULL
[CPUHP_OFFLINE + 1]->teardown()
[CPUHP_OFFLINE]

失败的上线操作如下:

[CPUHP_OFFLINE]
[CPUHP_OFFLINE + 1]->startup()       -> 成功
[CPUHP_OFFLINE + 2]->startup()       -> 成功
[CPUHP_OFFLINE + 3]                  -> 跳过,因为startup == NULL
...
[CPUHP_BRINGUP_CPU]->startup()       -> 成功
=== 准备部分结束
[CPUHP_BRINGUP_CPU + 1]->startup()   -> 成功
...
[CPUHP_AP_ONLINE]->startup()         -> 成功
=== 启动部分结束
[CPUHP_AP_ONLINE + 1]->startup()     -> 成功
---
[CPUHP_AP_ONLINE + N]->startup()     -> 失败
[CPUHP_AP_ONLINE + (N - 1)]->teardown()
...
[CPUHP_AP_ONLINE + 1]->teardown()
=== 启动部分开始
[CPUHP_AP_ONLINE]->teardown()
...
[CPUHP_BRINGUP_ONLINE - 1]->teardown()
...
=== 准备部分开始
[CPUHP_BRINGUP_CPU]->teardown()
[CPUHP_OFFLINE + 3]->teardown()
[CPUHP_OFFLINE + 2]                  -> 跳过,因为teardown == NULL
[CPUHP_OFFLINE + 1]->teardown()
[CPUHP_OFFLINE]

失败的下线操作如下:

[CPUHP_ONLINE]
[CPUHP_ONLINE - 1]->teardown()       -> 成功
...
[CPUHP_ONLINE - N]->teardown()       -> 失败
[CPUHP_ONLINE - (N - 1)]->startup()
...
[CPUHP_ONLINE - 1]->startup()
[CPUHP_ONLINE]

无法合理处理递归失败。看下面的例子,由于失败的下线操作而导致递归失败:

[CPUHP_ONLINE]
[CPUHP_ONLINE - 1]->teardown()       -> 成功
...
[CPUHP_ONLINE - N]->teardown()       -> 失败
[CPUHP_ONLINE - (N - 1)]->startup()  -> 成功
[CPUHP_ONLINE - (N - 2)]->startup()  -> 失败

CPU热插拔状态机在此处停止,并且不尝试再次下降,因为这可能导致无限循环:

[CPUHP_ONLINE - (N - 1)]->teardown() -> 成功
[CPUHP_ONLINE - N]->teardown()       -> 失败
[CPUHP_ONLINE - (N - 1)]->startup()  -> 成功
[CPUHP_ONLINE - (N - 2)]->startup()  -> 失败
[CPUHP_ONLINE - (N - 1)]->teardown() -> 成功
[CPUHP_ONLINE - N]->teardown()       -> 失败

反复如此。在这种情况下,CPU处于状态:

[CPUHP_ONLINE - (N - 1)]

这至少让系统取得进展,并且让用户有机会调试甚至解决问题。

分配 CPU 热插拔状态

静态分配

静态分配必须在子系统或驱动程序与其他 CPU 热插拔状态存在顺序要求时使用。例如,在 CPU 上线操作期间,必须在 PERF 驱动程序启动回调之前调用 PERF 核心启动回调。在 CPU 下线操作期间,必须在核心拆除回调之前调用驱动程序拆除回调。静态分配的状态由 cpuhp_state 枚举中的常量描述,该枚举可以在 include/linux/cpuhotplug.h 中找到。

  • 将状态常量插入到枚举中的适当位置,以满足顺序要求。状态常量必须用于状态设置和移除。
  • 当状态回调在运行时未设置并且是内核/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)

cpuhp_setup_state_multi(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部分中不提供启动回调的状态

@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()安装回调并调用@startup回调(如果不为NULL)对当前状态大于新安装状态的所有在线CPU进行调用。根据状态部分,回调在当前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()删除回调并调用拆卸回调(如果不为NULL)对当前状态大于被移除状态的所有在线CPU进行调用。根据状态部分,回调在当前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的指针。指针被传递给多实例状态回调,并可以被回调使用container_of()来检索实例。

这些函数在安装回调的方式上有所不同:

  • cpuhp_state_add_instance_nocalls()仅将实例添加到多实例状态的节点列表中。

  • cpuhp_state_add_instance()添加实例并调用与@state关联的启动回调(如果不为NULL)对当前状态大于@state的所有在线CPU进行调用。回调仅对要添加的实例进行调用。根据状态部分,回调在当前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关联的拆卸回调(如果不为NULL)对当前状态大于@state的所有在线CPU进行调用。回调仅对要移除的实例进行调用。根据状态部分,回调在当前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);
....
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)
 ...

如图所示,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 发生更改时,sysfs 文件 /sys/devices/system/cpu/crash_hotplug 包含 '1',如果内核通过 elfcorehdr 更新 kdump 捕获内核的 CPU 列表,则为 '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 列表),则规则将跳过卸载然后重新加载 kdump 捕获内核。

内核内联文档

https://www.kernel.org/doc/html/v6.6/core-api/cpu_hotplug.html#kernel-inline-documentations-reference