背景

高阶 GFP_ATOMIC 分配在 zone 处于 low/min 水位之间时,可能经 alloc_flags 放宽后由 get_page_from_freelist 重试,落到 PCP list 上以 ALLOC_HIGHATOMIC 服务。这种分配命中 PCP 后,会接着调一次 reserve_highatomic_pageblock,意图是给高阶原子预留扩容。

┌────────────────────────────────────┐
│  高阶原子分配经 per-CPU 缓存命中   │
└─────────────────┬──────────────────┘
                  ▼
┌────────────────────────────────────┐
│   命中后每次都尝试扩高阶原子预留   │  缓存里的页是不可移动型,永不是高阶原子型
└─────────────────┬──────────────────┘
                  ▼
┌────────────────────────────────────┐
│ 每次新转一个块为高阶原子,块多空闲 │  吃满 zone 上限,正常分配被挤、过早回收
└────────────────────────────────────┘

但这里有个隐藏前提没对上:PCP list 上的页是不可移动迁移类型(unmovable),而 MIGRATE_HIGHATOMIC 的页根本不出现在不可移动 PCP list 上。所以每次 reserve 都发现"这个页不是高阶原子型",于是每次都新转一个 pageblock 成 MIGRATE_HIGHATOMIC。

问题

  • 高阶原子 PCP 命中后每次都扩高阶原子预留
  • PCP 页是不可移动型,永不是高阶原子型
  • 每次新转一个 pageblock,多数空闲
  • 吃满 zone 1% 上限,挤占正常分配、过早触发回收

方案

核心是认清"能被 PCP 服务的高阶原子分配,说明还没到需要扩预留的程度",把扩预留挪到真正需要它的伙伴路径。

旧:per-CPU 缓存命中后每次扩预留
┌────────────────────────────┐
│ 缓存页不可移动,每次转新块 │
└─────────────┬──────────────┘
              ▼
┌────────────────────────────┐
│ 预留膨胀吃满上限,块多空闲 │
└────────────────────────────┘

新:扩预留只在伙伴路径
┌────────────────────────────┐
│      缓存命中不扩预留      │
└─────────────┬──────────────┘
              ▼
┌────────────────────────────┐
│ 缓存空则回退伙伴,失败才扩 │  预留回到正常水位,不挤正常分配
└────────────────────────────┘

具体地,把 highatomic reserve 的检查与扩展移到 buddy 路径;PCP 空时 ALLOC_HIGHATOMIC 不再 refill PCP,而是直接回退 rmqueue_buddy。在 rmqueue_buddy 里仍先试 MIGRATE_HIGHATOMIC 的 buddy list(与以前一样),只有它失败才扩展 reserve。这样 PCP 仍提供快速的高阶原子分配,但不再每次命中都无谓地扩预留。

收益

作者未提供量化 benchmark。从机制与真实场景推断的预期收益:

  • 高阶原子预留不再被 PCP 命中无谓扩容,回到正常水位
  • 不再有无故的(多数空闲)高阶原子 pageblock 吃掉 zone 1% 上限
  • 正常分配不再被挤占,不再因此过早触发回收

作者在真实系统上观测到:突发内存活动配 GFP_ATOMIC SLUB(新 slab 用 GFP_ATOMIC、偶有低于 low 水位的 get_page_from_freelist 重试)会让 MIGRATE_ATOMIC pageblock 持续累积;修复后测试稳定,预留建到正常水位、无 highatomic 失败。