sched discussion
[PATCH v4 00/11] Dynamic Housekeeping Management (DHM) via CPUSets
LLM 分析
sched/isolation 与 cgroup/cpuset:DHM v4 运行时 housekeeping 管理
系列概况
- 标题:
[PATCH v4 00/11] Dynamic Housekeeping Management (DHM) via CPUSets - 作者:Jing Wu
<realwujing@gmail.com>;Qiliang Yuan 共同开发并签署补丁 - 版本:v4
- 规模:1 封 cover letter、11 个 patch,共 12 封邮件
- 修改文件:涉及
sched/isolation、cpuset、context_tracking、RCU NOCB、tick/nohz、watchdog、Hyper-V、arm64 topology、cgroup 文档和 selftests,共 20 个文件 - 代码统计:1181 行新增、65 行删除
- Message-ID:
20260710-wujing-dhm-v4-0-2e912e5d9645@gmail.com - 完整性:输入包含 cover letter 和 11/11 patch;部分邮件中的 diff 被整理或截断,尤其 patch 01 和 patch 11,因此细节分析以可见代码、commit message 和 cover letter 为限。基线提交为
eb3f4b7426cfd2b79d65b7d37155480b32259a11
补丁目的
DHM 希望把过去主要由启动参数确定的 kernel-noise housekeeping 隔离,接入现有的 cgroup v2 cpuset isolated partition,使管理员能够在运行时动态完成:
nohz_full调度 tick 抑制;- RCU callback NOCB offload;
- managed IRQ 迁移;
- watchdog 从隔离 CPU 上撤出;
- 销毁 isolated partition 后恢复启动时的 housekeeping 配置。
该系列不新增 cgroup 文件或其他用户空间 ABI。用户仍然只需向 cpuset.cpus.partition 写入 isolated。
其中最明确的“零启动参数”能力是:即使启动时没有 nohz_full= 和 rcu_nocbs=,也可以在首次运行时隔离时懒初始化 tick 和 RCU NOCB 基础设施。
旧流程的问题
HK_TYPE_KERNEL_NOISE、HK_TYPE_MANAGED_IRQ等 mask 主要在启动时形成,缺少安全的运行时更新模型。- 直接并发修改 mask 不足以让 tick、RCU 和 IRQ 的 per-CPU 状态同步改变,也可能让 reader 访问已经释放的旧 mask。
context_tracking_key原来是只允许启动阶段修改的 static key,ct_cpu_track_user()也带有__init限制。- 没有
rcu_nocbs=或nohz_full=时,RCU NOCB 组织结构没有初始化,运行时 offload 会因nocb_gp_rdp未建立而失败。 - CPU cycling 的临时 offline 状态会使 cpuset 的 effective CPU 集短暂变空,进而被误判为 partition 失效。
- watchdog 在启动后持有独立 mask;housekeeping mask 运行时改变时,它不会自然同步。
- v3 尝试通过 subsystem apply callback 原地应用新 mask,但 cover letter 记录 Thomas Gleixner 认为这种并发应用方式从根本上不安全,因此 v4 改用 CPU hotplug 生命周期。
新流程
创建 isolated partition 时,cpuset 先更新 sched-domain housekeeping mask,再从不可变的启动快照计算 kernel-noise 和 managed-IRQ mask。随后释放 cpuset 与 CPU read-side 锁,逐个循环状态发生变化的 CPU。
隔离 CPU 的主要顺序是:
- 用
remove_cpu()将 CPU offline; - 将 CPU 加入
tick_nohz_full_mask,启用 context tracking; - 懒初始化 RCU NOCB,并 offload 该 CPU 的 callback;
- 用
add_cpu()重新上线; - 让 IRQ dying/online hotplug callback 根据新的
HK_TYPE_MANAGED_IRQmask 完成迁移; - 全部 CPU 处理后重新同步 watchdog mask。
解除隔离时,系统根据 dhm_prev_isolated 计算反向差集,在 CPU offline 期间执行 RCU de-offload 和 tick de-isolation,再重新上线。
User writes "isolated" to cpuset.cpus.partition
|
v
Update DOMAIN, KERNEL_NOISE, and MANAGED_IRQ masks
|
v
Drop cpuset and CPU read locks
|
v
Mark changed CPUs in dhm_cycling_cpus
|
v
remove_cpu -> update tick and RCU state -> add_cpu
|
v
IRQ hotplug callbacks read the new masks
|
v
Clear cycling mask -> resync watchdog
dhm_cycling_cpus 相当于告诉 cpuset:这些 CPU 的暂时消失是 DHM 主动执行的中间步骤,不应把 partition 标记为 invalid。
Patch 概览
-
Patch 01,housekeeping 基础模型
增加HK_TYPE_KERNEL_NOISE_BOOT、HK_TYPE_MANAGED_IRQ_BOOT等启动快照类型,并增加housekeeping_cpumask_rcu()和housekeeping_update_types()。运行时 mask 始终从不可变 boot mask 重新计算。 -
Patch 02,RCU reader 审计
修改 sched/core、hrtimer、arm64 topology、Hyper-V 和 CPU sysfs 等 reader,使它们在 RCU read-side critical section 内读取可能被替换的 cpumask。TIMER 和 TICK 的修改属于防御性修正。 -
Patch 03,cpuset 触发 mask 更新
isolated partition 除更新HK_TYPE_DOMAIN外,也更新HK_TYPE_KERNEL_NOISE和HK_TYPE_MANAGED_IRQ。sched-domain 更新排在前面,因为 workqueue flush 和 timer migration 依赖它。 -
Patch 04,运行时 context tracking
拆分ct_cpu_track_user()、ct_cpu_untrack_user()和启动期包装函数,允许 offline CPU 在运行时启停 user/kernel context tracking。 -
Patch 05,RCU NOCB 懒初始化
新增rcu_nocb_lazy_init()和rcu_nocb_cpu_isolate();首次隔离时才分配 NOCB mask、组织 GP/CB 关系并创建 kthread。 -
Patch 06,watchdog 同步
保存 watchdog 启动快照,并通过lockup_detector_hk_update()将它与当前HK_TYPE_KERNEL_NOISEmask 求交后重配 detector。 -
Patch 07,运行时 full-dynticks
新增tick_nohz_cpu_isolate()和tick_nohz_cpu_deisolate(),首次调用时懒分配tick_nohz_full_mask。 -
Patch 08,抑制临时 partition invalidation
增加dhm_cycling_cpus和对应 spinlock,同时覆盖 local partition 与 remote-partition disable 判断路径。 -
Patch 09,核心 hotplug orchestration
实现dhm_cycle_isolated_cpus(),计算 newly-isolated 和 newly-deisolated 差集,并串联 CPU offline、tick、RCU、IRQ hotplug callback 和 online 流程。 -
Patch 10,文档
说明 isolated partition 会扩展到 tick、RCU NOCB 和 managed IRQ,并记录 non-hotpluggable CPU 的限制。 -
Patch 11,自测试
增加 cpulist range 解析、nohz_full精确集合比较、RCU NOCB kthread affinity 检查,以及创建、销毁、全 CPU 隔离拒绝、SMT 和不可 hotplug CPU 等场景。
关键实现
运行时 mask 不能基于当前已经缩小的 mask 继续求差集,否则 CPU 一旦退出 housekeeping 集合,销毁 partition 时也无法恢复。正确做法是始终以 boot snapshot 为锚点:
if (type == HK_TYPE_KERNEL_NOISE &&
!(housekeeping.flags & HK_FLAG_KERNEL_NOISE_BOOT))
base = cpu_possible_mask;
else if (type == HK_TYPE_KERNEL_NOISE)
base = housekeeping_cpumask(HK_TYPE_KERNEL_NOISE_BOOT);
else if (type == HK_TYPE_MANAGED_IRQ)
base = housekeeping_cpumask(HK_TYPE_MANAGED_IRQ_BOOT);
cpumask_andnot(trial, base, isol_mask);
新 mask 分配并验证后,通过 RCU 替换指针;旧 mask 只能在 grace period 结束后释放。对应 reader 必须在 RCU 临界区内完成解引用和使用,不能把返回的指针带出临界区。
Patch 09 的核心状态计算是:
cpumask_andnot(newly_isolated,
new_isolated, dhm_prev_isolated);
cpumask_andnot(newly_deisolated,
dhm_prev_isolated, new_isolated);
cpumask_copy(dhm_prev_isolated, new_isolated);
因此只 cycling 状态真正发生变化的 CPU,而不是每次重做整个 isolated 集合。
managed IRQ 没有新增专用 apply 函数。CPU offline 时的 irq_migrate_all_off_this_cpu() 和 online 时的 irq_affinity_online_cpu() 会读取已经更新的 housekeeping mask,从而复用现有 hotplug 机制。
类比
可以把 housekeeping CPU 看成工厂里的“后勤班组”,隔离 CPU 则是要求保持安静的“精密加工车间”。
启动参数像建厂时确定的固定规划图。旧模式只能在开工前决定哪些车间承担巡检、清运和广播任务,运行中很难安全改变。
DHM 则像一次受控的车间停产改造:
remove_cpu()是让车间暂时停产并清空现场;- tick suppression 是关闭周期性广播;
- RCU NOCB offload 是把待处理单据送到后勤办公室;
- managed IRQ migration 是把临时通知转给其他车间;
add_cpu()是按新职责重新开工;- boot snapshot 是永久保留的原始厂区图,撤销隔离时据此精确恢复,而不是根据多次修改后的临时图猜测原状;
dhm_cycling_cpus是门口的“计划检修中”标志,避免管理系统把短暂停产误报为车间永久失效。
Highlight:风险与注意点
-
失败处理不是事务式的:
tick_nohz_cpu_isolate()、rcu_nocb_cpu_isolate()和add_cpu()的失败主要通过WARN_ON_ONCE()报告。若中间步骤失败,mask、tick、RCU 和 CPU online 状态可能只完成了一部分,代码片段中没有完整 rollback。 -
mask 更新错误是否应阻止 cycling:patch 03 对
housekeeping_update_types()的错误使用WARN_ON_ONCE(),而不是向上返回;随后 patch 09 仍可能继续 cycling。需要验证-ENOMEM或-EINVAL时不会出现“per-CPU 状态已变化但全局 mask 未变化”的不一致。 -
必须保留 housekeeping CPU:trial mask 会检查是否仍与
cpu_online_mask相交。全 CPU 隔离应被拒绝,自测试也专门覆盖这一点。 -
non-hotpluggable CPU 无法获得完整 DHM 隔离:典型的 x86-64 CPU0 会被跳过并告警。它可以出现在 isolated partition 中,但仍可能保留 tick 和 RCU housekeeping 职责,用户不能把 sched-domain isolation 等同于完整 kernel-noise isolation。
-
managed IRQ 的零启动参数语义需确认:可见的 patch 01 只允许
HK_TYPE_KERNEL_NOISE在未 boot-enable 时首次启用;其他类型会被跳过。因而“无nohz_full=/rcu_nocbs=”能力很清楚,但从未启用isolcpus=managed_irq时 managed IRQ mask 的预期行为需要结合完整代码验证。 -
watchdog 与运行时 sysctl 的交互需验证:可见代码只在
lockup_detector_init()中保存watchdog_cpumask_boot。如果管理员启动后通过 sysctl 收窄 watchdog mask,后续 DHM 更新是否会覆盖该设置,取决于未展示路径是否同步更新快照。 -
RCU reader 审计必须完整:任何遗漏的 reader 如果在 RCU 临界区外长期使用 runtime-mutable mask 指针,都可能访问 grace period 后已经释放的内存。
-
hotplug cycling 有运行时扰动:每个变化的 CPU 都要真实 offline/online。即使设计上安全,也需要评估任务迁移、设备 callback、CPU hotplug 延迟和多 CPU 大批量切换的管理面影响。
-
remote partition 是已知脆弱点:v4 特别扩展
dhm_cycling_cpus检查以修复TEST_MATRIX[69],说明 local 与 remote partition 的 transient-empty 判断都必须持续回归测试。 -
相关系列:cover letter 明确关联 Waiman Long 的
[PATCH-next 00/23]。两者都面向 cpuset runtime housekeeping,DHM 的突出差异是通过 hotplug cycling 实现无nohz_full=/rcu_nocbs=的首次运行时激活;输入未包含两系列最终合并或取舍结论。
版本变化
-
V3 到 V4:删除
struct housekeeping_cbs、pre_validate、apply和通知式 subsystem apply 架构,改成逐 CPU hotplug cycling;修复 RCU NOCB 函数被放入.init.text后引发的 NX page fault;扩展 cycling 抑制到 remote-partition disable 路径,修复TEST_MATRIX[69]。 -
V2 到 V3:notifier chain 改为显式 per-type callback;housekeeping cpumask 指针改为 RCU 保护;修复 CPU hotplug 锁死锁和异步 work 中保存旧 mask 导致的 UAF;补充 context tracking、watchdog、RCU reader 和 selftest。该 callback 架构随后在 v4 被整体撤销。
-
V1 到 V2:系列从 DHEI 改名为 DHM;删除自定义 sysfs ABI,改用 cgroup v2 cpuset isolated partition;增加 SMT-aware 限制、文档、自测试和 RCU-safe mask handover。
一句话总结
DHM v4 通过“先更新 RCU-protected housekeeping mask,再逐 CPU offline、重配并 online”的方式,把 nohz_full 和 RCU NOCB 等隔离能力接入 cpuset isolated partition,但错误回滚、不可 hotplug CPU、managed IRQ 首次启用语义及 watchdog 配置交互仍是评审时最值得关注的部分。