背景

"dying memory cgroup"是个持续多年的顽疾:用户不再使用的 memcg,因被某些内存对象长期持有引用而无法释放,元数据一直占用着内存。slab 对象、非 slab 内核分配、per-CPU 对象早就改成对 object cgroup 记账(不持有原 memcg 引用),但 LRU 页(匿名页与文件页)仍在分配时记账、一直持有原 memcg 引用直到被回收。

┌──────────────────────────────────────┐
│ 页面在分配时记账,长期持有原 cgroup  │
│ 引用                                 │
└──────────────────┬───────────────────┘
                   ▼
┌──────────────────────────────────────┐
│ cgroup 已不再使用,却被页面 pin 住无 │
│ 法释放                               │  文件页尤其严重,被重复作业反复访问
└──────────────────┬───────────────────┘
                   ▼
┌──────────────────────────────────────┐
│      垂死 cgroup 堆积,内存浪费      │  显著降低页回收效率
└──────────────────────────────────────┘

文件页比匿名页更棘手:它可以跨 memcg 共享、生命期可能超过所属 memcg,还经常被同一作业的后续实例(重启进新 cgroup)反复访问。结果这些文件页把旧 memcg 长期 pin 住,垂死的 memcg 不断堆积,既浪费内存,又显著降低页回收效率。

问题

  • LRU 页持有原 memcg 引用直到回收
  • 文件页跨 cgroup 共享、被重复作业反复访问
  • 垂死 memcg 被 pin 无法释放、不断堆积
  • 内存浪费、页回收效率显著降低

方案

核心是让 LRU folio 不再 pin 其 memcg:改对 object cgroup 记账,folio 的 memcg_data 字段指向 object cgroup 而非原 memcg。

旧:页面 pin 住原 cgroup
┌────────────────────────────────┐
│ 页面持有原 cgroup 引用直到回收 │
└───────────────┬────────────────┘
                ▼
┌────────────────────────────────┐
│   cgroup 无法释放,垂死堆积    │
└────────────────────────────────┘

新:页面只对对象 cgroup 记账
┌─────────────────────────────────────────┐
│ 页面改指向对象 cgroup,不 pin 原 cgroup │
└────────────────────┬────────────────────┘
                     ▼
┌─────────────────────────────────────────┐
│   cgroup 可及时释放,页面记账不受影响   │  读锁与 lruvec 锁重试保证绑定稳定
└─────────────────────────────────────────┘

但 folio 不再 pin memcg 后,访问者取到的 memcg 或 lruvec 可能在被使用时被释放,整套修复按访问者对绑定稳定性的依赖分别处理。

保护临时访问

只读计数、不依赖 folio 与 memcg 绑定稳定性的访问者,只需要访问期间 memcg 不被释放。这类访问在进入时加 RCU 读锁,或短暂获取一个 memcg 引用、访问结束即释放,从而在解绑后仍能安全取到 memcg 或 lruvec。

稳定持锁绑定

依赖绑定稳定性的操作(典型是持有 lruvec 锁的 LRU 维护)要求锁内 folio 始终归属同一个 lruvec。锁内增加一道校验:一旦发现 folio 的归属 memcg 与当前 lruvec 不一致,说明 cgroup 正在 reparenting、绑定发生变更,就放弃锁重新获取。cgroup 死亡时,则把它名下的 object cgroup 与 LRU 链表整体迁移到父 cgroup,完成 reparenting。

如此切换之后,LRU folio 与 kmem folio 的记账指针都指向单个 object cgroup,slab folio 则指向 object cgroup 向量。

收益

作者未提供量化 benchmark。从机制与回归动机推断的预期收益:

  • 垂死 memcg 不再被 LRU folio 长期 pin,能及时释放
  • 文件页不再因被重启进新 cgroup 的同一作业反复访问而 pin 住旧 memcg
  • 垂死 memcg 不再堆积,减少内存浪费
  • 页回收不再被垂死 memcg 的元数据拖累,效率改善

作者用复现脚本验证:修复后垂死 memcg 数量不再显著增长。封面信定性描述问题为"memory leaks or inefficiencies"、"memory wastage"、"significantly reducing the efficiency of page reclamation"。