0/17 已展开

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.h
    • include/linux/hrtimer.h
    • include/linux/{hrtimer_defs.h => hrtimer_bases.h}(重命名)
    • include/linux/hrtimer_rearm.h
    • kernel/sched/fair.c
    • kernel/time/hrtimer.c
    • kernel/time/tick-internal.h
    • sound/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_basestruct hrtimer_clock_base 等 timer 基础结构。

任何对 base 结构的修改(例如加字段、对齐调整)都会触发几乎全量的内核重编译,浪费开发者时间和 CI 资源。实际上真正需要 base 结构定义的调用方非常少。本系列的目标就是:

  1. 重命名 hrtimer_defs.hhrtimer_bases.h,让名字匹配其实际内容。
  2. hrtimer_callback_running()hrtimer_update_function() 这些依赖 base 结构的 helper 从 hrtimer.h 移走。
  3. 给所有真正需要 base 定义的调用方(hrtimer.ctick-internal.hfair.cdummy.c 等)显式 include <linux/hrtimer_bases.h>
  4. 最后从 hrtimer.h 里删除 hrtimer_bases.h 的 include,断开这条"每次改 base 就重编全树"的依赖链。
  5. 顺手补齐 hrtimer_rearm.hnospec-branch.h 等被破坏的隐式 include(linux/types.hlinux/irqflags.hlinux/lockdep.hlinux/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 依赖 boolraw_spinlock_irqsave()preempt_* 等,但都没显式 include,靠 hrtimer.h 透传。
  • arch/x86/include/asm/nospec-branch.h 用了 bool,但从来没显式 include <linux/types.h>

只要切掉 hrtimer.hhrtimer_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.hlinux/irqflags.hlinux/lockdep.hlinux/preempt.hnospec-branch.h 自己 include linux/types.h

Patch 概览

Patch标题关键改动
1/8Rename hrtimer_defs.h -> hrtimer_bases.h文件重命名 + 头部宏改名 + 更新 hrtimer.h 的 include
2/8Move hrtimer_callback_running() to hrtimer_bases.h移除 hrtimer.h 里的 static inline;移到 base 头里;fair.cdummy.c 显式 include
3/8Move hrtimer_update_function() to hrtimer.c改为 EXPORT_SYMBOL_GPL 的普通函数;不再头文件展开
4/8tick: Explicitly include <linux/hrtimer_bases.h>tick-internal.h 显式依赖
5/8hrtimer: Explicitly include <linux/hrtimer_bases.h>kernel/time/hrtimer.c 显式依赖
6/8hrtimer_rearm.h: Explicitly include 必要头types.hirqflags.hlockdep.hpreempt.h
7/8x86/speculation: Explicitly include <linux/types.h>bool 不再走 transitive
8/8hrtimer: 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.ctickfair.c)发一把钥匙(显式 #include);前台桌面就清空了,再有人事变动,前台这边啥都不重印。
  • 顺手补的 nospec-branch.h / hrtimer_rearm.h 的 include,相当于把某些员工抽屉里靠前台"顺路带水"才能喝到水的杯子,直接换成自带水壶——以后前台不再"路过送水",他们也不会渴。

另一组比喻更直观:把 hrtimer.h 当成一个"总开关面板",之前每个面板里都嵌了一台电机(base 结构定义);现在电机被独立抽出来装到配电箱里,面板只剩按钮,谁要操作电机谁自己拉线。

Highlight:风险与注意点

  1. 真正的依赖图并不显然。第 2 个 patch 移走 hrtimer_callback_running() 后,必须找到所有调用方(sched/fair.csound/drivers/dummy.c 等)显式 include,否则编译会爆。
  2. hrtimer_update_function() 从 inline 变 out-of-line,理论上多一次函数调用开销。但它通常不在热路径上,且只有 CONFIG_PROVE_LOCKING 才进 lock/unlock,权衡明显偏向可维护性。
  3. 破坏 transitive includehrtimer.h 删 include 之后,依赖了它间接得到 boolraw_spinlock_*preempt_* 的文件全部需要单独补头(这就是 patch 6/7 存在的原因)。后续维护者要继续警惕。
  4. EXPORT_SYMBOL_GPLhrtimer_update_function() 第一次出内核头文件,外部 GPL 模块将能直接使用,可能引出新的 API 用户,需不需要新增 module owner 说明要看后续 maintainer 反馈。
  5. 冲突风险:其他基于 hrtimer_defs.h 的本地 out-of-tree 代码(如果还有人在用)会编译失败,但因为重命名 + sed 一起做,搬移代价低。
  6. 后续观察:和 Peter Zijlstra 反复推的 "sched: Untangle cgroup.h from sched.h" 等"分拆头文件"系列目标一致,长期收益在 CI 重编成本下降,是个非常"省钱"的清理。
  7. 不需要 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 就重编全树"的痛点彻底切断,是一次典型的"轻度脏活 + 长期省钱"的内核头文件清理。