背景

本地节点的透明大页分配(__GFP_THISNODE)有一条设计原则:只做 compaction,不触发 reclaim。目的是让本地大页缺页保持低延迟,不因为本节点内存吃紧就去回收页面,更不去别的节点拿。THP 页错误的整体分配方案分三步:

  • 第一步:本地节点仅压缩、不回收
  • 第二步(madvised VMA 或同步 compaction):才跨节点、按全局 defrag 决定努力程度
  • 第三步:回退到基础页
┌──────────────────────────────────────┐
│ 本地节点的透明大页分配应只压缩不回收 │  设计承诺压缩之后不再回收
└──────────────────┬───────────────────┘
                   ▼
┌──────────────────────────────────────┐  压缩被推迟或因基础页不足而跳过时才不回
│    但回收只在部分压缩结果下被规避    │  收
└──────────────────┬───────────────────┘
                   ▼
┌──────────────────────────────────────┐
│   压缩实际执行却失败时回收照常继续   │  本地节点近高水位时缺页触发过度回收
└──────────────────────────────────────┘

问题出在第一步对"不回收"的兑现。回收并非在所有 compaction 结果下都被规避,只在 compaction 被推迟(deferred)或因基础页不足而被跳过(skipped)时才不回收;一旦 compaction 真正执行却失败,回收就会继续推进。这个缺口由来已久,先前两次收紧回收与压缩努力的改动走对了方向,却没能堵上它。

问题

  • 本地透明大页分配应只压缩不回收,但回收规避有缺口
  • 回收仅在压缩被推迟或跳过时才被规避
  • 压缩实际执行却失败时,回收照常继续
  • 本地节点接近高水位时缺页触发过度回收

方案

在慢路径加一道 __GFP_THISNODE 检查:本地限定的透明大页分配,当初次 compaction 未能分出页时,就不再继续 reclaim 与 compaction,直接退出慢路径。

旧:压缩失败后仍继续回收
┌────────────────────────────┐
│  初次压缩未能分出透明大页  │  回收与压缩继续在慢路径推进
└─────────────┬──────────────┘
              ▼
┌────────────────────────────┐
│ 本地节点近高水位时过度回收 │  呈节点级回收行为,非设计意图
└────────────────────────────┘

新:本地限定的大页分配压缩失败即停
┌────────────────────────────────────┐
│ 本地限定的透明大页分配初次压缩失败 │  检测到本地限定标记即停止慢路径
└─────────────────┬──────────────────┘
                  ▼
┌────────────────────────────────────┐
│ 不再继续回收与压缩,回归无回收设计 │  其它节点有空闲时不强行回收本节点
└────────────────────────────────────┘

检查落在初次 compaction 失败之后。对于 __GFP_THISNODE 分配,compaction 既然没成,后续的回收与重试压缩都违背"本地无回收"的承诺,应当停止;这样本地大页缺页要么靠初次 compaction 成功,要么回退基础页,不再拖出一条回收链。非 __GFP_THISNODE 的分配(第二、三步)行为不变。这个过度回收的可能性并非最近才引入,而是自恢复本地大页分配以来就以某种形式存在,本 patch 把最后这道缺口补上。

收益

作者未提供量化 benchmark,问题与修复效果基于 customer 报告与 vmstat 观测。

  • 修复前:本地 NUMA 节点接近高水位时,透明大页缺页触发过度回收;compact_fail 计数上升,回收照常推进;其它节点尚有空闲,却呈现节点级回收(zone_reclaim_mode 类)行为,并非设计意图
  • 修复后:本地限定的大页分配初次压缩失败后不再继续回收;回归"本地无回收"的设计意图;其它节点有空闲时不强行回收本节点