0/2 已展开

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.cfs/fs-writeback.cinclude/linux/blk_plug.h(新)、include/linux/blkdev.hinclude/linux/io_uring_types.hio_uring/io_uring.hio_uring/kbuf.cio_uring/rsrc.hio_uring/rw.hkernel/exit.ckernel/sched/core.cmm/madvise.cmm/page-writeback.cmm/readahead.cmm/swap_state.cmm/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_plugblk_plug_cbrq_list 以及 plug 相关 inline 函数搬到一个新的轻量头文件 include/linux/blk_plug.h 中。这样做的目的是:

  1. 让 block 层之外只想用 plug 的子系统(aio、writeback、io_uring、mm、scheduler core 等)不再被迫 #include <linux/blkdev.h> 这种重型头;
  2. blkdev.h 后续改动时减少需要重编译的翻译单元;
  3. 头文件依赖更清晰:plug -> sched,blkdev -> plug,io_uring 只 include 真正需要的。

旧流程的问题

旧流程下任何用到 struct blk_plug 的代码都要 #include <linux/blkdev.h>blkdev.h 本身又包含大量 SCSI/NVMe/分区/队列相关声明,牵一发动全身。例如 kernel/sched/core.ckernel/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.ckernel/exit.c:直接去掉 #include <linux/blkdev.h>,因为它们只是通过 task_struct->plug 引用,并不需要 plug 的全部定义。

Patch 概览

单个 patch,包含三类改动:

  1. 新增 include/linux/blk_plug.h:把 rq_listblk_plugblk_plug_cbblk_plug_cb_fnblk_start_plug*blk_finish_plugblk_flush_plug__blk_flush_plugblk_plug_invalidate_tsblk_check_plugged 的声明搬过来;在 !CONFIG_BLOCK 分支保留空 inline stub。
  2. include/linux/blkdev.h:顶部 #include <linux/blk_plug.h>;删除被搬走的结构与 inline 函数;删除冗余 #include <linux/sched.h>(已通过 plug.h 间接拿到)。
  3. 调用方替换:在 aio.cfs-writeback.cio_uring_types.hmadvise.creadahead.cswap_state.cvmscan.c 中把 <linux/blkdev.h> 换成 <linux/blk_plug.h>;在 mm/page-writeback.ckernel/exit.ckernel/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->plugPF_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.ckernel/exit.c 直接去掉 <linux/blkdev.h>,这是最激进的改动;如果将来有人给 task_struct->plug 之外的字段引入新依赖,需要重新审视。
  • API 兼容:blk_plugblk_plug_cbrq_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 重型头,从而加快编译并收敛头依赖。