0/3 已展开

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.ckernel/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(commit ef9293b3b7972),由 Peter Zijlstra 落款。

补丁目的

修复上游 commit 9650ce11f2e3("sched: dynamic: Simplify preempt model accessors")的一处遗漏:那次清理移除了 preempt_dynamic_nonepreempt_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 实测确认输出正常。