背景
balance_dirty_pages() 里曾有一段逻辑:在每次检查脏页时,如果当前没有 writeback 在跑,就无条件启动一次 background writeback。这段逻辑给"IO 被限流等待脏页回写"兜底:确保只要 writer 还在被节流,就一定有 writeback 在跑,不至于干等。
┌──────────────────────────────────────┐
│ 移除笔记本模式时,顺带删了无条件回写 │
│ 启动 │
└──────────────────┬───────────────────┘
▼
┌──────────────────────────────────────┐
│ 限流设备和组级节流场景受害 │ 全局阈值未超但局部已限流
└──────────────────┬───────────────────┘
▼
┌──────────────────────────────────────┐
│ 限流等待回写,却没有回写在跑 │ 严重停顿,吞吐暴跌数百倍
└──────────────────────────────────────┘
64dd89ae01f2(移除 laptop_mode)清理 laptop_mode 相关代码时,把这段无条件 writeback 启动也一并删了。laptop_mode 和无条件启动其实是两回事,删它是误伤。
问题
- 移除笔记本模式时误删无条件回写启动
- 限流设备和组级节流场景受害
- 全局阈值未超但局部已限流
- 限流等待回写却无回写在跑,严重停顿
方案
恢复那段被误删的无条件 writeback 启动:每次进 balance_dirty_pages,如果没有 writeback 在跑就启动一次。
旧:限流时无回写在跑
┌──────────────────┐
│ 节流等脏页回写 │
└────────┬─────────┘
▼
┌──────────────────┐
│ 没有回写进程在跑 │
└──────────────────┘
新:节流必有回写在跑
┌────────────────────┐
│ 恢复无条件启动回写 │
└─────────┬──────────┘
▼
┌────────────────────┐
│ 限流时回写保证在跑 │ 移除笔记本模式只是顺带误删,该逻辑与笔记本模式无关
└────────────────────┘
这样无论 IO 因为什么原因被限流,都一定有 writeback 在跑。两种最容易受害的场景:
- strictlimit BDI 的 per-wb 阈值已超、而全局脏页数尚未触及后台回写阈值
- memcg 自己的脏页阈值已超而全局未超
之前这两种情况下 IO 被限流等待回写,却没有 writeback 在跑,等于干等;恢复后不再有这个空档。
收益
作者提供了量化回归数据(fuse,buffered write):
| 指标 | 修复前(误删后) | 修复后(恢复后) | 变化 |
|---|---|---|---|
| buffered write 吞吐 | 2,000 KiB/s | 1,400 MiB/s | 恢复约 700 倍 |
移除 laptop_mode 误删无条件 writeback 启动后,fuse 上 buffered write 从 1,400 MiB/s 暴跌到 2,000 KiB/s(约 700 倍回归);恢复后回到 1,400 MiB/s。