背景

khugepaged 是内核的后台透明大页折叠线程,把散落的 4KB 基页合并成大页,借此削减 TLB 未命中。在此以前,它只会把一个 PMD 区间(x86_64 上 512 个 PTE、2MB)折叠成一条 PMD 大页:扫描区间、统计 occupied 页,只有占用率满足 max_ptes_none 才尝试折叠。

┌───────────────────────────────┐
│ khugepaged 扫描一个 PMD 区间  │
└───────────────┬───────────────┘
                ▼
┌───────────────────────────────┐
│   统计区间内 occupied 页数    │
└───────────────┬───────────────┘
                ▼
┌───────────────────────────────┐
│     占用率撑不起整个 PMD      │
└───────────────┬───────────────┘
                ▼
┌───────────────────────────────┐  大量部分占用的区间被整体放弃,其中连续数十页
│ 整个区间被放弃,保持 4KB 基页 │  本可装下一个更小的 mTHP,TLB 收益白白流失
└───────────────────────────────┘

这种"全 PMD 或全不"的判断留下大片盲区。进程的匿名内存很多是部分占用的:一段 2MB 里散落着若干已用页和空洞,占用率撑不起一个 PMD 大页,整段就被放弃,继续以 4KB 基页承载。可这些被放弃的段里,往往有连续几十页早就够装下一个更小的 mTHP。mTHP(多尺寸透明大页)介于 4KB 基页与 2MB PMD 之间,前台缺页路径早已能按需分配,但 khugepaged 的后台折叠一直只认 PMD,眼睁睁放过这些中等粒度的合并机会。本系列把后台折叠扩展到 mTHP。

问题

  • khugepaged 后台折叠只能产出 PMD 大页
  • 占用率不足整 PMD 的区间被整体放弃
  • 其中连续数十页本可合并为更小 mTHP 却无法受益
  • 大量部分占用区域保持 4KB 页,TLB 压力居高不下

方案

整套改动的核心,是把 khugepaged 折叠流程里原本绑在一起的两件事拆开:判断一个区间值不值得折叠,和决定折成多大。

旧流程在扫描阶段就把两者一并定了,按 max_ptes_none 衡量整个 PMD 区间,不达标就放弃整段。新流程让扫描退回成纯粹的记录,把折叠决策挪到扫描之后。

扫描阶段:bitmap 记录整段占用
┌──────────────────────────────────┐
│      遍历 PMD 区间每个 PTE       │
└────────────────┬─────────────────┘
                 ▼
┌──────────────────────────────────┐  只要启用任意 mTHP,扫描就放开空页上限,把
│ 对每个 occupied 页在 bitmap 置位 │  整段占用情况记全,不再提前放弃
└──────────────────────────────────┘

决策阶段:每个自然对齐段挑最大 mTHP
┌──────────────────────────────────────┐
│    从 offset 0 按自然对齐切分子段    │
└──────────────────┬───────────────────┘
                   ▼
┌──────────────────────────────────────┐  不达标则 order 递减同段重试;越过该段
│ 子段 occupied 数达标则折叠为最大可用 │  后新 order 由新 offset 的对齐决定;含
│ mTHP                                 │  swap 或共享页的段跳过,避免折叠反复提
└──────────────────────────────────────┘  拔

扫描只记录,决策后移

只要启用了任意 mTHP order,扫描阶段就把 max_ptes_none 放开到极限,遍历完区间内每一个 PTE,对每个 occupied 页在一张 bitmap(mthp_present_ptes)上置位。整段占用情况被原样记下,扫描不再因占用率提前放弃。当 PMD 是唯一启用的 order 时,扫描沿用旧的 max_ptes_none 行为,对已有配置没有任何改变。

在对齐窗口里挑最大 mTHP

折叠决策交给新函数 mthp_collapse,用那张 bitmap 在每个自然对齐的子段里挑最优 mTHP。大页必须按自身大小自然对齐,因此一个 offset 上能用的最高 order 由它的对齐位数决定(__ffs(offset)):offset 0 可用 PMD order,offset 4 只能从 order 2 起。算法从 offset 0 出发,每个 offset 先取对齐允许的最高 order,统计该段内 occupied 页数,满足该 order 的阈值就折叠;不满足则 order 递减、在同一段重试;一旦折叠成功或最小 order 耗尽,就越过这段、按新 offset 的对齐算下一个 order,直到铺满整个 PMD 区间。每个对齐窗口都被尝试装下尽可能大的 mTHP,原本被整体放弃的局部机会就此一一兑现。

防止折叠爬升

一道约束贯穿始终,防止折叠"爬升"(creep)。一次折叠会把零散页聚拢成一个大页,原本空洞的位置被填实,下一轮扫描时 occupied 数升高,又会满足更大 order 的条件,于是 mTHP 被不断提拔到更高 order,来回折腾。为掐断这个正反馈,mTHP 折叠被加上两道闸:max_ptes_none 只接受 0 或上限值(按 order 缩放),含换出页或共享页的段直接跳过。这类段折叠后会引入新的空洞页,正是爬升的温床;前一道闸由 collapse_max_ptes_none 把阈值落到每个 order。

范围上,当前只有匿名的后台折叠用上 mTHP;madv_collapse 与文件、shmem 路径仍只走 PMD。

收益

作者未提供性能数据。封面信中作者说明,系列经 x86_64、aarch64、ppc64le、s390x 多架构构建,跑过 kernel-tests、selftests/mm 及自研压测脚本,未观察到显著回归;部分场景下 collapse 延迟更优、单位工作量能扫描更多页。从代码逻辑推断的预期收益:

  • 占用率不足整 PMD 的区间里,连续数十页可被合并为 mTHP,原本流失的中等粒度大页收益被兑现
  • 更多区域获得 mTHP,TLB 未命中下降,内存访问延迟改善
  • 无须先启用 PMD THP 即可由后台折叠产出 mTHP,降低使用门槛
  • per-order 失败统计与 tracepoints 使 mTHP 折叠效果可观测、可按 order 调优