背景
dying memcg 指用户弃用后仍被内存对象引用而无法释放的 memcg。Muchun Song 与 Qi Zheng 的系列(v7.1-rc1 合入)让 LRU folio 改对 objcg 记账,memcg offline 时 objcg reparent 到父级,LRU 页不再钉住 dying memcg。
换出页走的是另一条路:swap entry 与 workingset shadow 这类寿命可能超过 cgroup 的对象存不下整个指针,用 memcg 私有 ID(memcgid)标识属主。ID 的引用计数嵌在 css 里,每个换出页持有的 ID 引用都钉住 css,整个 memcg 要等页换入并记回 online 父级才能释放。LRU 页修复之后,shmem 页换出的负载上 dying memcg 依然复现。
换出页经 memcgid 钉住 css
┌──────────────────────────────────────┐
│ swap entry 存 memcgid 标识属主 memcg │
└──────────────────┬───────────────────┘
▼
┌──────────────────────────────────────┐
│ ID 引用计数嵌在 css 里,每个换出页都 │
│ 加一 │
└──────────────────┬───────────────────┘
▼
┌──────────────────────────────────────┐
│ memcg 弃用后仍被钉住,等换入才释放 │
└──────────────────────────────────────┘
问题
- memcgid 的引用计数嵌在 css 里,随 ID 分发到各换出页。
- 每个换出页持有的 ID 引用都钉住 css,memcg 无法释放。
- 须等页换入并记回 online 父级,dying memcg 长期滞留。
方案
把 memcgid 的引用计数从 css 移入 objcg,让换出页随 objcg reparent 解析到祖先,不再钉住 dying memcg。
做法仿照 folio 对 objcg 记账:memcg 私有 ID 的引用计数移入 memcg 自己的一个 objcg,全局 memcgid xarray 改存 objcg 指针并持有 objcg 引用,不再持有 css。memcg offline 时 objcg 按既有机制 reparent 到父级,残留的 ID 引用随之解析到祖先 memcg。
解绑也有代价。ID 不再给 memcg 续命,以往靠 ID 引用保活 memcg 的 swap charge 路径改为在 RCU 读锁下操作。
swapin 因此自然落到 online 父级:按 ID 查 memcg 时从 xarray 取出 objcg,再取它当前的 memcg,正是 swapin charge 路径期望的 online 祖先。swap uncharge 用 objcg 一次完成放 ID 引用与取 online 祖先。charge 与 uncharge 未必落在同一个 memcg,计数是层级累计的所以不会泄漏,但「entry 是否携带计数」不能再问解析出的 memcg,非 root 的 ID 被 reparent 到 root 后引用仍在,其计数必须释放,于是改按 ID 本身的 root 状态判断。
例外是 list_lru、workingset 与 MGLRU 的 shadow 检测,它们需要 ID 当初指向的那个 memcg 而非祖先:按 ID 取 memcg 时检查它是否仍拥有该 ID,已 reparent 的返回 NULL;list_lru 与 workingset 检测因此跳过该条目,MGLRU 的 recency 检测则不加 NULL 检查,由 mem_cgroup_lruvec() 把 NULL 换成 root 的 lruvec。
引用计数移入 objcg,reparent 解开钉住
┌────────────────────────────────────────┐
│ ID 引用计数移入 memcg 的 objcg,xarray │
│ 改持 objcg 引用 │
└───────────────────┬────────────────────┘
▼
┌────────────────────────────────────────┐
│ memcg offline 时 objcg reparent 到父 │
│ 级,残留引用解析到祖先 │
└───────────────────┬────────────────────┘
▼
┌────────────────────────────────────────┐
│ 换出页不再钉住 dying memcg,swapin │
│ 自然落到 online 父级 │
└────────────────────────────────────────┘
收益
作者未提供性能数据,从代码逻辑推断的预期收益:
- 换出页不再钉住 dying memcg,其 css 与元数据可及时释放。
- swapin 时换出页自然记到 online 父级,无需额外迁移。
- shmem 换出负载上 dying memcg 不再堆积。