RWF_DONTCACHE:一种 I/O 提示,dropbehind
读写完成后立即丢弃相关 page cache,避免只访问一次的数据长期占用缓存
文件系统侧已经落地
writeback 完成后,对标记了 dropbehind 的 folio 执行失效
裸块设备却用不上它
读写产生的页只能堆在 page cache 里
等回收器慢慢清理,既污染缓存又徒增回收开销
同一个提示,两种待遇
块设备 writeback 的完成回调运行在 IRQ 上下文
dropbehind 失效 folio 需要可睡眠的任务上下文
块设备无处把完成延迟出去,无法支持 dropbehind
文件系统(XFS)靠自己专门的 workqueue
把 dropbehind 从 IRQ 上下文搬到任务上下文
文件系统早有解法,块设备一直没有
块设备 writeback 在 IRQ 上下文完成
中断上下文不能睡眠,dropbehind 无法就地执行
块设备缺少把完成延迟到任务上下文的机制
故不支持 RWF_DONTCACHE
直接读写裸块设备的程序只能白白填充 page cache
数据库是典型;写完即弃的数据,却要承受回收开销
缺的不是提示,是能安全做失效的上下文
在 block 层加一套通用的任务上下文完成基础设施
把不能在 IRQ 上下文收尾的 bio 排到 per-CPU worker 里延迟执行
bio 完成回调当前处在原子上下文
排入本 CPU 完成队列并调度 worker
worker 在任务上下文执行回调做 dropbehind
两种方式共用同一套 per-CPU 机制
每个 CPU 一个 bio 链表 + 一个 work item,挂在 WQ_PERCPU 上
生产者关中断把 bio 塞进本 CPU 链表,链表原本为空才调度 work
BIO_COMPLETE_IN_TASK:提交时就声明要任务上下文
dropbehind 回写即属此类
bio_endio() 看到标志且 bio_in_atomic() 为真,就把 bio 交给 worker
bio_complete_in_task():回调在 bi_end_io() 里动态决定
例如 iomap 的 fserror 上报
本系列暂未用到它,但已有多个计划中的使用者
判断一律以 bio_in_atomic() 为准
它覆盖所有回调里睡眠不安全的上下文
包括不可抢占的任务上下文
配套的 CPU 热插拔回调
下线时排空该 CPU 的残余 bio,上线时重新入队
判据只有一条:此处能不能睡眠
iomap 与 buffer-head 两条回写路径
提交 dropbehind bio 时设置 BIO_COMPLETE_IN_TASK
完成回调被 worker 搬到任务上下文后再做 dropbehind
失效 folio 终于可以安全执行
删掉旧的 IOMAP_IOEND_DONTCACHE
连同 XFS 自己的 DONTCACHE workqueue 一起,统一改走新路径
一套机制,替掉两处旧实现
原子上下文检测原本只在 erofs 里
下沉为 block 层的 bio_in_atomic(),erofs 也改用它
块设备的读写路径识别并透传 RWF_DONTCACHE
这个提示才真正交到裸块设备的使用者手上
块设备第一次拿到与文件系统同等的 dropbehind 能力
读吞吐(MB/s)
1173.7 → 1828.8
+56%
写吞吐(MB/s)
3762.5 → 7699.1
+105%
测试条件
VM 单块设备,CONFIG_BUFFER_HEAD=y,结果有噪声
读:root 分区跨 45GB 区间(约 2 倍内存)
写:已禁用的 swap 分区(约 1GB),放进 244MB 的 memcg 制造回收压力
读测试区大于内存:dropbehind 让缓存不再被只读一次的数据挤占
写测试的 memcg 极小:dropbehind 直接消除了写完即弃数据的回收压力
空格
播放/暂停
←
→
逐动作
R
重播
⚠
1x