sched discussion
[PATCH v2 0/6] sched: dynamic: Simplify PREEMPT_DYNAMIC
LLM 分析
sched/dynamic:把 PREEMPT_DYNAMIC 收敛到只剩 FULL/LAZY
系列概况
- 标题:[PATCH v2 0/6] sched: dynamic: Simplify PREEMPT_DYNAMIC
- 作者:Mark Rutland mark.rutland@arm.com
- 版本:v2,相对 v1 rebase 到 v7.2-rc4,吸收 Shrikanth、Jinjie 的 Reviewed-by,并新增 accessors 清理 patch
- 规模:6 个 patch
- 修改文件:arch/Kconfig、arch/arm64/Kconfig、arch/arm64/include/asm/preempt.h、arch/loongarch/Kconfig、arch/powerpc/Kconfig、arch/riscv/Kconfig、arch/s390/Kconfig、arch/s390/include/asm/preempt.h、arch/x86/Kconfig、arch/x86/include/asm/preempt.h、include/asm-generic/preempt.h、include/linux/irq-entry-common.h、include/linux/kernel.h、include/linux/preempt.h、include/linux/sched.h、kernel/Kconfig.preempt、kernel/entry/common.c、kernel/sched/core.c(共 18 个)
- 代码统计:+23 / -374
- Message-ID(首封):20260803191731.3244294-1-mark.rutland@arm.com
- 完整性:完整线程——6 个 patch + Shrikanth/Mete/Jinjie 的 build 测试与 review 回复 + Mark 的回应 + tip-bot2 6 封合并通知 + Jinjie 关于 kernel-parameters.txt 文档补齐的最后一封
补丁目的
v7.0 起 commit 7dadeaa6e851("sched: Further restrict the preemption modes")让任何支持 ARCH_HAS_PREEMPT_LAZY 的架构都无法选择 NONE/VOLUNTARY 模型,PREEMPT_DYNAMIC 实际上只剩 FULL/LAZY 两个选项。但 Kconfig、enum、动态 wrapper、HAVE_PREEMPT_DYNAMIC_{CALL,KEY}、架构 stub 全部还在,按运行时"事实上的现状"被白白维护着。
系列目标:把这种"事实"提升为 Kconfig/代码层面的正式约束——让 PREEMPT_DYNAMIC 依赖 ARCH_HAS_PREEMPT_LAZY,并按顺序逐个把"NONE/VOLUNTARY 路径 + 多余动态切换机制"砍干净。
旧流程的问题
sched_dynamic_mode()还在认 "none"/"voluntary",但对应的preempt_dynamic_{none,voluntary}值被注释成不可选;初始化分支里也看不到这两条路径。- 每条 preemption 函数都同时存在 CALL 和 KEY 两套动态化实现:
DEFINE_STATIC_CALL(...)包 trampoline,或dynamic_*_xxx()套一层static_branch_unlikely。 cond_resched()、might_resched()在 PREEMPT_DYNAMIC 下永远是关的;preempt_schedule()、preempt_schedule_notrace()、irqentry_exit_cond_resched()永远是开的。enable/disable 都成了空操作。HAVE_PREEMPT_DYNAMIC_{CALL,KEY}强依赖把 arm64/loongarch/powerpc/riscv/s390 锁到 PREEMPT_DYNAMIC_KEY,把 x86 锁到 CALL 路径。preempt_model_none/voluntary在 PREEMPT_DYNAMIC=y 时仍是函数体(多一次跳转和 EXPORT_SYMBOL),而 NONE/VOLUNTARY 模型又根本不会进来。
新流程
kernel/Kconfig.preempt:PREEMPTION 加depends on ARCH_HAS_PREEMPT_LAZY;PREEMPT_DYNAMIC 不再depends on HAVE_PREEMPT_DYNAMIC,也不再select JUMP_LABEL,默认 y。kernel/sched/core.c:删除 "none"/"voluntary" 字符串解析、对应 enum、PREEMPT_MODEL_ACCESSOR(none/voluntary)、初始化分支里相应的(unselectable)注释。- 架构
preempt.h:__preempt_schedule{,_notrace}()直接展开成preempt_schedule_thunk或普通函数调用;删 dynamic wrapper。 kernel/entry/common.c:raw_irqentry_exit_cond_resched()改回irqentry_exit_cond_resched(),删 static_call/key 包装。arch/Kconfig:删除HAVE_PREEMPT_DYNAMIC、HAVE_PREEMPT_DYNAMIC_CALL、HAVE_PREEMPT_DYNAMIC_KEY三个 Kconfig;6 个架构的 Kconfig 删除对应select。include/linux/preempt.h:把preempt_model_none/voluntary由 out-of-line 实现改为 inlineIS_ENABLED。
整体把动态切换收窄到一个静态 key 上(FULL/LAZY 切换点见下面"关键实现")。
Patch 概览
| # | 主题 | 关键改动 |
|---|---|---|
| 1/6 | Make PREEMPT_DYNAMIC depend on ARCH_HAS_PREEMPT_LAZY | Kconfig 加依赖,core.c 删 none/voluntary 解析 |
| 2/6 | Simplify {cond,might}_resched() | 两者永远关,删 dynamic wrapper |
| 3/6 | Simplify preempt_schedule{,_notrace}() | 两者永远开,删 trampoline 与 dynamic wrapper |
| 4/6 | Simplify irqentry_exit_cond_resched() | 永远开,函数名恢复单一实现 |
| 5/6 | Remove HAVE_PREEMPT_DYNAMIC_{CALL,KEY} | 删除三个 Kconfig 与所有架构 select |
| 6/6 | Simplify preempt model accessors | accessor 改 inline,删多余 enum |
关键实现
5/6 完成之后,运行时只剩一个静态 key 切换点:
/* kernel/sched/core.c */
static DEFINE_STATIC_KEY_TRUE(sk_dynamic_preempt_lazy);
static void __sched_dynamic_update(int mode)
{
switch (mode) {
case preempt_dynamic_full:
preempt_dynamic_key_disable(preempt_lazy);
pr_info("Dynamic Preempt: full\n");
break;
case preempt_dynamic_lazy:
preempt_dynamic_key_enable(preempt_lazy);
pr_info("Dynamic Preempt: lazy\n");
break;
}
}
架构侧变成直接调用或单条 asm跳转:
/* arch/x86/include/asm/preempt.h */
#define __preempt_schedule() \
asm volatile("call preempt_schedule_thunk" : ASM_CALL_CONSTRAINT)
#define __preempt_schedule_notrace() \
asm volatile("call preempt_schedule_notrace_thunk" : ASM_CALL_CONSTRAINT)
/* arch/arm64, arch/s390, asm-generic/preempt.h */
#define __preempt_schedule() preempt_schedule()
#define __preempt_schedule_notrace() preempt_schedule_notrace()
BEFORE AFTER
PREEMPT_DYNAMIC mode parser PREEMPT_DYNAMIC mode parser
"none" -> preempt_dynamic_none [DEAD] "full" -> preempt_dynamic_full
"voluntary" -> preempt_dynamic_voluntary[DEAD]"lazy"-> preempt_dynamic_lazy
Runtime switch mechanism Runtime switch mechanism
HAVE_PREEMPT_DYNAMIC_CALL (static_call) sk_dynamic_preempt_lazy (static_key)
OR only
HAVE_PREEMPT_DYNAMIC_KEY (static_key)
Dynamic wrappers (5 functions) Dynamic wrappers
cond_resched dynamic_* cond_resched direct
might_resched dynamic_* might_resched direct
preempt_schedule dynamic_* preempt_schedule direct
preempt_schedule_notrace dynamic_* preempt_schedule_notrace direct
irqentry_exit_cond_resched dynamic_* irqentry_exit_cond_resched direct
Architectures forced to select Architectures
{arm64,loongarch,powerpc,riscv,s390}: KEY Just need ARCH_HAS_PREEMPT_LAZY
{x86} : CALL
Lines: 18 files, +23 / -374
类比
把这套机制想成餐厅。
菜单上印了五种菜(NONE / VOLUNTARY / FULL / LAZY / RT),厨房里实际只做 FULL 和 LAZY;但每张桌子还摆着"点 NONE/VOLUNTARY 之后请等厨师拒绝"的小纸条——dynamic wrapper 与 Kconfig 分支。
这个系列相当于:
- 把菜单上不再供应的两道菜划掉(删 NONE/VOLUNTARY 解析、删 enum 值)。
- 撤掉桌面上那些"点了也别做"的小纸条(删
dynamic_*包装与 static_call/key 路径)。 - 服务员只需记住"今天炒锅是猛火还是文火"——也就是
dynamic_preempt_lazy这一个开关。
食客吃到的菜没变,但厨房动作、菜单和服务员都轻了一截。
Highlight:风险与注意点
- 合入时机偏晚:作者主动指出 v7.2-rc4 才 rebase,tip:sched/core 基于 v7.2-rc1,rc 末班车合入风险大;已用 tip-bot2 推送,证实最终走了 v7.3 周期。
- 行为默认值改变:删
HAVE_PREEMPT_DYNAMIC_KEY后 arm64/loongarch/powerpc/riscv/s390 默认也会开 PREEMPT_DYNAMIC。作者已论证运行时只剩sk_dynamic_preempt_lazy,但下游发行版与打 custom patch 的厂商要重新看默认配置。 - 文档遗漏:Jinjie 在 9 月 3 日指出
Documentation/admin-guide/kernel-parameters.txt中preempt=参数仍描述了 "none"/"voluntary" 选项,需要补一封 patch。 - 评审容易误读:6/6 中保留的
(unselectable)注释块让人以为模式仍可选;Jinjie 提出异议后 Mark 同意移除,体现后续版本注释需更干净。 - PowerPC 死代码冲突:v1 之前已与
a2c02aa0c6ca(powerpc 删死代码)冲突;未来再清理时要确认没遗漏的 static_key 路径。 - 保守边界:
preempt_model_voluntary()当前没有用户,作者为 accessor 命名一致性保留 inline 实现;若后续确无调用方,可继续删。
版本变化
- v1 → v2(作者列点):rebase 到 v7.2-rc4 以避开 powerpc 死代码冲突;应用 Shrikanth、Jinjie 的 Reviewed-by;修正 commit message 错字;新增 patch 6/6("Simplify preempt model accessors"),回应 Shrikanth 的清理建议。
- v2 期间评审反馈 →计划 v3
- Jinjie(9 月 1 日)对 6/6:建议把
(unselectable)注释一并删除;Mark 同意。 - Jinjie(9 月 3 日):要求同步更新
Documentation/admin-guide/kernel-parameters.txt,会触发一封文档补丁或合入下个 spin。
- Jinjie(9 月 1 日)对 6/6:建议把
- 最终落地:tip-bot2 在 9 月 2 日连续推送 6 封合并通知(commit 9650ce11…、5b9a28ee…、aa4178f6…、b9d267b9…、88e0b3bb…、d3d16750…),全部进入 sched/core 分支。
一句话总结
把 PREEMPT_DYNAMIC 里关于 NONE/VOLUNTARY 的死代码、Kconfig 选项、动态 wrapper 一次性砍掉,让运行时只剩 dynamic_preempt_lazy 一个静态 key,行为不变、6 个 patch 净瘦身 374 行。