背景
compaction 通过把已用页迁移到一起,腾出高阶连续空闲页。compaction 时,free scanner 负责找空闲页当迁移目标,它一次最多隔离 COMPACT_CLUSTER_MAX(32)个页,凑够一个 migration batch 就停手。在判断一个 zone 是否"适合"compaction,以及 kswapd 回收时要不要继续追求某个高阶时,内核都用 compact_gap(order) 在 watermark 之上加一道 headroom:zone 得在 watermark 之上额外备好 compact_gap(order) 个空闲页,才认为 compaction 有施展空间。
compact_gap(order) 原本返回 2 << order,随阶指数增长:order-0 是 2 页,到 order-9(透明大页)就是 1024 页。这道 headroom 的本意,是给 free scanner 预留足够的迁移目标。
┌────────────────────────────────────┐
│ headroom 随 order 指数增长 │
└─────────────────┬──────────────────┘
▼
┌────────────────────────────────────┐
│ order-9 大页时 headroom 达 1024 页 │ 空闲页扫描器单批最多隔离 32 页
└─────────────────┬──────────────────┘
▼
┌────────────────────────────────────┐
│ headroom 过度预留 32 倍 │ 回收线程徒劳扫描,适合性假性失败
└────────────────────────────────────┘
但 headroom 的算法默认了 free scanner 一次要处理 2 << order 页,而 scanner 的实际工作集被 COMPACT_CLUSTER_MAX 死死卡在 32 页。对 order-9 来说,headroom 预留 1024 页,scanner 却只消 32 页就够,整整过度预留 32 倍。
问题
- headroom 按 order 指数增长,free scanner 容量却恒为 32 页
- order-9 大页 headroom 预留 1024 页,过度 32 倍
- 碎片化主机上 kswapd 为够到虚高 headroom 徒劳回收,仅 18% 能达标
- 46% 的 order-9 适合性检查假性失败,compaction 启动被推迟
方案
把 headroom 拉回 scanner 的真实容量:compact_gap 改为返回阶算值与 COMPACT_CLUSTER_MAX 的较小者。
改动虽只有一道 min,compact_gap 却同时喂给两个关键启发式,牵一发动全身。
cap 前:headroom 按阶无上限增长
┌────────────────────────────────┐
│ order-9 时 headroom 达 1024 页 │
└───────────────┬────────────────┘
▼
┌────────────────────────────────┐
│ 远超扫描器单批 32 页容量 │ 回收线程死磕高阶回收,zone 适合性假性失败
└────────────────────────────────┘
cap 后:headroom 封顶于单批容量
┌─────────────────────────────────┐
│ headroom 取阶算值与 32 的较小者 │
└────────────────┬────────────────┘
▼
┌─────────────────────────────────┐
│ 反映扫描器真实容量 │ 更早降级交班 compaction,更早启动
└─────────────────────────────────┘
第一处是 zone 适合性判断。__compaction_suitable 用 watermark 加 headroom 衡量一个 zone 值不值得 compaction。headroom 虚高时,明明空闲页足够 scanner 工作的 zone 也会被判"不适合",compaction 迟迟启动不了;headroom 收窄到 32 页后,这道门槛落到 scanner 真正需要的水平,compaction 得以更早启动。
第二处是 kswapd 的高阶回收降级。kswapd 为某个 order 回收时,若 zone 空闲页够不到 watermark 加 headroom,就继续往该 order 回收;headroom 虚高让 kswapd 在空闲页其实已够 order-0 平衡时仍死磕高阶。headroom 收窄后,kswapd 更早认清高阶无望、降级到 order-0 平衡,把原本的高阶需求交给 kcompactd 接手。这正是 A/B 数据里"kswapd 降级到 order-0 的次数从 0 升到 28"的由来。
阶算值在 order 0-4 本就不超过 32,所以 cap 只对 order 5 及以上(透明大页所在区间)生效,低阶行为完全不变。
收益
作者在两组环境下做了 A/B 测试。
Instagram 生产主机(基于 v6.13,64GB,60s 测量,未打补丁 43 台 / 已打补丁 59 台):
| 指标 | 未打补丁 | 已打补丁 |
|---|---|---|
| pgscan_kswapd(每台均值) | ~1.6M | ~449K |
| 回收效率(steal/scan) | 83.8% | 91.0% |
| 单次 compaction 成功率(success/stall) | 2.1% | 28.3% |
| THP 成功率(alloc/alloc+fallback) | 4.9% | 17.2% |
| forced lru_add_drain(每台均值) | ~107K | ~64K |
相似 workload(基于 mm-new,3 次 60s run):
| 指标 | 未打补丁 | 已打补丁 |
|---|---|---|
| kswapd 降级到 order-0(均值) | 0 | 28 |
| thp_fault_fallback(均值) | 1217 | 738 |
| pgscan_kswapd(均值) | 6328 | 3773 |
| pgsteal_kswapd(均值) | 5657 | 3243 |
kswapd 扫描量大幅下降(生产环境 1.6M → 449K),回收效率与 compaction、THP 成功率显著提升,THP 缺页回退明显减少;kswapd 降级到 order-0 的次数从 0 升到 28,正是 headroom 收窄后 kswapd 更早把高阶需求交班 kcompactd 的直接证据。