deferred split 队列
未充分使用的 THP
先挂到 deferred split 队列
由 THP shrinker 择机拆分
THP shrinker
扫描 deferred split 队列
拆掉未映射的部分
把页面归还伙伴分配器
回收时按需进行
为什么要延迟分裂
不是立刻拆
等内存压力再拆
避免无谓开销
队列就是回收入口
不在队列,shrinker 看不到
NUMA 迁移会经过这条路径
目的页能否归队,是关键
迁移路径
__folio_migrate_mapping()
迁移把数据搬到目的 folio
映射切换时也要处理队列
源页先出队
folio_unqueue_deferred_split()
源 folio 从队列移除
这是迁移的既有动作
目的页还没有接手
状态没有跟过去
目的页不在队列
源页的队列状态丢失
迁移数据成功,状态没成功
问题在“归队”这一步
旧路径的结果
源页出队,目的页不重新入队
数据迁过去了,回收入口却断了
目的页静默脱队
源页的队列位置没了
目的页没有补上
shrinker 扫描不到它
回收链断了
未映射部分不能及时拆
THP 继续占着内存
用户看到的空闲内存变少
不是数据坏了,是回收漏了
为什么 shrinker 关键
deferred split 是入口
脱队就少了回收入口
NUMA 迁移会触发这条路径
underutilized THP 受影响
回归动机
THP 从 deferred_list 移除后
用户会显示比以前更少的空闲内存
迁移前先记状态
src_deferred_split
src_partially_mapped
在 move_to_new_folio() 之前记录
记录的条件
folio_order(src) > 1
_deferred_list 非空
源页确实在队列上
否则不记录
然后才迁移
move_to_new_folio()
迁移成功才回放状态
失败就不归队
目的页没有成功接手
只记两个 bool
不改变迁移数据路径
只把队列状态补回来
成功后按状态归队
if (src_deferred_split)
deferred_split_folio(dst, src_partially_mapped)
目的页回到延迟分裂队列
partially_mapped 参数
true:有 pin 的部分映射
false:完全映射但可能未被充分利用
状态一并传给目的页
不是只看“在不在队列”
shrinker 又能看到
队列恢复完整
后续 reclaim 可拆分
未映射部分归还
回收路径重新接上
不是所有迁移都归队
只有源页在队列上才 requeue
普通迁移不多做一次入队
迁移后还剩哪些
无 pin 的 partial folio
已被 migrate_pages_batch 提前拆掉
剩下两类才要处理
情况一:有 pin
partially mapped
拆分失败,仍在队列
用 true 归队
保留部分映射状态
情况二:完全映射
fully mapped
可能未被充分利用
false 归队(开关开启时)
让 shrinker 后续判断
partially_mapped 状态不能一概而论
有 pin 与完全映射要分别传
源页状态决定目的页怎么归队
改动范围
mm/migrate.c
migrate_folio_move()
只补队列状态,不改迁移数据
判断条件
folio_order(src) > 1
_deferred_list 非空
deferred_split_folio() 也忽略
order <= 1 的 folio
完全映射这一路的开关
split_underused_thp
sysfs:shrink_underused
默认开启,但可关闭
关闭时完全映射页不入队
没有量化 benchmark
作者未提供延迟/空闲内存数字
以上是机制与回归动机
预期收益
NUMA 迁移后不再静默脱队
shrinker 能重新拆分
未映射部分可归还
为什么重要
underutilized THP
NUMA 迁移是典型场景
脱队后回收不及时
用户看到更少空闲内存
边界
预期,不是实测
无 benchmark 数据
不写回收比例
不写空闲内存增量
一句话
迁移不只搬数据,也要搬队列状态
数据到了,回收入口也要到
空格
播放/暂停
←
→
逐动作
R
重播
⚠
1x