0/1 已展开

LLM 分析

sched_ext selftests:修复 CPU hotplug 写失败导致的挂死

系列概况

  • 标题:[PATCH] selftests/sched_ext: Handle CPU hotplug write failures
  • 作者:Tianyi Chen hi@tychen.cc
  • 版本:单版本(无 vN 编号,1/1)
  • 规模:1 个文件,+23 / -11
  • 修改文件tools/testing/selftests/sched_ext/hotplug.c
  • Message-ID20260906135534.749534-1-hi@tychen.cc
  • 完整性:包含 commit message(Fixes / Assisted-by / Signed-off-by)、diff 和测试方法说明;信息齐全。

补丁目的

tools/testing/selftests/sched_ext/hotplug.c 在 CPU 上下线写文件失败时不再傻等。

selftests 中对 sched_ext 热插拔的测试要切换 CPU 的 online 状态来触发调度器退出。原实现中 toggle_online_status() 只把错误 fprintf 到 stderr 就返回 void,不返回错误码,也不中止测试。一旦对 /sys/devices/.../online 的写入失败(权限、忙、EIO 等),测试会一直在 while (!UEI_EXITED(...)) 里转圈,等一个永远不会触发的 scheduler exit。

修复目标:

  1. 把错误码向上层传。
  2. 在关键 CPU 状态切换失败时立即中止测试(并清理 skel/link)。
  3. 把默认返回值从 SCX_TEST_PASS 改成 SCX_TEST_FAIL,避免"路径走到一半就算 pass"。

旧流程的问题

toggle_online_status()void,调用方完全无视写入失败;测试主体在 CPU 在线/离线状态没切对的情况下继续运行 bpf_map__attach_struct_ops()、挂起等待 UEI_EXITED。失败信息只在 stderr 里,没人检查,CI 上表现为超时挂死。

另外 test_hotplug() / test_hotplug_attach()status 初值是 SCX_TEST_PASS,因此即便路径中途出问题(甚至 goto 出去),老代码也会"返回 PASS"——一个隐藏的假阳性。

新流程

toggle_online_status() 改成 int,返回 file_write_long() 的结果;把两处测试函数的入口清理和结束分支改成 goto-out 模式:写入失败直接 goto out_destroy_skel / goto out_destroy_link,让既有的 cleanup() 统一回收资源(destroy link、destroy skel、再次 toggle_online_status(1) 复位)。status 初值置为 SCX_TEST_FAIL,只有走完 happy path 才显式置 SCX_TEST_PASS。这样原先"日志里报失败、但测试返回 PASS"的窗口被关掉。

+-------------------------------------+
| test_hotplug() entered              |
| status = SCX_TEST_FAIL |
+-----------------+-------------------+
 |
                  v
   toggle_online_status(0) ?
 +--------+--------+
        |ok |fail
        |                 v
        v          out_destroy_skel
   attach +            |
 wait_for_exit()     v
        |          cleanup() + return status
        v
   toggle_online_status(1) ?
        +--------+--------+
        |ok               |fail
        |                 v
        v          out_destroy_link
   status =             |
   SCX_TEST_PASS        v
        |          cleanup() + return status
        v
   out_destroy_link:
   out_destroy_skel:
        |
        v return status

Patch 概览

只改一个文件:tools/testing/selftests/sched_ext/hotplug.c

主要变化:

  • toggle_online_status()void -> int,补一个 return ret;,顺手补 \n
  • test_hotplug():把关键 toggle_online_status() 的两次调用改为 if (... toggle_online_status(...)) goto out_*;引入 out_destroy_link / out_destroy_skel 两个标签;status 默认 SCX_TEST_FAIL,happy path 显式置 SCX_TEST_PASS
  • test_hotplug_attach():同上套路的两次 toggle_online_status() 改写;同样的 out 标签和 status 初值。

关键实现

核心改动是函数签名与返回值的传递:

static int toggle_online_status(bool online)
{
    long val = online ? 1 : 0;
    int ret;

    ret = file_write_long(online_path, val);
    if (ret != 0)
        fprintf(stderr, "Failed to bring CPU %s (%s)\n",
                online ? "online" : "offline", strerror(errno));
    return ret;
}

调用点:

if (onlining && toggle_online_status(0))
    goto out_destroy_skel;
...
if (toggle_online_status(onlining ? 1 : 0))
    goto out_destroy_link;
...
if (!onlining && toggle_online_status(1))
    goto out_destroy_link;

status = SCX_TEST_PASS;

out_destroy_link:
out_destroy_skel:
    return status;

status 默认值设为 FAIL,意味着任何"没显式 status = PASS 就出函数"的路径都会失败——包括后续再加新分支、写错 goto 标签的情况。这是把"沉默正确"改成"沉默失败"的好做法。

类比

这相当于一个电梯门的安全回路:原来按钮按下去但门被卡住时,系统只是在日志里写一行"门没动",然后原地等乘客走出来——永远不会有人走出来。新版本让按钮直接告诉调度中心"门没动",调度中心立刻进入恢复流程(释放 BPF 资源、把 CPU 重新拉回在线),并明确把这趟行程标记为"不成功",而不是算"运行正常"。把默认结果从 PASS 改成 FAIL,等于把电梯出厂设定从"默认准点"改成"默认故障",只有完全跑完一遍才发现问题才算准点。

另一个类比:厨房烤箱的温度传感器失灵——老代码会读到旧温度继续烤,新代码会立刻跳到安全清理(关火、开门散温),并把这一炉记为失败,让维护人员去检查传感器。

Highlight:风险与注意点

  1. cleanup() 自己还会再调一次 toggle_online_status(1)。如果正常路径上 CPU1 复位失败,现在也会通过 status 报 FAIL——这是作者刻意加入的"normal CPU restoration 失败也要 fail"逻辑,但需要确认 cleanup()toggle_online_status 的返回值仍是无害地忽略(diff 没有显示 cleanup() 改动,假定它仍然不关心)。
  2. Assisted-by: LLM 出现了——审阅时应留意 LLM 生成的 goto 标签是否覆盖了所有原本隐式 fall-through 的清理路径,特别是 test_hotplug() 里"attach 之后、wait 之前"的那段代码,看是否漏掉了 bpf_link__destroy
  3. 既有 cleanup() 自身不感知新返回值——如果 cleanup() 内部又失败一次,目前没有第二层兜底;这是一个未来可以补的"显式 propagation"。
  4. 测试验证方法依赖 strace 注入 EIO,是白盒负向测试;建议在 CI 上也跑一次"没有注入"的 baseline,确认改动没引入误报。
  5. 关联代码:scx_test_statushotplug__destroybpf_link__destroy——这些都是项目内既有宏/函数,patch 不引入新依赖,回归面较小。

版本变化

单封 patch,无 v2/v3 演进。

一句话总结

把 sched_ext hotplug selftest 中 toggle_online_status() 从"只打印"改成"返回错误码",并在两次关键 CPU 状态切换失败时通过 goto 跳出测试,把默认返回值从 PASS 改为 FAIL,从而消除"CPU 写失败 → 测试挂死或假阳性 pass"的窗口。