0/3 已展开

LLM 分析

sched/fair:跳过 memoryless NUMA 节点上的 NUMA balancing 扫描

系列概况

  • 标题:[PATCH] sched/fair: Skip NUMA balancing scan on memoryless nodes
  • 作者:Phineas Su pohaosu@google.com(Google)
  • 版本:单封 patch(v1,无版本号)
  • 规模:1 file changed, 6 insertions(+), 0 deletions(-)
  • 修改文件kernel/sched/fair.c
  • 代码统计:+6 行,0 删除
  • Message-ID20260730175151.3855700-1-pohaosu@google.com
  • 完整性:完整,包含 1 封 patch + 2 封回复(AMD K Prateek Nayak、Peter Zijlstra)

补丁目的

在存在 memoryless NUMA 节点(即 CPU 在线但 N_MEMORY 状态为 false 的节点)的系统上,自动 NUMA balancing(kernel.numa_balancing=1)会陷入"无意义的循环":

  1. task_tick_numa() 每个 scan period 都向运行在 memoryless node 上的 task 排入 task_numa_work
  2. task_numa_work() 把 VMA unmap 成 PROT_NONE,制造 hint fault;
  3. 缺页处理走到 migrate_misplaced_folio(),因为目标节点没有 managed memory 而 TNF_MIGRATE_FAIL
  4. 下个 scan period 又重新开始。

结果是在根本没有迁移可能性的情况下产生 page fault 风暴,%sys 飙到约 78%。

补丁目标:在源头阻断这条无收益路径,避免空转浪费 CPU。

旧流程的问题

task_tick_numa(curr)
        |
        v schedule task_work --> task_numa_work        |
        v unmap VMA (PROT_NONE)
        |
        v
   next access -> do_numa_page        |
        v
   migrate_misplaced_folio -> TNF_MIGRATE_FAIL        |
        v
   next scan period -> repeat forever

症结:task_tick_numa()task_numa_work() 没有判断"目标节点到底有没有内存"。

新流程

task_tick_numa(curr)
        |
        v
  node_state(task_node(curr), N_MEMORY)? ---no---> return (skip)
        | yes
        v schedule task_work --> task_numa_work
        |
        v
  node_state(task_node(p), N_MEMORY)? ---no---> return (abort)
        | yes
        v
   unmap / hint fault / migration attempt

两道防线:调度侧避免再次 enqueue,工作侧即使已被 enqueue(任务在 enqueue 后迁移到 memoryless node)也立即 abort。

关键实现

@@ task_numa_work() @@+    if (!node_state(task_node(p), N_MEMORY))
+        return;

@@ task_tick_numa() @@
+    if (!node_state(task_node(curr), N_MEMORY))
+        return;
  • task_node()cpu_to_node() 等路径得到当前 task 所用 CPU 所属的 nid。
  • node_state(nid, N_MEMORY)node_states[N_MEMORY] 的位测试,由 memory hotplug 在节点内存全部 offline 时清除。
  • 两处都是 cheap 的位检查,没有分配、没有锁。

Patch 概览

仅1 个 hunk × 2 处:

位置函数作用
fair.c:4101task_numa_work已 enqueue 的回调到达时直接 return
fair.c:4394task_tick_numatick路径不再向 memoryless node 的 task 排 numa_work

类比

把 NUMA balancing 想象成快递员送货

  • 旧流程:快递员每10 分钟跑到一个"地址上没有信箱"的客户家,敲门、按门铃、把包裹退回、再10 分钟后再来——一遍又一遍。
  • 新流程:快递出发前先看一眼地图(N_MEMORY),确认这个地址确实有信箱;没信箱就直接跳过这一户,省下时间服务真正能签收的客户。

Peter Zijlstra 的反对意见则像是说:"别跳过送货,而是把客户的信箱搬到离他最近的网点——让页面靠近任务所在的 CPU节点,而不是直接把这条路径砍掉。"

Highlight:风险与注意点

  • Peter Zijlstra 直接反对当前思路:他认为更合理的做法是把 page 安置到离运行节点最近的位置,而不是完全 skip scan。补丁可能需要从"跳过扫描"调整为"重定向迁移目标"或"反方向迁移 task 到有内存的节点"。
  • AMD 的 K Prateek Nayak 也参与讨论(body 被截断,需要看完整内容确认是否提出测试或替代方案)。
  • 验证点:在真实或模拟 memoryless NUMA 拓扑(如 acpi_memhotplug 全部 offline、cpuset 限制 mems)上 %sys 是否真的从 ~78% 回落到 baseline。
  • 后续观察:task 在 memoryless node 与 memoryful node 之间迁移的场景,task_numa_work 那道防线是唯一兜底,需要确认 race window(task已经在 enqueue 队列中、但执行前被 migrate)。

版本变化

仅一版(v1),无 v2/v3 历史。

与其他相关 patch 系列的关联

  • mm/migrate路径上的 memoryless-node修复(如 migrate_misplaced_folio 对 N_MEMORY 的处理)有上下游关系——上游跳过扫描后,下游的 migration 失败路径虽不再被频繁触发,但健壮性补丁仍然有价值。
  • 与 cpuset/cgroup memory affinity 相关讨论相邻:cpuset 内存限制也可能造成 task 看似在的节点 N_MEMORY 为 false 的情形。

一句话总结

该 patch 在 NUMA balancing 触发源头加入两道 N_MEMORY 早返回,根治 memoryless 节点上的 page-fault 风暴;但 Peter Zijlstra倾向于"让页面靠近运行节点"而非"直接禁用",整体方案仍有调整空间。