0/1 已展开

LLM 分析

scheduler:清理最后几个 deprecated static key 调用

系列概况

  • 标题: [PATCH v2] sched: Convert last bits of deprecated static key usage
  • 作者: Hongyan Xia hongyan.xia@transsion.com
  • 版本: v2(单封 patch,无后续回复)
  • 规模: 2 个文件,8 行新增,8 行删除
  • 修改文件: kernel/sched/core.ckernel/sched/fair.c
  • 代码统计: 8 insertions(+), 8 deletions(-)
  • Message-ID: 20260903115728.11864-1-hongyan.xia@transsion.com
  • 完整性: 单封 patch 收尾,没有其他 patch 或同主题回复

补丁目的

kernel/sched/ 下剩余的最后几个 deprecated static_key_* API 调用迁移到新的 static_branch_* API。

  • 旧 API 没有类型信息:调用 static_key_false() 时底层 key 到底是默认 TRUE 还是 FALSE,要靠读代码或注释才知道,写错也不会有 warning。
  • 旧 helper 命名 static_key_{true/false}() 容易让人误用:函数名只描述"取值",不描述"默认值",新人很容易把方向写反。
  • 新 API static_branch_* 把"默认值"塞进 struct 类型本身(DEFINE_STATIC_KEY_TRUEDEFINE_STATIC_KEY_FALSE 是两种不同的类型),并把 helper 改成 likely / unlikely,从命名上直接告诉读者分支预测方向。

这是这条迁移工作的收尾:作者在之前的 patch 里已经清掉了大部分站点,本 patch 把 scheduler 子系统里最后两处遗留也清掉,让 sched 下不再有任何 deprecated 的 static key 调用。commit message 明确写了 "No functional change"。

Patch 概览

