背景

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 字段