sched discussion
[PATCH v2] sched: Convert last bits of deprecated static key usage
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.c、kernel/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_TRUE和DEFINE_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_false、static_key_slow_inc_cpuslocked、static_key_slow_dec_cpuslocked调用换成static_branch_unlikely、static_branch_inc_cpuslocked、static_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:风险与注意点
- 裸
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 命中率。 _cpuslocked锁约束未变:新 API 仍带_cpuslocked后缀,说明调用方必须持有cpus_read_lock()。patch 没动调用点,但命名统一后,未来审查这类调用时锁约束会更显眼,遗漏的 review 应该比之前更难逃过。- v2 的 rebase 冲突:v1 → v2 主要是在
core.c上 rebase 后修复了一个 "big conflict",说明迁移期间有别的 patch 也在改preempt_dynamic_*同一区域。后续如果有人继续在这一带改,需要先核对最新版本,避免再次撞车。 preempt_dynamic_key_*宏本身不算 deprecated:commit message 已经说明旧static_key_enable/disable本身没被废弃,作者借这次机会把宏名一起改成_branch_,属于顺手统一风格而不是修 bug。读者不要误以为这个 rename 是为了修复什么 correctness 问题。- 后续观察点:建议关注
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 完全等价。
- commit message 里补充了"旧 API 为什么被废弃"的解释(type 信息缺失、
一句话总结
把 scheduler 里最后两个文件的 deprecated static_key_* 调用替换成类型更安全、命名更直观的 static_branch_*,一次零功能变化的清理收尾。