sched discussion
[PATCH 1/3] sched/fair: Move __cfs_bandwidth_used to new static key API
LLM 分析
sched/fair:把 __cfs_bandwidth_used 等迁到新 static key API
系列概况
- 标题:[PATCH 0/3] Remove deprecated static key API in scheduler code
- 作者:Hongyan Xia hongyan.xia@transsion.com(Transsion)
- 版本:v1,3 个补丁(cover letter 是 [PATCH 0/3])
- 规模:13 个文件改动,44 行新增、35 行删除(
-wc -l统计见 cover) - 修改文件:
kernel/sched/{fair.c,debug.c,sched.h,core.c,cputime.c}、include/linux/sched/cputime.h,以及多个架构:arch/{arm64,loongarch,powerpc/pseries,riscv}/kernel/paravirt.c、arch/x86/kernel/{cpu/vmware.c,kvm.c}、drivers/xen/time.c - 代码统计:1/3 改 4 行(kernel/sched/fair.c),2/3 改 21 行(10 文件,对称替换),3/3 改 19 行(sched.h + debug.c)
- Message-ID:
cover.1784538478.git.hongyan.xia@transsion.com(编号顺序在 lore 里是 1/3 → 3/3 → 0/3 → 2/3 → 两封回复) - 完整性:3 个 patch 都有 diff,cover letter 列了完整 stat,两位 reviewer 已给 tag(Juergen Gross:
Acked-by;Ben Segall:Reviewed-by)
补丁目的
内核最近重构了 static key 基础设施,把旧的 struct static_key 和 static_key_true/false() 标记为 deprecated,并引入 struct static_key_true / struct static_key_false 加 DEFINE_STATIC_KEY_* / static_branch_* API。新 API 在编译期区分 true/false key,类型更安全,间接调用也收敛得更清晰。
这套 patch 系列做一次扫荡式迁移:把 scheduler 路径上还残留的旧 API 全部替换成新 API,没有功能改动。
旧流程的问题
- 用
struct static_key直接作为全局变量,每次static_branch_*()都要包static_key_*()wrapper,类型和"启用/未启用"语义都耦合在同一个结构里。 static_key_true()/static_key_false()命名本身就是带偏见的,调用方和阅读者都要靠前缀揣测语义。SCHED_FEAT还把整个sched_feat_keys[]声明成struct static_key数组,但每个元素语义可能是 true 也可能是 false——一个数组元素没法统一表达两种类型。- 多个架构各自有启用 paravirt steal clock 的代码分支,共用同一对旧 API,难看、易忘。
新流程
- 1/3 把
__cfs_bandwidth_used换成DEFINE_STATIC_KEY_FALSE,访问改static_branch_unlikely,inc/dec 改static_branch_*_cpuslocked。 - 2/3 把
paravirt_steal_{rq,}enabled在所有架构上的定义/声明/操作全部换成static_key_false家族,再加DECLARE_STATIC_KEY_FALSE暴露到头文件。 - 3/3 在
sched_feat_keys[]上用union sched_feat_key包装static_key_true/static_key_false两种 variant,给SCHED_FEAT生成对应版本的static_branch_*()入口。 - 调用点
sched_feat(x)不变;但sched_feat_enable/disable()这两行暂时仍用旧 API,作者明确承认"sadly they remain on the old API"。
Patch 概览
1/3 kernel/sched/fair.c (cfs_bandwidth_used path)
|__cfs_bandwidth_used
2/3 arch/*/paravirt.c paravirt_steal_{rq,}enabled
kernel/sched/{core,cputime}.c
include/linux/sched/cputime.h
drivers/xen/time.c
3/3 kernel/sched/{sched.h,debug.c}
sched_feat_keys[] via union sched_feat_key
关键实现
1/3 kernel/sched/fair.c
-static struct static_key __cfs_bandwidth_used;
+static DEFINE_STATIC_KEY_FALSE(__cfs_bandwidth_used);
static inline bool cfs_bandwidth_used(void)
{
- return static_key_false(&__cfs_bandwidth_used);
+ return static_branch_unlikely(&__cfs_bandwidth_used);
}
作用:未启用 cgroup bandwidth control 时,跳过 unthrottle_cfs_rq 和配额统计;inc/dec 路径改成新版 static_branch_*_cpuslocked,对外接口保持不变。
2/3 paravirt_steal_*
/* kernel/sched/{core,cputime}.c */
-struct static_key paravirt_steal_rq_enabled;
+DEFINE_STATIC_KEY_FALSE(paravirt_steal_rq_enabled);
-if (static_key_false(¶virt_steal_rq_enabled)) {
+if (static_branch_unlikely(¶virt_steal_rq_enabled)) {
各架构的 pv_time_init / vmware_cpu_down_prepare / xen_time_setup_guest / KVM x86_hyper_kvm 注册处把 static_key_slow_inc 改成 static_branch_inc。include/linux/sched/cputime.h 用 DECLARE_STATIC_KEY_FALSE 暴露给 arch/xen。
3/3 sched_feat_keys[]
最棘手的一个。features.h 里 SCHED_FEAT(name, enabled) 可以展开成 enabled ∈ {true,false},数组元素类型必须统一:
union sched_feat_key {
struct static_key_true key_true;
struct static_key_false key_false;
};
#define sched_feat_branch_true(key) static_branch_likely(&(key)->key_true)
#define sched_feat_branch_false(key) static_branch_unlikely(&(key)->key_false)
#define SCHED_FEAT(name, enabled) \
static __always_inline bool \
static_branch_##name(union sched_feat_key *key) \
{ \
return sched_feat_branch_##enabled(key); \
}
extern union sched_feat_key sched_feat_keys[__SCHED_FEAT_NR];
debug.c 的初始化宏相应改为:
#define jump_label_key__true { .key_true = STATIC_KEY_TRUE_INIT }
#define jump_label_key__false { .key_false = STATIC_KEY_FALSE_INIT }
union sched_feat_key sched_feat_keys[__SCHED_FEAT_NR] = { ... };
sched_feat_disable/enable 处由于暂时还没法干净替换,只能 (struct static_key *)&sched_feat_keys[i] 强转,绕过类型差异——这正是本系列"还剩两条线"的原因。
类比
- 把旧
static_key想象成一面没有挂旗的旗杆:static_key_true()是手写的红色飘带,static_key_false()是手写的白色飘带,每次写代码都要在脑子里查颜色。新 API 把旗杆拆成"红杆"和"白杆"两种,你想用哪个颜色就直接拿对应的杆,编译器帮你查。 sched_feat_keys[]像一排双面门牌:每家门牌可能是"营业"也可能是"休业",可是门牌号只能有一种字形。union sched_feat_key就相当于把门牌做成翻板,正面写"营业",翻过去是"休业",一排门牌共用一个格子。- Juergen Gross 给 Acked-by、Ben Segall 给 Reviewed-by,就像两位主厨在 tasting 一盘摆盘只是换了盘子、菜没变的菜,确认味道还是原来的。
Highlight:风险与注意点
sched_feat_enable/disable处(struct static_key *)&sched_feat_keys[i]强转是已知技术债:作者在 commit message 里直接承认。这种 reinterpret 在新版struct static_key_*内部分布局变了之后,可能导致 inc/dec 操作点到错误的内存。后续需要真正实现sched_feat_key_enable/disablehelper 或者重载static_key_enable_cpuslocked接受 union。DECLARE_STATIC_KEY_FALSE要求头文件已经被<linux/jump_label.h>包含。2/3 在include/linux/sched/cputime.h里手动加了一行#include <linux/jump_label.h>——本系列还好,如果将来调度头文件对外露出更多 key,可能需要在linux/sched.h集中 include。- 这套替换是 API 同步迁移,技术上一对一等价;任何架构的 paravirt 路径如果走 custom CPU hotplug 锁路径,仍要核对
*_cpuslocked后缀是否真的对应正确的锁——目前 inc 改成static_branch_inc(无_cpuslocked),要确保调用方已经持锁。Cover letter 没逐一审计这个前提,需要 maintainer 复核。 - 没有对每个编译配置(
CONFIG_JUMP_LABEL关闭、CONFIG_CFS_BANDWIDTH=n、CONFIG_PARAVIRT=n等)单独做 CI;理论上0-day会跑,但 mailing list 里只有两位 reviewer 的 tag,没有维护者合入信号。 - 这一系列没有真实"bug",但若 deprecated API 在未来内核版本被彻底移除,本系列相当于预防性补丁;mention 这种"为未来的编译警告/构建失败做准备"的语义在 cover letter 里几乎没提,建议补一段说明。
版本变化
目前只有 v1。后续可能的 v2 改动方向:
- 真正把
sched_feat_enable/disable改造成 union 友好的 helper,去掉强转struct static_key *。 - 给 cover letter 加一段"为何做这次清理"(deprecation timeline、treewide static-key cleanup 的关联)。
- 把
DECLARE_STATIC_KEY_FALSE在头文件里的暴露做得更对称,可能把include/linux/jump_label.h集中挪到linux/sched.h。
一句话总结
这套 3-patch 系列把 scheduler 路径上残留的 deprecated struct static_key 调用一次性迁到 static_key_true/false 新 API;多数改法机械,唯一卡点在 sched_feat_keys[] 用 union 包装两种 variant,剩余两行强转留给后续跟进的二阶段。