操作者写 node reclaim
nodeX/reclaim 接口
echo '64M swappiness=max'
本意:只回收匿名页
scan_control
proactive_swappiness
承载接口传进来的 swappiness
回收核心从这里读
sc_swappiness()
决定匿名页 / 文件页的回收比例
node reclaim 真正依赖的判定
它到底读哪个变量?
参数一路传到这里
CONFIG_MEMCG 版
proactive_swappiness 有值 → 用它
为空 → 回落 mem_cgroup_swappiness()
参数被看见
!CONFIG_MEMCG 版
从不检查 proactive_swappiness
直接 return 全局 vm_swappiness
参数被静默忽略
问题就出在这一版
同一个参数,两套定义 → 两种行为
分叉的代码路径,就是 bug 的温床
操作者本意
swappiness=max → 只回收匿名页
实际:回收边界失控
文件页拿走了绝大多数额度
pgsteal_proactive 实际回收
文件页
17676
匿名页
337
总计 18013 页 · 回收的是文件页,不是匿名页
匿名页只剩零头
sc_swappiness() 只保留一份实现
先看调用方传入的 proactive_swappiness,再回落 mem_cgroup_swappiness()
判定顺序
if (sc->proactive_swappiness)
→ 用设定值
else mem_cgroup_swappiness()
→ 回落 memcg / 全局
改动前:#ifdef CONFIG_MEMCG
两套实现,参数可能被吞
改动后:单一定义
两种 config 共用同一条判定
代码里不再有分叉的路径
include/linux/swap.h
public 头文件
mem_cgroup_swappiness()
vm_swappiness
mm/swap.h
mm/ 私有头文件
使用者都在 mm/ 内:
memcontrol.c · memcontrol-v1.c
vmscan.c
memcg->swappiness 是 v1 专属字段
struct mem_cgroup { ... }
swappiness;
用 CONFIG_MEMCG_V1 守护
v2 永远读全局 vm_swappiness
这个字段在 v2 上没有用
v2-only 内核直接省掉
落脚后的 mem_cgroup_swappiness() 对两种 config 各有一份正确处理
所以合并后的 sc_swappiness() 直接复用,不再需要额外适配
修复前
匿名页
337
文件页
17676
修复后
匿名页
16283
文件页
0
边界回到操作者手里
同一条指令,回收对象完全翻转
匿名页 337 → 16283;文件页 17676 → 0
① 参数生效
!CONFIG_MEMCG 内核
swappiness 不再被吞
回收边界与设定一致
② 一套定义
两套实现的分叉消除
只维护一条判定路径
行为不再随 config 漂移
③ 省掉字段
v2-only 内核
memcg->swappiness
未使用字段不再占位
作者未提供性能数据 · 以下为逻辑推断的预期收益
先查 proactive_swappiness,再回落 mem_cgroup_swappiness()
一个函数,两种 config,同一种行为
node reclaim 的 swappiness 参数不再被静默忽略
空格
播放/暂停
←
→
逐动作
R
重播
⚠
1x