vmpressure:回收路径上的压力上报点
每次回收按调用者传入的 tree 标志分流
tree 为假
上报内核态 socket 压力
tree 为真
上报 v1 用户态 eventfd 通知
tree 为假:供 TCP/SCTP 收紧发送窗口
只在 cgroup v2 上有意义
v1 的 socket 内存另有 tcpmem,读的是另一处压力值
tree 为真:经 memory.pressure_level 送 eventfd
cgroup v2 没有对应物,用户态直接读 PSI
v2 的 memcg 根本注册不上事件
cgroup 层级
tree 为假
tree 为真
cgroup v1
✗ 结果无人读
✓ 用户态在监听
cgroup v2
✓ 供网络栈使用
✗ 事件链表恒为空
原有早退:只挡住 v1 + tree 为假
对称的 v2 + tree 为真,照常走完整流程
cgroup v2 + tree 为真,仍走完整流程
加锁
抢全局锁
累加计数
扫描与回收
延迟上报
调度 work item
遍历父级
逐层 cgroup
可它最终要遍历的事件链表,在 v2 上永远是空的
事件只能经 v1 的 memory.pressure_level 注册
事件链表:空
回收线程挤在同一把全局锁上串行排队
全局锁
线程
线程
线程
一次只进一个
实测平台:176 核 · cgroup v2 · 285 个 memcg · 持续回收
约 16200 次/分
无人消费的调用
4 种组合,只有 2 种真有人读结果
没人读的 2 种,函数应当直接返回
原来只早退
cgroup v1 + tree 为假
另一格照常走完整流程
现在也早退
cgroup v2 + tree 为真
入口判明无人消费就返回
入口即返,跳过加锁、累加、延迟上报、父级遍历
在只跑 cgroup v2 的机器上,那条串行路径消失
补上的正是对称的那一格
把 v1 用户态通知代码隔离出去
搬进 v1 内存控制器专用源文件
事件注册与注销
事件链表及其互斥锁
延迟上报的工作项与处理函数
沿父级逐层上报
按回收优先级上报的入口
只在启用 v1 时编译
头文件给不启用 v1 的内核
留下空操作桩
调用处无须加条件编译
vmpressure 主文件代码量减半
只剩共享部分与 tree 为假的 socket 压力路径
源码里剩下的条件编译
只有 vmpressure 结构体内部那几个 v1 专属字段
socket 压力路径今天还不能删
PSI 并非 vmpressure 的等价替代
把 socket 回撤切到 PSI 的风险
依赖 vmpressure 滞回特性的负载
可能被拖慢网络、加重内存压力
中期路线
先引入基于 PSI 的
socket 压力路径
cgroup v2 以可配置
选项逐步退出
最后砍掉 tree 为假
留下纯 v1 子系统
运行时:tree 为真的调用
176 核生产机 · cgroup v2 · 285 个 memcg · 持续回收
约 16200
次/分(Before)
→
0
次/分(After,入口早退跳过)
体积(未启用 v1)
struct vmpressure
112 B
→
24 B
struct mem_cgroup
1664 B
→
1536 B
vmpressure 主文件代码量
减半
没人读的路径,入口就该返回
不是删功能,是把代价移走;socket 与 PSI 的取舍留到中期
空格
播放/暂停
←
→
逐动作
R
重播
⚠
1x