sched-ext discussion
[PATCH] selftests/sched_ext: Cover duplicate DSQ creation and ID reuse
LLM 分析
sched_ext:扩展 create_dsq selftest 覆盖 DSQ 重复创建与 ID 复用
系列概况
- 标题:
[PATCH] selftests/sched_ext: Cover duplicate DSQ creation and ID reuse - 作者:Tianyi Chen
<hi@tychen.cc> - 版本:单封 PATCH(非系列)
- 规模:2 文件改动,+38/-1
- 修改文件:
tools/testing/selftests/sched_ext/create_dsq.bpf.c(+35)tools/testing/selftests/sched_ext/create_dsq.c(+3/-1)
- Message-ID:
20260906144044.849222-1-hi@tychen.cc - 完整性:完整(含 diff、commit message、Link、Assisted-by、Signed-off-by)
补丁目的
把 sched_ext DSQ ID 生命周期 API 契约钉死在 selftest 上:
- 重复
scx_bpf_create_dsq必须返回-EEXIST; - 失败的重复创建不能破坏原 DSQ 的可访问性;
destroy_dsq后dsq_nr_queued必须返回-ENOENT;- 同 ID 销毁后允许重新创建;
- 1024 次迭代全部跑通作为空队列回归基线。
旧流程的问题
原 create_dsq_init 只在循环里 create + destroy,没断言任何错误码:
- 重复创建是否真返回
-EEXIST?没测; - 失败的重复创建是否破坏原 DSQ?没测;
- destroy 后
dsq_nr_queued返回什么?没测; - 同 ID 销毁后能否复用?没测;
- 1024 次循环只是计数,无法发现中段回归。
新流程
每次迭代扩成九步:destroy → create → create(断言 -EEXIST)→ nr_queued==0 → destroy → nr_queued==-ENOENT → recreate → nr_queued==0 → 计数++。
用户态 run() 追加 SCX_EQ(nr_lifecycle_tests, 1024) 把 BPF 侧结果与迭代次数对齐。
关键实现
BPF 侧新增 u32 nr_lifecycle_tests,每次迭代核心逻辑:
bpf_for(i, 0, 1024) {
scx_bpf_destroy_dsq(i); /* 清场 */
err = scx_bpf_create_dsq(i, -1); /* 1) 首次创建 */
err = scx_bpf_create_dsq(i, -1); /* 2) 重复 -> -EEXIST */
err = scx_bpf_dsq_nr_queued(i); /* 3) 原 DSQ 仍可读 */
scx_bpf_destroy_dsq(i);
err = scx_bpf_dsq_nr_queued(i); /* 4) 销毁后 -> -ENOENT */
err = scx_bpf_create_dsq(i, -1); /* 5) 复用 ID */
err = scx_bpf_dsq_nr_queued(i); /* 6) 新 DSQ 仍空 */
nr_lifecycle_tests++;
}
用户态只改两行:计数器断言 + description 文案更新。
ASCII 流程
+-----------+
i in [0,1024) |---1. destroy(i)----+
+-----------+ |
| v
| +------------------+
| | 2. create(i) OK |
| +------------------+
| |
| v
| +----------------------+
| | 3. create(i) -> EEXIST|
| +----------------------+
| |
| v
| +----------------------+
| | 4. nr_queued(i) == 0 |
| +----------------------+
| |
| v
| +----------------------+
| | 5. destroy(i) |
| +----------------------+
| |
| v
| +--------------------------+
| | 6. nr_queued(i) == -ENOENT|
| +--------------------------+
| |
| v
| +----------------------+
| | 7. create(i) OK reuse|
| +----------------------+
| |
| v
| +----------------------+
| | 8. nr_queued(i) == 0 |
| +----------------------+
| |
| v
| +----------------------+
+---|9. nr_lifecycle_tests++|
+----------------------+
After loop: user-space run() -> SCX_EQ(nr_lifecycle_tests, 1024)
-> PASS / FAIL
DSQ 状态机
create(i) create(i) --EEXIST--> (no-op)
+-----------+ +-----------+ +-----------------------+
| Free |->| Active |-| Active (unchanged) |
+-----------+ +-----------+ +-----------------------+
^ | ^ |
| destroy | | recreate(i) | destroy
| v | v
| +-----------+ +-----------+
| | Destroyed | | Destroyed |
| +-----------+ +-----------+
| | nr_queued==-ENOENT
+-------------+----------------+
next iteration
类比
把 DSQ ID 想成酒店前台的房号:
- 首次
create(i):发房卡 → Active; - 再
create(i):前台说「已有人」→-EEXIST,但原房卡仍有效; destroy(i):客人退房 → 房间不存在;nr_queued(i):客服查房 →-ENOENT;- 再
create(i):同房号重新挂出 → ID 复用成立; - 累计 1024 次:连续 1024 次入住都没出房务事故。
Highlight:风险与注意点
- 只覆盖空 DSQ:commit message 明说「without concurrent enqueueing or destruction of a nonempty queue」,无法捕获有任务被消费时的并发销毁竞态,需另写并发 selftest。
-EEXIST是 API 契约:若 sched_ext 改为 refcount/race-tolerant 创建语义,本测试会误报。- 依赖前置 patch:
Link:指向Z-OZ7tJWhRZbUk1l@gpd3/,本补丁基于其 diff 二次增强,需先 apply 前置 patch 才能匹配基础结构,但提交时未在 commit message 注明依赖。 - debug build 风险:
BPF_STRUCT_OPS_SLEEPABLE在 lockdep/KEI 等 debug 构建下可能被放慢,1024 次 BPF 调用耗时不可控。 - 硬件覆盖不足:作者仅在双核 matching-kernel VM 验证,16 CPU/NUMA 拓扑上是否一致未经验证。
- 全局命名冲突:
nr_lifecycle_tests是 BSS 全局,未来同类 selftest 共用 BPF skeleton 时需注意命名。
一句话总结
sched_ext 把 create_dsq selftest 的每次迭代从「create+destroy」扩成九步 DSQ 生命周期,并用 1024 次计数器断言把 -EEXIST / -ENOENT / ID 复用这套 API 契约钉死在空队列场景上。