sched discussion
[PATCH] sched: dynamic: Fix preemption model strings
LLM 分析
sched: dynamic: 修复 preemption model 字符串
系列概况
- 标题:
[PATCH] sched: dynamic: Fix preemption model strings - 作者: Mark Rutland mark.rutland@arm.com
- 版本: 单封 PATCH,非 series
- 规模: 1 patch,2 文件,+3/-9
- 修改文件:
kernel/sched/core.c、kernel/sched/debug.c - 代码统计: 2 files changed, 3 insertions(+), 9 deletions(-)
- Message-ID:
20260902101637.232129-1-mark.rutland@arm.com - 完整性: 含
Reported-by/Fixes:/Signed-off-by/Cc;后续 Mete Durlu (s390) 提供Tested-by;已被 tip-bot2 提入tip:sched/core(commitef9293b3b7972),由 Peter Zijlstra 落款。
补丁目的
修复上游 commit 9650ce11f2e3("sched: dynamic: Simplify preempt model accessors")的一处遗漏:那次清理移除了 preempt_dynamic_none 与 preempt_dynamic_voluntary 两个 enum 值,但忘了同步精简 preempt_modes[] 字符串数组。导致两个明显症状:
preempt_model_str()通过 enum 索引反查的字符串错位,backtrace 显示的 preemption model 不正确。sched_dynamic_show()输出的/sys/kernel/debug/sched/preempt完全为空,丢失可见性。
补丁同时把 sched_dynamic_show() 内层那段根据 CONFIG 推算起止下标的循环,改成简单的 NULL 终止符遍历。
旧流程的问题
preempt_modes[] 仍挂着 4 项,enum 缩成 2 项,索引错位:
enum preempt_dynamic_mode preempt_modes[]
preempt_dynamic_full (0) --> "none" [MISMATCH]
preempt_dynamic_lazy (1) --> "voluntary" [MISMATCH]
[none REMOVED] --> "full" [MISMATCH]
[voluntary REMOVED] --> "lazy" [MISMATCH]
Result:
- preempt_model_str() returns wrong string for backtraces
- sched_dynamic_show() shifts bracket markers onto the wrong rows
sched_dynamic_show() 内部还有一段历史包袱:通过 (RT || LAZY) * 2 算起始 i,再数 NULL 终止符算 j,再 j -= !IS_ENABLED(LAZY)。新前提(只剩 full / lazy 两项)下,这段算术已经没意义,但旧代码没清理。
新流程
preempt_modes[]缩为"full", "lazy", NULL,与 enum 一一对应。sched_dynamic_show()改成for (int i = 0; preempt_modes[i]; i++),NULL 终止符遍历。- 隐含前提:
CONFIG_PREEMPT_DYNAMIC现在依赖CONFIG_ARCH_HAS_PREEMPT_LAZY,"lazy" 始终存在,不会出现数组只剩 "full" 一项的退化情形。
preempt_modes[] enum dynamic_mode debugfs bracket
"full" <-- preempt_dynamic_full (full) when full
"lazy" <-- preempt_dynamic_lazy lazy when full
full when lazy
(lazy) when lazy
NULL <-- end sentinel
关键实现
核心 diff 两段:
/* kernel/sched/core.c */
const char *preempt_modes[] = {
- "none", "voluntary", "full", "lazy", NULL,
+ "full", "lazy", NULL,
};
/* kernel/sched/debug.c, sched_dynamic_show() */
- int i = (IS_ENABLED(CONFIG_PREEMPT_RT) || IS_ENABLED(CONFIG_ARCH_HAS_PREEMPT_LAZY)) * 2;
- int j;
- /* Count entries in NULL terminated preempt_modes */
- for (j = 0; preempt_modes[j]; j++)
- ;
- j -= !IS_ENABLED(CONFIG_ARCH_HAS_PREEMPT_LAZY);
-
- for (; i < j; i++) {
+ /* Stop at NULL terminator */
+ for (int i = 0; preempt_modes[i]; i++) {
if (mode == i)
seq_puts(m, "(");
seq_puts(m, preempt_modes[i]);
效果:
- 数组与 enum 对齐,
preempt_model_str()直接返回正确字符串。 - 内层循环不再做条件切片,遍历范围天然匹配数组长度,括号标记
(mode)落在当前选中项周围。
类比
把 preempt_modes[] 想成餐厅门口的菜单牌:厨房只剩两道菜(full、lazy),但菜单上还挂着 "none"、"voluntary"。客人按 enum 编号点第 0 号菜,服务员递给写错的旧菜名——补丁就是清理菜单牌,只挂现在实际供的两道菜,并把"按菜单牌一格一格写到黑板"的流程改成"看到菜名就写到黑板,看到菜单结束就停"。
另一种类比:旧代码像用两把尺子量窗帘——一把量起点,一把量终点,再把中间那段剪下来;新代码直接说"看到布边就停"。
Highlight:风险与注意点
- 典型 refactor 漏改:enum 删 2 项、字符串数组忘同步删,是 cleanup 系列里最常见的遗留 bug。后续任何 enum 与字符串数组配对维护的地方,建议加
static_assert(ARRAY_SIZE(preempt_modes) == NR_PREEMPT_MODES)锁死长度。 - 影响调试可信度:backtrace 里显示出错的 preempt model 字符串可能让内核开发者误判当前配置;脚本/CI 若依赖
cat /sys/kernel/debug/sched/preempt抓配置,会静默拿到空字符串。 - debugfs 空输出比"输出错"更隐蔽:监控/CI 可能在不知不觉中走错分支。
- 循环改写依赖隐含配置约束:
for (...; preempt_modes[i]; ...)假设数组至少含 "full",且依赖CONFIG_PREEMPT_DYNAMIC depends on CONFIG_ARCH_HAS_PREEMPT_LAZY才能保证 "lazy" 不会孤立;若未来这条 Kconfig 依赖被改,需重新审视。 - 跟进点:观察后续是否有
static_assert硬约束补丁跟进,把 enum/数组配对彻底锁死;可关注/sys/kernel/debug/sched/preempt在 -rt / lazy 平台下的回归测试。
一句话总结
清理型 refactor 9650ce11f2e3 把 enum 缩成两项却忘了同步 preempt_modes[] 字符串数组,Mark Rutland 用 +3/-9 行的最小补丁把数组和 sched_dynamic_show() 循环一起纠回,并已被 tip-bot2 合入 sched/core 分支,由 s390 上 Mete Durlu 实测确认输出正常。