背景
swap 的读写提交已抽象成 swap_ops:can_merge、submit_write、submit_read 三个钩子,块设备与文件系统各挂各的实现。回收路径把换出页攒进 swap_iocb 的 bvec 数组,攒满 SWAP_CLUSTER_MAX 才提交一次 I/O,摊薄提交开销。
批量攒写并非对所有设备都划算。zram 这类同步 swap 设备(SWP_SYNCHRONOUS_IO)写完立即完成,writeback 位随写随清。经典 LRU 的扫描循环正指望这一点:扫到正在 writeback 的 folio 会等它完成,位清得越及时,扫描看到的进展越真实。攒批让 writeback 位滞留,扫描误以为进展停滞。
批量攒写与同步设备的矛盾
┌───────────────────────────────────────────┐
│ 换出页攒进 swap_iocb,满 SWAP_CLUSTER_MAX │
│ 才提交 │
└─────────────────────┬─────────────────────┘
▼
┌───────────────────────────────────────────┐
│ 同步设备本应写完即清 writeback 位, │
│ 攒批让位滞留 │
└─────────────────────┬─────────────────────┘
▼
┌───────────────────────────────────────────┐
│ LRU 扫描循环看不到进展 │
└───────────────────────────────────────────┘
文件系统 swap 是另一处低效。NFS、SMB 上的 swap 文件,读写要先经 mm/page_io.c 里的通用 swap_ops,再兜到文件系统的 swap_rw 方法,中间串着两层间接调用。
问题
- 同步 swap 设备被批量攒写,writeback 位滞留,LRU 扫描循环看不到进展
- zram 等设备的逐 folio 直写优势丢失
- 文件系统 swap 的读写经通用 swap_ops 再兜 swap_rw,串两层间接调用
方案
把两件不相干的事一次做完:同步设备恢复逐 folio 直写,文件系统自供 swap_ops。
两条独立改进
┌───────────────┐
│ swap_ops 更新 │
└───────┬───────┘
┌────────────────────┴────────────────────┐
▼ ▼
┌──────────────────────────────────┐ ┌─────────────────────────────────┐
│ 同步设备逐 folio 直写,writeback │ │ 文件系统自供 swap_ops,读写直达 │
│ 位及时清除 │ │ │
└──────────────────────────────────┘ └─────────────────────────────────┘
逐 folio 直写落在 swap_add_folio() 的攒批判断上:bvec 攒满 SWAP_CLUSTER_MAX 照例提交,但同步设备的写不等攒满,每个 folio 进数组后立即提交。块设备的批量行为不变。
文件系统一侧分两步走。先新增 include/linux/swap_ops.h,把 swap_iocb、swap_ops、swap_io_ctx 的声明开放给 mm/page_io.c 之外的实现;再把通用层里的 swap_fs_submit 重构成 swap_fs_prepare_rw(),在调用方栈上初始化 iov_iter,NFS 与 SMB 借此各写各的 swap_ops,激活时经 swap_fs_activate() 挂上。swap 读写从此直达文件系统,通用层兜转消失。
收益
作者未提供性能数据,从代码逻辑推断的预期收益:
- zram 等同步 swap 设备恢复逐 folio 直写,writeback 位随写随清,LRU 扫描看到真实进展
- 文件系统 swap 的读写不再串两层间接调用
- swap_ops 声明独立成头文件,后续其他 swap 后端可直接接入