sched discussion
[PATCH RESEND] sched/feat: Use the new static key API for sched_feat
LLM 分析
sched/feat:使用新的 static key API 重构 sched_feat
系列概况
- 标题:[PATCH RESEND] sched/feat: Use the new static key API for sched_feat
- 作者:Hongyan Xia hongyan.xia@transsion.com
- 版本:RESEND(单 patch,原系列被拆分成单 patch 提交)
- 规模:1 个 patch,修改 2 个文件,新增 19 行,删除 10 行
- 修改文件:
kernel/sched/debug.ckernel/sched/sched.h
- 代码统计:+19 / -10
- Message-ID:
<20260819081312.12447-1-hongyan.xia@transsion.com>(原 patch)<178833568366.3717435.7198709553946268204.tip-bot2@tip-bot2>(tip-bot 通知 1,commitd92d1a182dc2d5dc5e4418765739f56bcbde806a)<178833798278.3717435.7817097970740592420.tip-bot2@tip-bot2>(tip-bot 通知 2,commit2a672daa4b272d092e193eb40376b4a8063e25fa)
- 完整性:作者明确声明 "No functional change",RESEND 描述拆分系列以便 review;该 patch 已被 tip-bot 推送到
tip:sched/core分支,出现两次 tip 通知。
补丁目的
内核的旧 struct static_key 接口以及 static_key_true() / static_key_false() 包装已经废弃,社区推荐统一迁移到区分 true / false 两种类型的 struct static_key_true 与 struct static_key_false 新接口。sched_feat 子系统目前使用旧的 static_key 数组来保存每个 feature 的开关状态。本 patch 的目的就是:
- 把
sched_feat_keys从旧struct static_key迁移到新 API; - 通过 union 解决"同一个数组既可能装 true key 又可能装 false key"的难题;
- 在
debug.c中将static_key_disable/enable_cpuslocked替换为带类型的static_branch_disable/enable_cpuslocked; - 在
sched.h中将SCHED_FEAT宏生成的static_branch_##name()适配到新的 union 类型。
无功能变化,只是 API 重构,便于后续彻底删除旧接口。
旧流程的问题
旧 struct static_key 是一个不区分 true / false 语义的"通用"静态键,使用时调用方必须记得自己配置的初始状态:
#define SCHED_FEAT(name, enabled) \
static __always_inline bool static_branch_##name(struct static_key *key) \
{ \
return static_key_##enabled(key); \
}
这种做法有两个明显问题:
- 类型不安全:
enabled可以是true或false,但key的类型始终是struct static_key *,编译器无法在编译期验证"true key 应当用static_key_true()读"还是"false key 应当用static_key_false()读"。 - API 已废弃:内核主线已经把
static_key_true/false()等接口标记为 legacy,新接口要求调用者显式选择struct static_key_true或struct static_key_false,由编译器强制约束语义。
如果直接改成新 API,数组元素无法同时容纳两种类型,需要一个 union 来兜底。
新流程
新方案用 union sched_feat_key 把两种类型装进同一个数组,并通过宏把"读哪一支"封装在 sched_feat_branch_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); \
}
数组里的每个元素是 union,具体类型由 features.h 中每个 feature 是 SCHED_FEAT(name, true) 还是 SCHED_FEAT(name, false) 决定,初始化宏也用指定初始化器 { .key_true = STATIC_KEY_TRUE_INIT } / { .key_false = STATIC_KEY_FALSE_INIT }。
关键实现
1. 类型定义与初始化
kernel/sched/sched.h 增加:
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)
extern union sched_feat_key sched_feat_keys[__SCHED_FEAT_NR];
kernel/sched/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] = {
#include "features.h"
};
2. SCHED_FEAT 宏展开
例如 SCHED_FEAT(WAKEUP_PREEMPTION, true) 会展开为:
static __always_inline bool
static_branch_WAKEUP_PREEMPTION(union sched_feat_key *key)
{
return sched_feat_branch_true(key);
}
而 SCHED_FEAT(..., false) 走 key_false 分支。
3. 开关路径
debug.c 里 sched_feat_disable/enable:
static_branch_disable_cpuslocked(&sched_feat_keys[i].key_true);
static_branch_enable_cpuslocked (&sched_feat_keys[i].key_false);
通过 cpuslocked 变体,避免在 cpus_read_lock() 持有期间再走 preempt_disable() 的慢路径。
类比
- 旧
struct static_key像一把没有刻度的万能钥匙:谁都能用,但不打开看就不知道这把钥匙出厂时是锁着还是开着的,用错了也不会立刻报错。 - 新的
struct static_key_true/struct static_key_false像是出厂时就在钥匙柄上刻了 "ON" 和 "OFF" 两种字样:只能用对应型号的锁孔去匹配,门锁自己会拒绝插错的钥匙。 union sched_feat_key就是一个小盒子,左边格子放 ON 型钥匙,右边格子放 OFF 型钥匙,盒子空间只能容纳其中一把,所以用 union 而不是 struct:同一时刻只装一种类型,但同一个收纳位可以装两种之一。sched_feat_branch_true/false就像自动感应灯:宏根据 feature 名字后的 enabled 标志,自动决定去摸哪个格子,调用者不用关心具体细节。
+-------------------------------------+
| features.h (per-feature) |
| SCHED_FEAT(WAKEUP_PREEMPTION, true) |
| SCHED_FEAT(NEW_FAIR_SLEEPERS, false)|
+-----------------+-------------------+
|
+-----------------+-------------------+
| |
v v
+-------------------+ +-----------------------+
| key_true branch | | key_false branch |
| .key_true = | | .key_false = |
| STATIC_KEY_TRUE_INIT | STATIC_KEY_FALSE_INIT |
+---------+---------+ +-----------+-----------+
| |
+----------------+-------------------+
|
v
+----------------------------------------------------+
| union sched_feat_key sched_feat_keys[N] |
| each slot holds either a true key or a false key |
+--------+--------------------+----------------------+
| |
v v
debug.c (cpuslocked): sched.h SCHED_FEAT macro:
static_branch_disable_ static_branch_##name(key)
cpuslocked(&k.key_true) return sched_feat_branch_##enabled(key);
static_branch_enable_ true -> static_branch_likely(&key->key_true)
cpuslocked(&k.key_false) false -> static_branch_unlikely(&key->key_false)
Highlight:风险与注意点
- union 别名风险:虽然 union 在 C 里合法,但
static_branch_*接口会按类型强转读对应分支;调用方必须保证访问的字段与初始化时使用的字段一致。features.h一旦错配(例如一个 feature 在两处定义不一样),编译器不会抓,必须靠代码 review 兜底。 - cpuslocked 语义:
static_branch_disable/enable_cpuslocked()要求调用者已持有cpus_read_lock(),不要在未加锁时误用,否则会破坏 RCU 同步。本 patch 在sched_feat_disable/enable中仍然沿用旧调用方已有的锁上下文,因此功能等价。 - 未迁移点:内核里其它模块如果还在用
struct static_key数组,本 patch 只解决 sched_feat 一处;后续还有大量 sites 需要迁移,本次只是其中一个先例。 - RESEND 与合并:邮件里提到 "separate the original series into individual patches",意味着作者原先可能是更大 series 的一部分;当前只有这一封,但说明上游还有其他相邻迁移 patch 需要关注,否则容易和 union 名称冲突。
- tip-bot 两次通知:两封 tip-bot 邮件 commit hash 不同(
d92d1a18和2a672daa),可能是不同 tip 树的再生或 rebase;后续如果回灌回 mainline,需要核对最终 cherry-pick 的 hash 与正文。
版本变化
- RESEND vs 上一版:上一版是某个 multi-patch series 的第 N 块,作者认为 "as a series 不易 review",所以 RESEND 单独拎出来变成独立的 1-patch 提交。
- v1(RESEND 后)-> tip:tip-bot 邮件显示已合入
tip:sched/core,无功能改动,纯 API 迁移。
与其他相关 patch 系列的关联
- 与内核 "deprecate old static_key API" 的整体迁移工作同源;本次只触碰 sched_feat,其它子系统的对应迁移 patch 会以类似 union 模式跟进。
tip:sched/core上sched_feat相关 patch 序列通常一次性合入,因此 tip-bot 两次通知通常对应不同 maint 分支或 topic 分支,建议以最终 commit hash 为准。- 关注后续 Peter Zijlstra / Ingo Molnar 提交的 cleanup 系列,确认 union 名称
sched_feat_key没有被进一步改名。
一句话总结
把 sched_feat 使用的旧 struct static_key 数组通过 union 改造成区分 true/false 的新 static_key_true/static_key_false 接口,并替换掉对应的 static_branch_*_cpuslocked 调用,无功能变化但消灭了一层废弃 API。