解除映射
匿名页改装
migration entry
冻结引用计数
refcount 原子清零
拿不到新引用
逐阶拆开
复合页结构
变成低阶 folio
解冻子 folio
挂回 LRU
改缓存条目
匿名页
改装 migration entry,拆完再恢复
file page
直接拆掉映射,之后按需缺页恢复
冻结引用计数
refcount 原子地清零
此后 folio_try_get 一律失败
拆分期间互斥
没人能在途中拿到新引用
__folio_freeze_and_split_unmapped
冻结与解冻的公共骨架
匿名
anon_vma · migration entry
page cache
xarray 更新 · EOF 裁剪
swap cache
cluster 加锁 · 条目替换
swap cache 拆分的现状
只允许统一拆到 order-0
不收无 mapping 的 swap cache folio
两条限制都难放开
动一侧要在交叉分支里反复确认不伤另一侧
带 mapping 的 swap cache folio
冻结引用、扣 THP 计数后才被拒 → 卡死、计数失真
未映射的匿名大页
拿不到 anon_vma → 一律报 -EBUSY 拒拆
拆后释放 swap cache
先解锁再 trylock,抢不到就残留 swap entry
__folio_split
前置检查 · 按类型分发 · 拆后统一解锁
只保留调度职责
不再持有任何一类 folio 的善后
__folio_freeze_split_anon
匿名 folio,含匿名 swap cache
__folio_freeze_split_file
page cache 与 shmem
不变量:file 助手永远见不到 swap cache
folio_check_splittable 本就拒绝无 mapping 的非匿名 folio
再加上拒绝带 mapping 的非匿名 swap cache folio,即 swap IO 中的 shmem
匿名助手
local_irq_disable
罩住 swap cluster 锁与 LRU 锁等多把普通自旋锁
文件助手
xas_lock_irq
一体完成关中断与 i_pages 的 xarray 加锁
收窄之后
__folio_split 不再管关中断区间
mm 里最后一把裸 xas_lock 消失
锁跟着 folio 类型各归各
已映射
取 anon_vma 写锁
解除映射
冻结引用
锁住 swap cache
cluster
拆开复合页
替换 swap 条目
解冻 · 挂回 LRU
已映射
恢复映射
释放 anon_vma
整体换出的 THP
还活在 swap cache 里
swap PTE 不计入映射计数
folio_mapped 为假
旧代码取 anon_vma 失败 → 报 -EBUSY
没东西要锁,却被锁挡住
新代码跳过 anon_vma
直接冻结引用计数完成拆分
安全性由两处兜底
folio 锁全程持有,并发拆分被排除
冻结 refcount 拒绝新引用 / mapping / GUP pin
仍映射的 folio:anon_vma 写锁窗口收窄,不再罩着拆后子 folio 的解锁循环
检查最小阶
mapping 允许的阶数
切到 folio 的 memcg
节点计入正确账单
预分配节点
xarray 拆分所需
读 i_mmap 锁
算 EOF 边界
解除映射
EOF 边界算好之后才动手
拆后回写 page cache
裁掉 EOF 之外,shmem 超额计费归还
旧:先解锁再抢锁
free_folio_and_swap_cache trylock 抢回
抢不到就把 swap entry 留下
新:仍持锁时就释放
is_swapcache 且未映射 → 锁下释放
映射着的子 folio 跳过,免得白做功
两处错误路径一并修掉
带 mapping 的 swap cache folio
在 folio_check_splittable 就被拒,任何状态都还没动
swap cache 自检循环
off-by-one:首条目被跳过、范围外多读一条,已对齐替换循环
测量方法
同机交替重启,交错测量
ftrace function_graph 过滤 __folio_split
每轮 135 次拆分调用,共 600 轮
单次调用耗时
68.52µs
→
67.38µs
−1.66%
耗时标准差
1.58µs
→
1.33µs
−15.8%
分家之后,swap cache 的限制可以单独放开
这次只做搬迁与修错,耗时改善有限
空格
播放/暂停
←
→
逐动作
R
重播
⚠
1x