patch 改两个文件,每个文件都是 4 处重命名式替换:

  • kernel/sched/core.c:内部 helper 宏 preempt_dynamic_key_enable/disable 重命名为 preempt_dynamic_branch_enable/disable,内部实现从 static_key_enable(&sk_dynamic_##f.key) 换成 static_branch_enable(&sk_dynamic_##f)
  • kernel/sched/fair.c:把 __cfs_bandwidth_used 从裸的 static struct static_key 改成显式的 DEFINE_STATIC_KEY_FALSE(...),并把所有 static_key_falsestatic_key_slow_inc_cpuslockedstatic_key_slow_dec_cpuslocked 调用换成 static_branch_unlikelystatic_branch_inc_cpuslockedstatic_branch_dec_cpuslocked

关键实现

core.c 的宏改名

-# define preempt_dynamic_key_enable(f) static_key_enable(&sk_dynamic_##f.key)
-# define preempt_dynamic_key_disable(f) static_key_disable(&sk_dynamic_##f.key)
+# define preempt_dynamic_branch_enable(f) static_branch_enable(&sk_dynamic_##f)
+# define preempt_dynamic_branch_disable(f) static_branch_disable(&sk_dynamic_##f)

新宏访问的是 sk_dynamic_##f 整体,而不是 &sk_dynamic_##f.key——新 API 直接接收 struct 本身,不再需要取 .key 字段。这两个 helper 本身并没有被 deprecated,作者借机统一命名让 core.c 和 fair.c 风格一致。

fair.c 的 cfs_bandwidth key 迁移

-static struct static_key __cfs_bandwidth_used;
+static DEFINE_STATIC_KEY_FALSE(__cfs_bandwidth_used);

- return static_key_false(&__cfs_bandwidth_used);
+ return static_branch_unlikely(&__cfs_bandwidth_used);

- static_key_slow_inc_cpuslocked(&__cfs_bandwidth_used);
+ static_branch_inc_cpuslocked(&__cfs_bandwidth_used);

- static_key_slow_dec_cpuslocked(&__cfs_bandwidth_used);
+ static_branch_dec_cpuslocked(&__cfs_bandwidth_used);

DEFINE_STATIC_KEY_FALSE 把"声明 + 默认 disabled"合并成一行;旧写法 static struct static_key 没有显式初值,靠 BSS 段零初始化被 jump label 当作"未启用",语义是隐式的。换显式宏后意图更清楚,编译器也能基于类型挡住误用。

调用点锁约束保持不变

_cpuslocked 后缀保留,说明调用方仍必须持有 cpus_read_lock()——这一不变量在 patch 里没动,只是被新 helper 名字继承下来。

类比

这就像把办公室里最后一台老式传真机换成新邮件系统:

  • 旧 API (static_key_*):要你手动接电话线、记哪个号码对应哪台机器(type 信息缺失)。static_key_true/false 这两个 helper 名字看上去对称,但跟底层 DEFINE_STATIC_KEY_TRUE/FALSE 的默认值是脱钩的,写反了也没有 warning——就像传真机贴反了号码纸,发不出去你也只会以为是对方没开机。
  • 新 API (static_branch_*):自带型号识别(DEFINE_STATIC_KEY_FALSE 的 struct 类型和 TRUE 那个不一样),插头形状也变了(直接传 struct、不再 .key),并且 helper 改名 static_branch_unlikely/likely——读代码的人一眼就知道这个分支是冷路径还是热路径。

迁移的本质不是改变行为,而是把"使用门槛"和"误用风险"都降下来,让以后的维护者更容易读懂。sk_dynamic_* 用的 static_key_enable/disable 本身没被 deprecated,但作者借这次机会把宏名一起改齐,相当于顺手把办公室门口的旧指示牌也换了。

ASCII 流程图

  +--------------------------+        +-----------------------------+
  |  old static_key_* API    |        |  new static_branch_* API    |
  +--------------------------+        +-----------------------------+
              |                                  |
              v                                  v
  +--------------------------+        +-----------------------------+
  |  raw struct static_key   |  ===>  |  DEFINE_STATIC_KEY_FALSE()  |
  |  no default info in type |        |  type carries default value |
  +--------------------------+        +-----------------------------+
              |                                  |
              v                                  v
  +--------------------------+        +-----------------------------+
  | static_key_false(&k)     |  ===>  | static_branch_unlikely(&k)  |
  | static_key_true(&k)      |        | static_branch_likely(&k)    |
  +--------------------------+        +-----------------------------+
              |                                  |
              v                                  v
  +--------------------------+        +-----------------------------+
  | static_key_slow_inc/     |  ===>  | static_branch_inc/          |
  |   dec_cpuslocked(&k)     |        |   dec_cpuslocked(&k)        |
  +--------------------------+        +-----------------------------+

  Files touched (rename only, no behavior change):

  kernel/sched/core.c
    preempt_dynamic_key_enable    -> preempt_dynamic_branch_enable
    preempt_dynamic_key_disable   -> preempt_dynamic_branch_disable

  kernel/sched/fair.c
    static struct static_key __cfs_bandwidth_used
        -> DEFINE_STATIC_KEY_FALSE(__cfs_bandwidth_used)
    static_key_false(&k)                  -> static_branch_unlikely(&k)
    static_key_slow_inc_cpuslocked(&k)    -> static_branch_inc_cpuslocked(&k)
    static_key_slow_dec_cpuslocked(&k)    -> static_branch_dec_cpuslocked(&k)

Highlight:风险与注意点

  1. static_key 默认值的不变量依赖:旧写法 static struct static_key __cfs_bandwidth_used; 没有显式初值,靠 jump label 把 BSS 零初始化当成 "disabled"。换 DEFINE_STATIC_KEY_FALSE 后语义变显式,但仍需确认 inc/dec 在所有路径上仍然配对——不平衡会让 key 长期处于 enabled 状态,影响 CFS bandwidth 检查路径的分支预测和 jump label 命中率。
  2. _cpuslocked 锁约束未变:新 API 仍带 _cpuslocked 后缀,说明调用方必须持有 cpus_read_lock()。patch 没动调用点,但命名统一后,未来审查这类调用时锁约束会更显眼,遗漏的 review 应该比之前更难逃过。
  3. v2 的 rebase 冲突:v1 → v2 主要是在 core.c 上 rebase 后修复了一个 "big conflict",说明迁移期间有别的 patch 也在改 preempt_dynamic_* 同一区域。后续如果有人继续在这一带改,需要先核对最新版本,避免再次撞车。
  4. preempt_dynamic_key_* 宏本身不算 deprecated:commit message 已经说明旧 static_key_enable/disable 本身没被废弃,作者借这次机会把宏名一起改成 _branch_,属于顺手统一风格而不是修 bug。读者不要误以为这个 rename 是为了修复什么 correctness 问题。
  5. 后续观察点:建议关注 cfs_bandwidth_used 在 cgroup v1/v2 切换、CPU offline/online 场景下的 inc/dec 配对审计;另外既然命名约定已经统一成 static_branch_*,未来引入新的静态分支时不要再退回旧 API。

版本变化

  • v1 → v2
    • commit message 里补充了"旧 API 为什么被废弃"的解释(type 信息缺失、static_key_{true/false} 命名易混淆)。
    • kernel/sched/core.c 上 rebase 修复了一个 "big conflict"(具体冲突内容没列在邮件正文里)。
    • 代码逻辑上 v1 和 v2 完全等价。

一句话总结

把 scheduler 里最后两个文件的 deprecated static_key_* 调用替换成类型更安全、命名更直观的 static_branch_*,一次零功能变化的清理收尾。