背景
per-node proactive reclaim 接口(/sys/devices/system/node/nodeX/reclaim)允许操作者写入「64M swappiness=max」这样的指令主动回收内存,其中 swappiness 参数决定本次回收偏向匿名页还是文件页。参数经 scan_control 的 proactive_swappiness 字段传进回收核心,由 sc_swappiness() 读取。
sc_swappiness() 却有两套定义:CONFIG_MEMCG 版本先查 proactive_swappiness 再查 memcg 自己的 swappiness;!CONFIG_MEMCG 版本直接返回全局 vm_swappiness,proactive_swappiness 一眼都不看。
swappiness 参数在 !CONFIG_MEMCG 下被吞
┌────────────────────────────────────────────────────┐
│ 写 node reclaim 接口并指定 swappiness │
└─────────────────────────┬──────────────────────────┘
▼
┌────────────────────────────────────────────────────┐
│ 参数进 scan_control 的 proactive_swappiness │
└─────────────────────────┬──────────────────────────┘
▼
┌────────────────────────────────────────────────────┐
│ !MEMCG 版 sc_swappiness() 直接返回 vm_swappiness, │
│ 参数被吞 │
└────────────────────────────────────────────────────┘
问题
- !CONFIG_MEMCG 版 sc_swappiness() 从不检查 proactive_swappiness
- node reclaim 的 swappiness 参数在 !CONFIG_MEMCG 内核上被静默忽略
- 主动回收的匿名与文件页比例偏离操作者设定,回收边界失控
复现很明显:!CONFIG_MEMCG 内核上写「64M swappiness=max」,本意只回收匿名页,实际 pgsteal_proactive 18013 里文件页占 17676,匿名页只有 337。修复后同样的指令回收到匿名页 16283、文件页 0。
方案
把 sc_swappiness() 合并成单一定义:先查 scan_control 里的 proactive_swappiness,没有设定再回落 mem_cgroup_swappiness()。
统一后的 swappiness 判定
┌──────────────────────────────────────────┐
│ sc_swappiness() 合并成单一定义 │
└────────────────────┬─────────────────────┘
▼
┌──────────────────────────────────────────┐
│ 先查 proactive_swappiness,有则用之 │
└────────────────────┬─────────────────────┘
▼
┌──────────────────────────────────────────┐
│ 否则回落 mem_cgroup_swappiness(),config │
│ 无关 │
└──────────────────────────────────────────┘
合并有个前置障碍:mem_cgroup_swappiness() 与 vm_swappiness 原本声明在公共头文件 include/linux/swap.h,而使用者都在 mm/ 内部(memcontrol.c、memcontrol-v1.c、vmscan.c)。先把它们挪进 mm 私有的 mm/swap.h,顺带把 memcg->swappiness 字段用 CONFIG_MEMCG_V1 守护起来:这个字段是 memcg v1 专属,v2 永远读全局 vm_swappiness,v2-only 内核因此省掉这个未使用的字段。落脚后的 mem_cgroup_swappiness() 本身就对两种 config 各有一份正确处理,sc_swappiness() 合并后直接复用。
收益
作者未提供性能数据,从代码逻辑推断的预期收益:
- node reclaim 的 swappiness 参数在 !CONFIG_MEMCG 内核上生效,回收边界与操作者设定一致
- sc_swappiness() 合并成单一定义,两套实现分叉的隐患消除
- v2-only 内核省掉未使用的 memcg->swappiness 字段