0/17 已展开

LLM 分析

内存 cgroup 分层限额 (Tiered Memcg Limits)

系列概况

  • 标题:[RFC PATCH v3 00/14] Introduce tiered memcg limits
  • 作者:Joshua Hahn joshua.hahnjy@gmail.com
  • 版本:RFC v3(14 个 patch + 1 封 cover letter + 2 封回复)
  • 规模:跨多个子系统(mm/memcontrol、mm/memory-tiers、mm/vmscan、mm/migrate、mm/page_alloc、kernel/sched/fair)
  • 修改文件:include/linux/memcontrol.h、include/linux/memory-tiers.h、mm/memcontrol.c、mm/memory-tiers.c、mm/vmscan.c、mm/migrate.c、mm/page_alloc.c、mm/memcontrol-v1.c、kernel/sched/fair.c
  • 代码统计:14 patch 累计约 700+ 行新增与重构,单 patch 之间相互依赖
  • Message-ID:20260807202059.2620949-1-joshua.hahnjy@gmail.com(cover letter),后续 patch 编号 -2 ~ -15,回复 -16、-17
  • 完整性:14/14 patch 全部呈现;附 Song Hu 提醒 calculate_high_delay() 溢出与 Joshua 回复

补丁目的

让 cgroup 限额不再只看"总内存脚印",还能限制"内存放在哪一层"。
在 HBM / DRAM / CXL / PMEM 这类分层内存机器上,先启动的 cgroup 可能占满高速层,把后启动的挤到慢速层,从而破坏性能隔离。
本系列按 tier 容量比例缩放 memcg 限额,让行为良好的 workload 也能拿到公平份额的高速层。

旧流程的问题

  • memory.{max, high, low, min} 只算总页数,不关心页落在哪个 tier。
  • 启动顺序决定高速层占用比例,启动越晚越吃亏。
  • demotion / migration 之后,cgroup 在高速层的占用不会自动回收。
  • NUMA balancing 的 promotion 只看节点水位,不看 cgroup tier 是否还有预算。

新流程

  • 启动参数 cgroup.memory=tiered_limits 打开特性;static key 门控让未启用系统走零开销路径。
  • 每个 memcg 在每个 tier 上挂一个 page_counter;限额按 tier 容量 / 系统总内存比例分配。
  • 五处 enforcement:
    1. 分配时 steer(仅 fastpath、非 THISNODE、MIGRATE_MOVABLE)。
    2. try_charge 时对落在已耗尽 tier 的 folio 做 targeted reclaim。
    3. memory.high worker 在后台异步回收。
    4. 跨 tier migration 在目的 folio 上单独 charge。
    5. NUMA promotion 在目标 tier 已满时 throttle。

Patch 概览

  • 01/14:引入 cgroup.memory=tiered_limits 启动参数 + static key,与 v1 冲突时输出警告。
  • 02/14:重构 try_charge_memcg 让 v1/v2 分支更清晰。
  • 03/14:memory-tiers 新增 nid → tier_slot 密集映射,hotplug 时重建。
  • 04/14:mem_cgroup 分配 per-tier page_counter 数组,父链对齐。
  • 05/14:写 memory.* 文件或 hotplug 时按 tier 容量比例缩放限额。
  • 06/14:try_to_free_mem_cgroup_pages 新增 nodemask 参数,做定点回收。
  • 07/14:try_charge/uncharge/migrate/replace_folio 同步 tier 计数器。
  • 08/14:memory.low / memory.min 改为 tier-aware 的保护。
  • 09/14:memory.high 改为 tier-aware 的 enforcement(异步回收路径)。
  • 10/14:memory.max 改为 tier-aware 的 enforcement(同步 charge 路径)。
  • 11/14:跨 tier migration 在目的 folio 上单独 charge。
  • 12/14:migration / folio 替换后 kick async reclaim。
  • 13/14:NUMA promotion 在目标 tier 已满时拒绝放行。
  • 14/14:page_alloc 在 fastpath 上绕开已耗尽的 tier。

关键实现

                +-------------------+
                | memcg A           |
                |   memory.max=100G |
                +---------+---------+
                          |
            tier-proportional scaling (patch 05)
                          |
        +-----------------+-----------------+
        |                 |                 |
   +----v----+       +----v----+       +----v----+
   | tier[0] |       | tier[1] |       | tier[2] |
   |  DRAM   |       |  CXL    |       |  PMEM   |
   |  75G    |       |  20G    |       |   5G    |
   +---------+       +---------+       +---------+
        ^                 ^                 ^
        |                 |                 |
   try_charge_memcg  (patch 07) --> tier_counter
        |                 |                 |
        +----- exceeded? -------------------+
                       |
                       v
             targeted reclaim on tier nodes (patch 06/09/10)

类比

想象宿舍楼有高速电梯(DRAM)和低速电梯(CXL/PMEM)。
老规则是"每个宿舍总共住多少人",先搬进来的宿舍把高速电梯全占满,后来搬进来的只能挤慢电梯。
新规则要求每个宿舍在两部电梯上的"人数比例"必须和楼层总容量比例一致——
高速电梯只能放对应比例的人,再有人要进宿舍就先下去几个,
这样无论谁先搬进来,最终高速层的体验都是公平的。

Highlight:风险与注意点

  • memcg stock 仍是 per-memcg 资源,不区分 tier,tier max 可能被瞬时打穿(patch 10 注释明确指出,作者后续会把 stock 下沉到 page_counter)。
  • 即便关掉开关,仍会编译并保留若干静态变量与分配代码,影响微小但非零。
  • memory.max 取所有 tier 最小 margin 来判定 pile-on 防护(patch 10 解释:避免在某个 tier 已满时把另一 tier 的普通超额也误关掉)。
  • migration 在跨 tier charge 失败时返回 -EBUSY 而非 -ENOMEM,避免批处理被无限切分重试。
  • 仅支持 cgroup v2;与 v1 共存会输出 cgroup.memory=tiered_limits should not be enabled with cgroupv1 警告。
  • 已知 pre-existing bug(Song Hu):calculate_high_delay() 在 max_overage 较大时因 max_overage * max_overage * HZ 回绕为较小正值甚至 0,Joshua 认为应作为独立 fix 而非塞进本系列。
  • 分配器 steering 只影响 fastpath 的 ac->nodemask;fallback 仍可能落在已耗尽 tier 上触发 reclaim。
  • tier_capacity 依赖 zone_managed_pages 计算,hotplug 后必须通过 memtier_hotplug_callback 重新 establish,否则比例会失真。

版本变化

v2 → v3:

  • 支持任意 N 个 tier(之前仅区分 toptier vs 其余)。
  • 引入 memory.max 的 tier-aware enforcement。
  • 加入 promotion / demotion 的 tier charge 转账。
  • page allocator 加入"绕开耗尽 tier"的 steering。
  • NUMA promotion 加入"目标 tier 已满则 throttle"的 fastpath 限制。

一句话总结

本 RFC 系列把 memcg 限额按内存层级容量比例做"分级缩放",并把 enforcement 分散到分配/charge/迁移/分配器四个关键点,从而在分层内存系统上实现真正按"层"公平的 cgroup 性能隔离。