0/5 已展开

LLM 分析

sched/numa:解除 VMA 扫描过滤器对 tiering promotion 的封锁

系列概况

  • 标题: [PATCH 0/2] sched/numa: stop VMA scan filters from gating promotion
  • 作者: Gregory Price (Meta) gourry@gourry.net
  • 版本: 无显式版本号,按 v1 处理(作者已声明下周发 v2)
  • 规模: 3 个文件,+47 / -10
  • 修改文件: include/linux/mm_types.hkernel/sched/fair.cmm/mempolicy.c
  • 代码统计: Patch 1:+32/-9;Patch 2:+16/-2
  • Message-ID: 20260904182006.1562449-1/2/3-gourry@gourry.net;回复 apsWE-y0nV5r1_5Z@gourry-fedora-PF4VCD3FapsX_E8GBzfp6lui@gourry-fedora-PF4VCD3F
  • 完整性: 完整 2/2 系列 + 2 封作者自回复(自我披露的 v2 计划),无 maintainer 评审

补丁目的

NUMA balancing 在 task_numa_work() 里对 VMA 做两层过滤——per-VMA PID filter(要求扫描任务本任务曾经 hint-fault 过该 VMA)和 read-only file mapping 排除——这两条规则源自 socket-residency 时代:跳过 VMA 顶多损失一点扫描精度。

进入 memory tiering(DRAM + CXL)后语义反转:hint fault 不再只是 placement 信号,而是 promotion 的入口——慢层 folio 被 promote 的唯一理由就是"被扫到 + 被访问"。被过滤掉的 VMA 永远进不了 promotion 候选。结果是大型 shmem VMA 吃光扫描周期,其余 ~84G 地址空间卡在"inactive"——一个 20G hash table 全部堆到 CXL 上,DRAM 带宽从 200GB/s 跌到 150GB/s,请求延迟从 800us 飙到 5ms。

旧流程的问题

  • PID filter 自指:VMA 需要 hint fault 才能在 pids_active 里留下痕迹;没扫就没有 hint fault;没有 hint fault 扫描任务就被 vma_is_accessed() 拒绝——自我锁死。
  • RO 文件一刀切commit 4591ce4f2d22(shared library hint 优化)的前提是"RO 文本在多核间 cache 复制,迁移收益低",但 tiering 下慢层 folio 是可以 promote 的,只是一直没人扫它——91% 主程序二进制跑到了 CXL。

新流程

不删任何过滤。在 VMA 状态里加 vma->numab_state->slow_only 标志。被旧过滤拒绝的 VMA 仍被扫描一次,但只对非 toptier 节点的 folio 做 hint fault 标记;toptier folio 的标记行为与旧过滤完全一致。受限扫描可能没走完 VMA,故不能动 prev_scan_seq,否则会让该 VMA 永久跳过 vma_is_accessed()

Old flow (task_numa_work):
  +--------------------------------------------------+
  | for vma in vma_list:                             |
  |   if vma_is_ro_file(vma):       skip  (all)     |
  |   if !vma_is_accessed(mm,vma):  skip  (all)     |
  |   scan_vma(vma)                  mark all folios|
  |   prev_scan_seq = numa_scan_seq                  |
  +--------------------------------------------------+

New flow (tiering mode only):
  +--------------------------------------------------+
  | for vma in vma_list:                             |
  |   ro_file = vma_is_ro_file(vma)                  |
  |   if ro_file && !tiering:        skip            |
  |   slow_only = ro_file                            |
  |   if !vma_is_accessed(mm,vma):                   |
  |       if !tiering:               skip            |
  |       slow_only = true                           |
  |   scan_vma(vma, slow_only)                       |
  |   if !slow_only:                                 |
  |       prev_scan_seq = numa_scan_seq              |
  +--------------------------------------------------+

folio_can_map_prot_numa() gate:
  +--------------------------------------------------+
  | if node_is_toptier(nid) &&                       |
  |    (!(mode & NORMAL) || slow_only):              |
  |       return false  (this folio not marked)      |
  | else:                                            |
  |       allow hint fault                           |
  +--------------------------------------------------+

