写 memory.high、memory.max
或 memory.reclaim
write 返回前,回收必须跑完
reclaim 循环
循环到用量降到目标以下
memory.reclaim 则收满请求量
swap I/O
被设备写带宽卡住,一趟可以很久
thrashing
回收几页,又被 workload 立即 fault 回来
大 cgroup 上这一趟可能很久
thrashing 时几乎无界:一直在 progress、永不收敛
cgroup_file_write()
不持 cgroup_mutex
也不 pin css
kernfs active reference
操作期间挂上,操作结束才放下
reclaim 跑多久
引用就握多久
引用不是 reclaim 自己拿的
是 kernfs 在文件操作期间替它挂上的 —— 回收不返回,引用就不放
并发 rmdir 同一个 cgroup
cgroup_rmdir() 先拿 cgroup_mutex
再进 kernfs_drain() 等引用
kernfs_drain()
阻塞等 active reference 排干
等多久,锁就持多久
cgroup_mutex 一路被持
其它任务
等 cgroup_mutex
其它任务
等 cgroup_mutex
读 /proc/<pid>/cgroup
无关任务也被 hung task 报出来
remover 不放锁,所有要用它的任务都堆在后面
实测整台机器停摆
reclaim 收几页
一次只收一点点
workload fault 回来
页立刻又变热
每轮都成功一点点,进度不为零
MAX_RECLAIM_RETRIES
只在零进度时触发
进度不为零 → 不触发
stall 无界
reclaim 一直在 progress
循环没有退出点
慢 swap I/O 与 thrashing 都不会收敛
于是 stall 没有上界
kill_css_sync()
置 CSS_DYING
标志在这里写下
css_clear_dir()
触发目录清理
随后才进入 drain
kernfs_drain()
等 active reference 排干
等的是已经跑起来的回收
dying 一定早于 drain
所以 in-flight 的回收保证在下一轮迭代前看到它
remover 开始等之前,标志已经写好
reclaim 循环
每轮回收几页
memcg_is_dying()
每轮问一次
yes
bail out
命中即退出回收循环
writer 立刻放下
active reference
no:继续回收
remover 得以前进
cgroup_mutex 解锁
memory.reclaim 的请求量没满足,write 返回 -EAGAIN
与 O_NONBLOCK 正交:那是调用方事先避免同步 reclaim;这里处理已经在跑、cgroup 开始被删
生产 hung task 日志 · 6.6.102 kernel · 作者未标具体 CPU/物理机/VM
场景
修复前 stall
修复后
cgdelete(kernfs_drain 等 active ref)
159 s
即时释放
不再 stall
systemd-journal 读 /proc/pid/cgroup(等 cgroup_mutex)
182 s
即时
cgroup_mutex 不再被持
修复前,系统要等那趟 reclaim 跑完才恢复
而 dying 的 cgroup 上的回收纯属浪费:页本来就要 reparent 给父
回收循环一看到 dying 就退出:active ref 即时释放,锁不再被长持
慢 swap I/O 与 thrashing 本来永远不会收敛 —— dying 检查正好覆盖
空格
播放/暂停
←
→
逐动作
R
重播
⚠
1x