sched discussion
[PATCH v2] block: split out a new blk_plug.h header
LLM 分析
block:拆分 blk_plug 头文件
系列概况
- 标题:
[PATCH v2] block: split out a new blk_plug.h header - 作者:Christoph Hellwig
<hch@lst.de> - 版本:v2(单 patch)
- 规模:1 个 patch,diff 共 16 个文件,新文件 1 个(
include/linux/blk_plug.h,95 行) - 修改文件:
fs/aio.c、fs/fs-writeback.c、include/linux/blk_plug.h(新)、include/linux/blkdev.h、include/linux/io_uring_types.h、io_uring/io_uring.h、io_uring/kbuf.c、io_uring/rsrc.h、io_uring/rw.h、kernel/exit.c、kernel/sched/core.c、mm/madvise.c、mm/page-writeback.c、mm/readahead.c、mm/swap_state.c、mm/vmscan.c - 代码统计:净效果是
blkdev.h减少约 84 行结构/函数定义,新增一个独立blk_plug.h;多个调用方把#include <linux/blkdev.h>`` 换成#include <linux/blk_plug.h>`或直接去掉冗余包含 - Message-ID:
20260804125524.740996-1-hch@lst.de;回复178584843869.926496.8137441533296046276.b4-ty@b4 - 完整性:完整,结尾被维护者 Jens Axboe 接受并合入,commit
758b86f7bc8a2582d0783f3535854ea05d3ff97d
补丁目的
把当前散落在 include/linux/blkdev.h 里的 struct blk_plug、blk_plug_cb、rq_list 以及 plug 相关 inline 函数搬到一个新的轻量头文件 include/linux/blk_plug.h 中。这样做的目的是:
- 让 block 层之外只想用 plug 的子系统(aio、writeback、io_uring、mm、scheduler core 等)不再被迫
#include <linux/blkdev.h>这种重型头; blkdev.h后续改动时减少需要重编译的翻译单元;- 头文件依赖更清晰:plug -> sched,blkdev -> plug,io_uring 只 include 真正需要的。
旧流程的问题
旧流程下任何用到 struct blk_plug 的代码都要 #include <linux/blkdev.h>。blkdev.h 本身又包含大量 SCSI/NVMe/分区/队列相关声明,牵一发动全身。例如 kernel/sched/core.c、kernel/exit.c 这类核心调度/进程退出路径根本不需要 block 队列概念,却只是因为 current->plug 字段而被迫 include 整头。这造成:
- 改动
blkdev.h触发大规模重编译; - 编译时间被无谓拖长;
- 头文件依赖耦合,scheduler/进程生命周期代码与 block 层抽象混在一起。
新流程
抽出 blk_plug.h 之后,依赖关系变成单向小图:
+-------------------+
| blk_plug.h |
| depends on |
+---------+---------+
|
v
+-------------------+
| sched.h |
+-------------------+
^
|
+-------------------+
| blkdev.h |
| (includes plug) |
+---------+---------+
|
+--------------+--------------+
| | |
v v v
+-------+ +---------+ +-----------+
| aio.c | | mm/*.c | | io_uring |
| fs-wb | | (plug | | (plug |
| (plug)| | only) | | only) |
+-------+ +---------+ +-----------+
kernel/sched/core.c and kernel/exit.c
no longer include blkdev.h at all
调用方只 include 自己真正需要的:
- 只用 plug:
#include <linux/blk_plug.h> - 用 queue/block 设备:
#include <linux/blkdev.h>(自动传递依赖到 plug) kernel/sched/core.c、kernel/exit.c:直接去掉#include <linux/blkdev.h>,因为它们只是通过task_struct->plug引用,并不需要 plug 的全部定义。
Patch 概览
单个 patch,包含三类改动:
- 新增
include/linux/blk_plug.h:把rq_list、blk_plug、blk_plug_cb、blk_plug_cb_fn、blk_start_plug*、blk_finish_plug、blk_flush_plug、__blk_flush_plug、blk_plug_invalidate_ts、blk_check_plugged的声明搬过来;在!CONFIG_BLOCK分支保留空 inline stub。 include/linux/blkdev.h:顶部#include <linux/blk_plug.h>;删除被搬走的结构与 inline 函数;删除冗余#include <linux/sched.h>(已通过 plug.h 间接拿到)。- 调用方替换:在
aio.c、fs-writeback.c、io_uring_types.h、madvise.c、readahead.c、swap_state.c、vmscan.c中把<linux/blkdev.h>换成<linux/blk_plug.h>;在mm/page-writeback.c、kernel/exit.c、kernel/sched/core.c中直接删掉<linux/blkdev.h>。io_uring 几个子头补上<linux/file.h>、<linux/uio.h>、<linux/bvec.h>等更精确的依赖。
关键实现
/* include/linux/blk_plug.h 关键片段 */
#include <linux/sched.h>
struct blk_plug_cb;
typedef void (*blk_plug_cb_fn)(struct blk_plug_cb *cb,
bool from_schedule);
struct rq_list {
struct request *head;
struct request *tail;
};
struct blk_plug {
struct rq_list mq_list; /* blk-mq requests */
struct rq_list cached_rqs; /* batch tag/rq alloc cache */
u64 cur_ktime;
unsigned short nr_ios;
unsigned short rq_count;
bool multiple_queues;
bool has_elevator;
struct list_head cb_list; /* md needs unplug callback */
};
注意几个设计点:
blk_plug.h只依赖<linux/sched.h>,因为它需要task_struct->plug与PF_BLOCK_TS,这是 plug 语义和调度器之间的最小契约。from_schedule形参从原来的bool显式命名为bool from_schedule,让调用点(schedule()里的blk_flush_plug)意图更清楚。!CONFIG_BLOCK分支继续提供空实现,保证无 block 子系统的配置(tiny 等)下 plug 调用是 no-op。blkdev.h顶部#include <linux/blk_plug.h>,所有原本透过blkdev.h间接拿到 plug 的代码无需改动。
调用方迁移示例:
/* fs/aio.c, fs/fs-writeback.c, mm/{madvise,readahead,swap_state,vmscan}.c */
- #include <linux/blkdev.h>
+ #include <linux/blk_plug.h>
/* kernel/sched/core.c, kernel/exit.c, mm/page-writeback.c */
- #include <linux/blkdev.h> /* no longer needed */
类比
把 blk_plug.h 想成一个工具箱里的「常用小工具袋」:以前不管是修水管还是做木工,大家都要打开整个巨型工具箱(blkdev.h),因为里面塞着一把小螺丝刀(struct blk_plug)。结果每次只换一把螺丝刀,整箱工具都要搬出来晒一遍。
这次重构相当于把那把小螺丝刀、几个常用内联函数和说明卡抽出来,做成一个小腰包(blk_plug.h),木工(block 层)继续背着大工具箱,里面顺带装了小腰包;水管工(aio/writeback/mm/scheduler core)只需要小腰包就够了——大工具箱从此不被他们搬进搬出,工坊里也更整洁。schedule() 是那位「出门前清点小腰包」的人:调用 blk_flush_plug(plug, from_schedule=true) 把攒下的请求一次提交出去。
Highlight:风险与注意点
- header 顺序:
<linux/blk_plug.h>自己 include 了<linux/sched.h>,所以下游必须仍然能拿到task_struct的定义;如果某文件原本只靠<linux/blkdev.h>间接获得current->flags,替换后必须保证<linux/sched.h>仍被传递,否则PF_BLOCK_TS引用会编译失败。本 patch 的迁移点都是 plug 真正使用方,依赖完整。 - io_uring 几个子头里原本依赖
<linux/blkdev.h>间接带出的类型,迁移后被替换成<linux/uio.h>、<linux/bvec.h>、<linux/file.h>,要留意这些头是否真的覆盖了被删除的声明——一旦遗漏就是 build breakage。 kernel/sched/core.c与kernel/exit.c直接去掉<linux/blkdev.h>,这是最激进的改动;如果将来有人给task_struct->plug之外的字段引入新依赖,需要重新审视。- API 兼容:
blk_plug、blk_plug_cb、rq_list的布局完全保留,只是搬家,没有 ABI/行为变更风险。 - 维护者 Jens Axboe 当日直接 ack 应用,说明 v1->v2 的改动已经满足 block 子系统的口味。
版本变化
邮件线程仅出现 v2,无 v1 diff 与变更说明可考(v1 改动未在 lore 中展示)。从 v2 提交内容看,新头文件已经成定型结构,v1->v2 的差异更可能是微调(例如 blk_plug_cb_fn 形参命名 bool -> bool from_schedule、对 io_uring 子头补 include 这种收尾)。
一句话总结
把 struct blk_plug 与相关 plug API 从 blkdev.h 抽到独立 blk_plug.h,让调度器、退出路径、mm、io_uring 等非 block 模块不必再 include 重型头,从而加快编译并收敛头依赖。