Patch 概览

  • Patch 1:在 struct vma_numab_state 增加 bool slow_onlytask_numa_work() 在 tiering 模式下把被 PID filter 拒绝的 VMA 降级为 slow_only 扫描;folio_can_map_prot_numa() 同步识别 slow_only,对 toptier folio 退避。
  • Patch 2:新增 vma_is_ro_file() helper;task_numa_work() 在 tiering 模式下不再 skip RO 文件映射,转而设 slow_only = ro_file

关键实现

/* Patch 1: restricted scan entry */
vma->numab_state->slow_only = false;
if (!vma_pids_forced && !vma_is_accessed(mm, vma)) {
    if (!(sysctl_numa_balancing_mode &
          NUMA_BALANCING_MEMORY_TIERING)) {
        vma_pids_skipped = true;
        trace_sched_skip_vma_numa(mm, vma,
                                  NUMAB_SKIP_PID_INACTIVE);
        continue;
    }
    vma->numab_state->slow_only = true;
}

/* Patch 1: restricted scan may not finish the VMA,
 * so do not disarm the get_nr_threads() horizon. */
if (!vma->numab_state->slow_only)
    vma->numab_state->prev_scan_seq = mm->numa_scan_seq;
/* Patch 2: RO file mapping takes the restricted path */
static bool vma_is_ro_file(struct vm_area_struct *vma)
{
    return vma->vm_file &&
           (vma->vm_flags & (VM_READ | VM_WRITE)) == VM_READ;
}
...
/* inside task_numa_work() */
ro_file = vma_is_ro_file(vma);
...
vma->numab_state->slow_only = ro_file;
/* mm/mempolicy.c: toptier folio suppressed under slow_only */
if (node_is_toptier(nid) &&
    (!(sysctl_numa_balancing_mode & NUMA_BALANCING_NORMAL) ||
     (vma->numab_state &&
      vma->numab_state->slow_only)))
    return false;

类比

旧过滤像门禁——住户必须先刷卡(hint fault)访客才会登记,扫楼只能记已经登记的房间。tiering 模式改成"代收快递":访客每轮把整栋楼信箱都过一遍,但只挑地下室(慢层)包裹送上去,顶楼住户的箱子纹丝不动。地面秩序不变,地下室积压才被搬到顶楼。

Highlight:风险与注意点

  • 作者自爆 bug:sashiko 指出 Patch 1 无条件 slow_only = false 会让跨两次扫描的 VMA 过早重新受扫描;Patch 2 的 slow_only = ro_file 会让正常 VMA 在首轮后被错误标为受限扫描。作者承认并将出 v2。
  • v2 计划修复if (mm->numa_scan_offset <= vma->vm_start) slow_only = false;,以及 patch 2 中对 slow_only = ro_file 语义的复核。
  • numa_scan_offset 单点救援:作者在回复中解释光标防 restart、reset_ptenuma_scan() bump sequence、mm->numa_next_scan 串行化扫描器,但 pte_protnone 重走路径的代价需要 trace 验证。
  • 首次扫描僵局:fork 后第一次扫且大 VMA 吃光周期时,小 VMA 永远凑不齐 hint fault,本 patch 只在 tiering 模式打破这一僵局,普通 NUMA 模式不受影响。
  • 正确性边界slow_only 仅控制 hint fault 标记,不影响 promotion 触发后 task_numa_fault() 报目的节点到 numa_faults[]——即只改变"谁可以发起 promotion",不改变 promotion 之后的行为。

版本变化

本帖仅 v1,作者已声明 v2 待发,关键改动:

  1. Patch 1:slow_only 复位加 guard if (mm->numa_scan_offset <= vma->vm_start)
  2. Patch 2:复核 slow_only = ro_file 对正常 VMA 的副作用(避免首次扫描后误分类)。

一句话总结

在 memory tiering 模式下,让被 per-VMA PID filter 与 RO 文件映射过滤掉的 VMA 仍以"只看慢层 folio"的受限方式参与扫描,从而为 CXL 等慢层 folio 打开 promotion 通道,toptier 标记行为不变。