背景

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。