背景

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 的直接证据。