0/5 已展开

LLM 分析

sched_ext: Fix stale errno in scx_sub_enable_workfn()

系列基线信息

  • 标题: [PATCH] sched_ext: Fix stale errno in scx_sub_enable_workfn() → v2 保持同一标题
  • 作者: Cui Jian (cjian720@163.com)
  • 版本: v1 → v2
  • 规模: v1 改 1 文件 5 行 (+4/-1);v2 改 1 文件 2 行 (+2)
  • Message-ID: 20260718201713.17890-1-cjian720@163.com(v1), 20260722100352.36627-1-cjian720@163.com(v2)
  • 来源: sched-ext 频道
  • 审核者: Andrea Righi(review)、Tejun Heo(maintainer,已合入)

明确目的

scx_sub_enable_workfn() 中有两处错误路径在跳转到 err_disable 时没有设置 ret,导致 ret 仍为 0。这使得 commit db4e9defd2e8 引入的 fallback 错误信息打印出 scx_sub_enable() failed (0) —— 一个 errno=0 的"成功式失败"日志。

当前无害,因为这两处路径在跳转前已经调用了 scx_error() 记录具体错误信息,而 scx_error() 的"first error wins"机制保证了有意义的信息已被记录。但 fallback 路径本身是坏的,未来如果"first error wins"逻辑改变或 fallback 被其他场景依赖,就会暴露问题。

本 patch 的目标:在嵌套深度检查处设 ret = -EINVAL,在 cgroup online 检查处设 ret = -ENODEV,使 fallback 始终报告真实 errno。


遍历代码

scx_sub_enable_workfn() 是 sched_ext 子调度器启用的工作函数,负责在 cgroup 层级下启用嵌套的 sched_ext 调度器。

关键路径:

  1. 嵌套深度检查if (sch->level >= SCX_SUB_MAX_DEPTH) → 调用 scx_error() 报错 → goto err_disable,但未设 ret。补丁加入 ret = -EINVAL

  2. cgroup online 检查if (!(cgrp->self.flags & CSS_ONLINE)) → 调用 scx_error()goto err_unlock_and_disable,未设 ret。补丁加入 ret = -ENODEV

  3. err_disable 标签处的 fallbackdb4e9defd2e8 在此处添加了 fallback 日志 scx_sub_enable() failed (ret),期望 ret 总是非零,但上述两条路径跳转过来时 ret=0。

v1 还修复了 validate_ops() 调用路径(if (validate_ops(sch, ops)) 改为 ret = validate_ops(sch, ops); if (ret)),但 v2 发现该路径已在 for-7.3 分支中被修复,所以 v2 只保留两处仍需修复的路径。


ASCII 流程图

scx_sub_enable_workfn() 错误路径
=================================

  sch->level >= SCX_SUB_MAX_DEPTH?
       │
       YES --> scx_error("max nesting depth")
               │
               ret = 0     <-- 原代码(BUG)
               ret = -EINVAL <-- 补丁修复
               │
               goto err_disable --> fallback: "failed (ret)"
                                     原来打印 "(0)"
                                     修复打印 "(-EINVAL)"

  !(cgrp->self.flags & CSS_ONLINE)?
       │
       YES --> scx_error("cgroup not online")
               │
               ret = 0      <-- 原代码(BUG)
               ret = -ENODEV <-- 补丁修复
               │
               goto err_unlock_and_disable --> 同上 fallback

概念类比

想象医院急诊分诊系统:病人被拒收时,护士手写详细拒收原因贴在病历上(scx_error()),然后到出口登记台把拒收代码录入电脑(fallback 日志的 ret)。问题是——护士写了详细原因后,忘了在电脑输入对应拒收代码,电脑默认填"0"(正常),打印出"拒收原因:0"。手写记录写着真实原因,但电脑记录是错误的。补丁让护士在写手写记录的同时,也把正确拒收代码输入电脑。


Highlight 突出问题

  1. "first error wins"掩盖了 fallback 的缺陷:当前无害是因为 scx_error() 先记录了具体信息,fallback 只是冗余补充。但如果未来 scx_error 的"first wins"规则被修改,或 fallback 被其他代码路径依赖,ret=0 就会成为真正的问题。这种"当前无害但机制不健壮"的 bug 最容易被忽视。

  2. v1→v2 范围缩减说明分支间代码已变动validate_ops() 路径在 for-7.3 分支已被独立修复(移到 sub.c 并改为 ret = scx_validate_ops())。提交 patch 前应检查目标分支最新状态,避免重复修复。

  3. 两个 goto 目标不同:嵌套深度检查跳 err_disable,cgroup 检查跳 err_unlock_and_disable(需要先解锁),补丁只加了 ret 赋值没改变 goto 目标,逻辑正确,但读者需注意两个错误出口的清理步骤不同。


版本演进

版本关键改动
v1修复 3 条路径:嵌套深度(ret = -EINVAL)、validate_ops(ret = validate_ops(); if (ret))、cgroup online(ret = -ENODEV);改 ext.c
v2只修复 2 条路径(嵌套深度 + cgroup online);validate_ops 已在 for-7.3 的 sub.c 中独立修复;文件从 ext.c 变为 sub.c

与其他相关 patch 系列的关联

  • commit db4e9defd2e8("sched_ext: Record an error on errno-only sub-enable failure"):引入 fallback 错误日志机制,是本 bug 的根源——它假设所有路径跳到 err_disable 时 ret 已设为非零,但遗漏了两条路径。
  • scx_root_enable_workfn():v1 commit message 提到 root enable 函数已正确保存 validate_ops() 返回值到 ret,是本修复的参照实现。

一句话总结

修复 scx_sub_enable_workfn() 中两条错误路径未设置 ret 导致 fallback 日志打印 errno=0 的缺陷,确保所有失败路径都报告真实错误码。