背景

THP 全局策略(transparent_hugepage=never/madvise/always)在 __thp_vma_allowable_orders() 里门控所有非匿名 VMA 的 huge fault 分发:never 直接拒绝,madvise 只放行带 VM_HUGEPAGE 的 VMA。DAX VMA 是例外,它有提前返回:khugepaged 不去 collapse DAX,但 page fault 照常放行。MADV_COLLAPSE 的强制 collapse 同样不受这组策略门控。

special huge VMA(由 vma_is_special_huge() 标识,典型是 VM_PFNMAP 的设备 BAR 映射)与 DAX 共享同一本质:直接把物理地址映射进页表。以 VFIO PCI 直通的大 BAR 设备为例,VFIO 驱动注册了 .huge_fault 处理函数(分发到 vmf_insert_pfn_pmd/pud),QEMU 的 vfio_region_mmap() 也对齐了 BAR 映射,大页映射的两个前提都具备,但策略检查在到达「信任 huge_fault 处理函数」的代码之前就把请求拦下了。

非匿名 VMA 的 THP 策略门控
                ┌───────────────────────────────┐
                │ 大页缺页分发先过 THP 策略检查 │
                └───────────────┬───────────────┘
             ┌──────────────────┴──────────────────┐
             ▼                                     ▼
┌──────────────────────────┐     ┌───────────────────────────────────┐
│ DAX VMA 提前返回直接放行 │     │ special huge VMA 直接映射物理地址 │
│                          │     │ 却被策略拦下退化为 4 KiB 映射     │
└──────────────────────────┘     └───────────────────────────────────┘

问题

  • special huge VMA 与 DAX 同样直接映射物理地址,却被 THP 策略门控
  • never 或 madvise 策略把缺页拦在驱动 .huge_fault 之前
  • 单 GPU 的 256 GB BAR 按 4 KiB 映射需 6700 万次缺页
  • 8 GPU 主机启动 VM 时 99.98% CPU 耗在 VFIO BAR 映射路径
  • 触发条件并不少见:内核命令行 transparent_hugepage=never
  • tuned cpu-partitioning 继承 network-latency 设为 never
  • RHEL 默认 madvise,而 VFIO VMA 没有 VM_HUGEPAGE

方案

新增 vma_file_bypass_thp_tuneables() 集中判定豁免条件,把注册 huge_fault 的 VM_PFNMAP 映射移出 THP tuneables 的管辖。

THP tuneables 约束的是大页带来的内存分配、compaction、split 与 reclaim 代价,而 PFN 映射一样都不涉及:vm_normal_folio() 对 PFN 映射返回 NULL,这些 folio 的引用计数由驱动而非 core mm 维护,不参与 reclaim 与 compaction,本来就不是 THP 机制的对象。因此豁免只覆盖 VM_PFNMAP 且 huge_fault 非空的映射:VM_MIXEDMAP 映射可能包含已分配内存,不单独豁免;没注册 huge_fault 的 VMA 照旧受策略门控。

两个豁免条件都在 vma_file_bypass_thp_tuneables() 里:

static bool vma_file_bypass_thp_tuneables(const struct vm_area_struct *vma,
        enum tva_type type)
{
    const bool has_huge_fault = vma->vm_ops->huge_fault;

    if (type == TVA_FORCED_COLLAPSE)
        return true;
    if (vma_test(vma, VMA_PFNMAP_BIT) && has_huge_fault)
        return true;
    return false;
}
豁免判定先于策略门控
                                ┌─────────────────────────┐
                                │ 非匿名 VMA 先过豁免判定 │
                                └────────────┬────────────┘
                     ┌───────────────────────┴───────────────────────┐
                     ▼                                               ▼
┌──────────────────────────────────────────┐     ┌────────────────────────────────────────┐
│ 命中豁免直接放行,MADV_COLLAPSE          │     │ 未命中看全局策略,always 放行,madvise │
│ 本就忽略策略,注册 huge_fault 的         │     │ 需 VM_HUGEPAGE,never 拒绝             │
│ VM_PFNMAP 映射不经 reclaim 与 compaction │     │                                        │
└──────────────────────────────────────────┘     └────────────────────────────────────────┘

通过判定的 VMA 按请求用途分发:

  • 缺页路径直接信任驱动注册的 huge_fault 回调
  • smaps 与缺页共用同一豁免判定,THPeligible 上报与缺页实际行为始终一致
  • collapse 路径仍只认普通文件(file_thp_enabled()),khugepaged 对这类 VMA 本来就没有可 collapse 的内容
通过判定后按用途分发
                                 ┌──────────────────────┐
                                 │ 通过判定后按用途分发 │
                                 └──────────┬───────────┘
          ┌─────────────────────────────────┴─────────────────────────────────┐
          ▼                                 ▼                                 ▼
┌────────────────────┐      ┌───────────────────────────────┐      ┌─────────────────────┐
│ 缺页路径信任驱动注 │      │ smaps 走同一判定,THPeligible │      │ collapse 路径只认普 │
│ 册的 huge_fault 回 │      │ 如实上报                      │      │ 通文件              │
│ 调                 │      │                               │      │                     │
└────────────────────┘      └───────────────────────────────┘      └─────────────────────┘

drm_gem_shmem_mmap() 与 panthor_gem_mmap() 建立的是 shmem folio 的 huge PFN 映射,同样命中豁免。这些 folio 在 drm_gem_get_pages() 里已标记 unevictable,即使不然也过不了 __remove_mapping() 的引用检查,纳入豁免没有副作用。

整个非匿名 VMA 的 THP 检查随之收进 vma_can_map_huge_file(),豁免判定、策略放行与按用途分发各自独立成 helper。

收益

指标 Before After 改善
单 GPU 256 GB BAR 的映射缺页次数 6700 万次(4 KiB 粒度) 几千次(PMD/PUD 粒度) 降约 4 个数量级
单个 128 GiB GPU BAR 的映射耗时 20.7 秒 0.4 秒 降 98.1%
4 块 H100 GPU 的虚拟机启动耗时 97 秒 17 秒 降 82.5%

后两行来自 Saravanan 的实测:4 块 H100(各 128 GiB BAR1)的 HGX 主机,cloud-hypervisor 虚拟机,THP 策略为 madvise。

其他预期收益:

  • THP 策略回归本位,只约束真正涉及内存分配的 VMA
  • smaps 的 THPeligible 如实反映 huge PFN 映射的大页资格