sched-ext discussion
[PATCH sched_ext/for-7.2-fixes] selftests/sched_ext: Handle sleeping task affinity changes in numa test
LLM 分析
系列基线信息
| 字段 | 值 |
|---|---|
| 标题 | [PATCH sched_ext/for-7.2-fixes] selftests/sched_ext: Handle sleeping task affinity changes in numa test |
| 作者 | Kuba Piecuch |
| 版本 | 单 patch,无版本号 |
| 规模 | 1 file changed, 12 insertions, 1 deletion |
| Message-ID | 20260723095955.1415621-1-jpiecuch@google.com |
| 来源 | sched-ext |
明确目的
修复 sched_ext NUMA 自测调度器中的一个崩溃性 bug:当睡眠任务的 CPU affinity 在其睡眠期间被修改后,task_cpu(p) 可能指向一个已不在 p->cpus_ptr 中的 CPU。由此确定的 NUMA node 完全落在任务 cpumask 之外,导致 scx_pick_idle_cpu_node() / scx_pick_any_cpu_node() 找不到合法 CPU 并返回 -EBUSY(即 -16)。sched_ext 框架将 -16 当作非法 CPU 编号处理,触发 scx_bpf_error("invalid CPU -16") 并使调度器异常退出。
补丁在 numa_select_cpu() 中增加防护逻辑:如果选定 NUMA node 内找不到可用 CPU,且检测到 task_cpu 已不在 p->cpus_ptr 中(说明 affinity 在睡眠中被改过),就回退到 prev_cpu 作为安全兜底。
遍历代码
改动集中在 tools/testing/selftests/sched_ext/numa.bpf.c,分两部分:
1. 提取 task_cpu 为局部变量(小重构)
// 之前:内联调用
int node = __COMPAT_scx_bpf_cpu_node(scx_bpf_task_cpu(p));
// 之后:先取出 task_cpu
s32 task_cpu = scx_bpf_task_cpu(p);
int node = __COMPAT_scx_bpf_cpu_node(task_cpu);
把 scx_bpf_task_cpu(p) 的返回值存到 task_cpu,后续 numa_select_cpu 也要用到这个值做 cpumask 检查,避免重复调用。
2. numa_select_cpu 中新增兜底检查
if (cpu < 0 && !bpf_cpumask_test_cpu(task_cpu, p->cpus_ptr))
return prev_cpu;
逻辑链:
cpu < 0:在选定 NUMA node 内没找到可用的 CPU(scx_pick_*_cpu_node返回负值)。!bpf_cpumask_test_cpu(task_cpu, p->cpus_ptr):task_cpu不在任务当前 cpumask 中 → 说明 affinity 在睡眠期间被改过,task_cpu是过时的。- 满足两个条件就返回
prev_cpu:wakeup 之前任务实际所在的 CPU,作为安全回退值。
概念类比
想象一个人在酒店睡觉时,酒店前台把他的房间号改了(相当于修改了 CPU affinity)。当他醒来问"我在哪个楼层?"(NUMA node),前台按旧房间号查楼层,结果发现那个楼层已经不属于他了。前台说"本楼层没空房"(cpu < 0),而且他的旧房间号在新房卡上找不到(task_cpu 不在 cpus_ptr 中),于是前台只好说"回到你睡觉前的那个房间吧"(return prev_cpu)。
ASCII 流程图
Task sleeping on CPU X (task_cpu = X)
|
| affinity changed while sleeping
| cpus_ptr now = {A, B, C} (X removed!)
|
wakeup → numa_select_cpu()
|
|-- node = scx_bpf_cpu_node(X) ← X may be outside cpus_ptr!
|
|-- scx_pick_idle_cpu_node(cpus_ptr, node)
| |
| |-- found CPU? → YES → return that CPU
| |
| |-- NO (cpu < 0) ──────────────────┐
| |
|-- bpf_cpumask_test_cpu(X, cpus_ptr) |
| | |
| |-- X in cpus_ptr → other fallback |
| | |
| |-- X NOT in cpus_ptr ──────────────┘
| (affinity changed)
| |
+-- return prev_cpu ───────────┘ ← safe fallback
Highlight 突出问题
-
只修了 selftest,没修 sched_ext 框架本身:
task_cpu可能超出cpus_ptr是一个更普遍的 wakeup 语义问题。其他 sched_ext BPF 调度器如果也依赖task_cpu/scx_bpf_cpu_node(task_cpu)做 NUMA 决策,同样会踩坑。本补丁只让 selftest 变得健壮,框架层是否需要统一兜底值得后续跟进。 -
prev_cpu 也可能不在新 cpus_ptr 中:如果 affinity 改动同时也把
prev_cpu移出了 cpus_ptr,返回prev_cpu同样不合法。当前场景中prev_cpu通常仍属合法范围,但极端情况下需验证框架是否会二次拦截。 -
竞态窗口短暂但真实:从 affinity 变更到
select_task_rq()完成选队之间,task_cpu与cpus_ptr不一致。窗口虽短,但在 migration/cpu hotplug 等路径下可触发。
版本演进
本补丁为单一版本(无 v1/v2 迭代),Tejun Heo 已直接 apply 到 sched_ext/for-7.2-fixes 分支。
与其他相关 patch 系列的关联
- Fixes tag 指向
5ae5161820e5("selftests/sched_ext: Add NUMA-aware scheduler test"),即引入 numa.bpf.c 自测的原始提交。 - 与 sched_ext 框架的
select_task_rq_scx()及task_cpu语义相关,后续若框架层增加对"sleeping task affinity change"的统一处理,本 selftest 补丁中的防护逻辑可能变得冗余。
一句话总结
修复 NUMA 自测调度器在睡眠任务 affinity 被改后因 task_cpu 越界导致 "invalid CPU -16" 崩溃的问题,通过检测 cpumask 不一致时回退 prev_cpu 兜底。