sched discussion
[PATCH 0/8] hrtimer: Untangle base definitions from hrtimer.h
LLM 分析
hrtimer:把基础定义从 hrtimer.h 里拆出去
系列概况
- 标题:
[PATCH 0/8] hrtimer: Untangle base definitions from hrtimer.h - 作者:Thomas Weissschuh (Schneider Electric)
<thomas.weissschuh@linutronix.de> - 版本:v1(共 8 个 patch)
- 规模:8 个文件变动(commit log 中字段显示 8 files changed, 49 insertions(+), 34 deletions(-))
- 修改文件:
arch/x86/include/asm/nospec-branch.hinclude/linux/hrtimer.hinclude/linux/{hrtimer_defs.h => hrtimer_bases.h}(重命名)include/linux/hrtimer_rearm.hkernel/sched/fair.ckernel/time/hrtimer.ckernel/time/tick-internal.hsound/drivers/dummy.c
- 代码统计:8 files changed, 49 insertions(+), 34 deletions(-)
- Message-ID(首封):
20260702-hrtimer-header-dependencies-v1-0-c50b19bda473@linutronix.de - 完整性:完整。线程包含 17 封邮件(1 cover-letter + 8 patch + 8 tip-bot2 合并通告)。所有 patch 已在 2026-07-07 合并入
tip: timers/core分支(Committer:Thomas Gleixner)
补丁目的
include/linux/hrtimer.h 被大量内核代码直接 include。这个文件当前又 include 了 hrtimer_defs.h(即后面的 hrtimer_bases.h),里面定义了 struct hrtimer_cpu_base、struct hrtimer_clock_base 等 timer 基础结构。
任何对 base 结构的修改(例如加字段、对齐调整)都会触发几乎全量的内核重编译,浪费开发者时间和 CI 资源。实际上真正需要 base 结构定义的调用方非常少。本系列的目标就是:
- 重命名
hrtimer_defs.h→hrtimer_bases.h,让名字匹配其实际内容。 - 把
hrtimer_callback_running()、hrtimer_update_function()这些依赖 base 结构的 helper 从hrtimer.h移走。 - 给所有真正需要 base 定义的调用方(
hrtimer.c、tick-internal.h、fair.c、dummy.c等)显式include <linux/hrtimer_bases.h>。 - 最后从
hrtimer.h里删除hrtimer_bases.h的 include,断开这条"每次改 base 就重编全树"的依赖链。 - 顺手补齐
hrtimer_rearm.h、nospec-branch.h等被破坏的隐式 include(linux/types.h、linux/irqflags.h、linux/lockdep.h、linux/preempt.h)。
旧流程的问题
+-----------------------------+
| modify hrtimer_bases.h |
| (old name: hrtimer_defs.h) |
+-------------+---------------+
| included by v +------------------------------+
| hrtimer.h directly includes |
+--------------+---------------+
|
v +------------------------------------------+
| almost every .c / .h includes hrtimer.h |
+----------------------+-------------------+
|
v
+------------------------------+
| full-tree rebuild |
| (even if only one field) |
+------------------------------+
此外还存在几个隐式 include 链:
hrtimer_rearm.h依赖bool、raw_spinlock_irqsave()、preempt_*等,但都没显式 include,靠hrtimer.h透传。arch/x86/include/asm/nospec-branch.h用了bool,但从来没显式 include<linux/types.h>。
只要切掉 hrtimer.h → hrtimer_bases.h 的链,这些"顺手 include"的隐式依赖就会暴露,引起编译错误。
新流程
+------------------------+ explicit include +------------------------+
| hrtimer.c | ---------------------> | hrtimer_bases.h |
| tick-internal.h | -----+ | (was hrtimer_defs.h) |
| kernel/sched/fair.c | ---+ | +------------------------+
| sound/drivers/dummy.c | -+ | | ^
+------------------------+ | | | |
| | +---------------------------+
| | explicit include
| |
v v
+------------------------+
| hrtimer.h |
| (no longer includes |
| hrtimer_bases.h) |
+------------------------+
|
v only needs hrtimer public API
+------------------------+
| bulk of the kernel |
+------------------------+
同时 hrtimer_rearm.h 自己 include linux/types.h、linux/irqflags.h、linux/lockdep.h、linux/preempt.h;nospec-branch.h 自己 include linux/types.h。
Patch 概览
| Patch | 标题 | 关键改动 |
|---|---|---|
| 1/8 | Rename hrtimer_defs.h -> hrtimer_bases.h | 文件重命名 + 头部宏改名 + 更新 hrtimer.h 的 include |
| 2/8 | Move hrtimer_callback_running() to hrtimer_bases.h | 移除 hrtimer.h 里的 static inline;移到 base 头里;fair.c、dummy.c 显式 include |
| 3/8 | Move hrtimer_update_function() to hrtimer.c | 改为 EXPORT_SYMBOL_GPL 的普通函数;不再头文件展开 |
| 4/8 | tick: Explicitly include <linux/hrtimer_bases.h> | tick-internal.h 显式依赖 |
| 5/8 | hrtimer: Explicitly include <linux/hrtimer_bases.h> | kernel/time/hrtimer.c 显式依赖 |
| 6/8 | hrtimer_rearm.h: Explicitly include 必要头 | 加 types.h、irqflags.h、lockdep.h、preempt.h |
| 7/8 | x86/speculation: Explicitly include <linux/types.h> | bool 不再走 transitive |
| 8/8 | hrtimer: Remove hrtimer_bases.h from hrtimer.h | 删除 include;最终落地 |
每个 patch 都是"前向兼容"的一小步,每步之后 tree 都能编,没有一个大爆炸的重构。这种顺序保证了 review 时能确认中间状态。
关键实现
/* PATCH 2/8: move helper to base header, avoid exposing base in hrtimer.h */
static inline int hrtimer_callback_running(struct hrtimer *timer)
{
return timer->base->running == timer;
}
timer->base 的完整类型在 hrtimer_bases.h 里,所以必须连头一起搬,并让 hrtimer_bases.h 反向 include hrtimer.h 来拿到 struct hrtimer 的前置声明。
/* PATCH 3/8: from static inline to out-of-line function */
void hrtimer_update_function(struct hrtimer *timer,
enum hrtimer_restart (*function)(struct hrtimer *))
{
#ifdef CONFIG_PROVE_LOCKING
guard(raw_spinlock_irqsave)(&timer->base->cpu_base->lock);
if (WARN_ON_ONCE(hrtimer_is_queued(timer)))
return;
if (WARN_ON_ONCE(!function))
return;
#endif
ACCESS_PRIVATE(timer, function) = function;
}
EXPORT_SYMBOL_GPL(hrtimer_update_function);
移到 .c 是因为它直接 deref timer->base->cpu_base->lock,本来就不适合放头文件。再叠上 EXPORT_SYMBOL_GPL,意味着外部模块也用得上。
/* PATCH 8/8: final cut, remove include from hrtimer.h */
--- a/include/linux/hrtimer.h
+++ b/include/linux/hrtimer.h
@@ -12,7 +12,6 @@
-#include <linux/hrtimer_bases.h>
类比
把它想成搬办公室的"分层收纳":
- 原本公司前台(
hrtimer.h)的桌子上堆着公司全员花名册(hrtimer_bases.h)。任何人来前台办业务都得顺手被复印一份名册;只要公司有人换岗(改一个字段),所有来办过业务的人都要重新复印一次。 - 这个系列做的事就是:把名册搬到人事部专属柜子里(
hrtimer_bases.h),给真正需要的人(hrtimer.c、tick、fair.c)发一把钥匙(显式#include);前台桌面就清空了,再有人事变动,前台这边啥都不重印。 - 顺手补的
nospec-branch.h/hrtimer_rearm.h的 include,相当于把某些员工抽屉里靠前台"顺路带水"才能喝到水的杯子,直接换成自带水壶——以后前台不再"路过送水",他们也不会渴。
另一组比喻更直观:把 hrtimer.h 当成一个"总开关面板",之前每个面板里都嵌了一台电机(base 结构定义);现在电机被独立抽出来装到配电箱里,面板只剩按钮,谁要操作电机谁自己拉线。
Highlight:风险与注意点
- 真正的依赖图并不显然。第 2 个 patch 移走
hrtimer_callback_running()后,必须找到所有调用方(sched/fair.c、sound/drivers/dummy.c等)显式 include,否则编译会爆。 hrtimer_update_function()从 inline 变 out-of-line,理论上多一次函数调用开销。但它通常不在热路径上,且只有CONFIG_PROVE_LOCKING才进 lock/unlock,权衡明显偏向可维护性。- 破坏 transitive include。
hrtimer.h删 include 之后,依赖了它间接得到bool、raw_spinlock_*、preempt_*的文件全部需要单独补头(这就是 patch 6/7 存在的原因)。后续维护者要继续警惕。 EXPORT_SYMBOL_GPL:hrtimer_update_function()第一次出内核头文件,外部 GPL 模块将能直接使用,可能引出新的 API 用户,需不需要新增 module owner 说明要看后续 maintainer 反馈。- 冲突风险:其他基于
hrtimer_defs.h的本地 out-of-tree 代码(如果还有人在用)会编译失败,但因为重命名 + sed 一起做,搬移代价低。 - 后续观察:和 Peter Zijlstra 反复推的 "sched: Untangle cgroup.h from sched.h" 等"分拆头文件"系列目标一致,长期收益在 CI 重编成本下降,是个非常"省钱"的清理。
- 不需要 ABI 检查:纯源代码级别重构,用户态 ABI 不变。
版本变化
线程里只有 v1,没有 v2/v3;Thomas Weissschuh 在 v1 直接被 Thomas Gleixner 取走,合并入 tip: timers/core,时间线如下:
2026-07-02 v1 RFC + 8 patches -> posted to LKML / timers
2026-07-07 tip-bot2 announcements -> one commit per patch lands in tip
2026-07-07 CommitterDate: Tue -> Thomas Gleixner merges to timers/core
也就是说,v1 即终结版本,没有经过 review 反复迭代就被 maintainer 直接收下,对于这种"纯整理、不动语义"的 cleanup 来说很典型。
一句话总结
通过把 hrtimer 的 base 结构定义搬出公头 hrtimer.h、并让真正需要的调用方显式 include,把"改 base 就重编全树"的痛点彻底切断,是一次典型的"轻度脏活 + 长期省钱"的内核头文件清理。