swapoff(sys_swapoff)
sys_swapoff
把 swap 全部换回内存
try_to_unuse
反复找下一个待换回 slot
find_next_to_unuse
从上次位置往后找
核心循环
找到 slot
一个接一个
换回内存
try_to_unuse
继续往后找
直到设备扫完
每次只推进一个 slot,进度靠 find_next_to_unuse 定位
目标
扫完整个 swap 设备
但只处理仍在用的 slot
空区域不该有成本
它们不产生换回工作
swap 设备按 cluster 组织
每个 cluster 含 SWAPFILE_CLUSTER(默认 256)个 slot
cluster 0
cluster 1
cluster 2
cluster 3
cluster_info 里有什么
swap table
每个 slot 的状态
count 计数
簇内仍在用的 slot 数
count 为 0 就说明整簇没有待换回的 slot
旧法:一次推进一个 offset
offset i
一个 slot
swap_table_get
读 swap table 表项
在用?
否则 i++ 继续
每个 offset 都要读一次表项
空 cluster 的代价
簇内 256 个 slot 全空
仍要逐个读 256 次表项
读完才知道整簇是空的
纯属无谓开销
swap 设备上常存在大量空 cluster
真正的工作量在哪
非空 cluster
才有 slot 需要换回
空 cluster
不该逐个表项确认
旧实现把两者按同样方式扫
按 cluster 推进
进入 cluster
先读一次 count
count == 0 ?
整簇跳过,i = end
否则
count > 0
簇内逐 offset 找
整簇跳过的判定
if (!READ_ONCE(ci->count)) {
i = end;
cond_resched();
continue;
无锁判定
不持 ci->lock
count 用 READ_ONCE() 读
非空簇才逐 offset
簇内读 swap table
找到下一个在用 slot
找到就返回
交给 try_to_unuse 换回
空簇的判定从 256 次表项读压成 1 次 count 读
为什么 count 为 0 就能跳
wait_for_allocation()
已经停住新分配
count 只减不增
没有新增在用 slot
判定期间不会有新 slot 冒出来
什么时候减 count
folio 离开 swap cache
之后才减这个 slot 的计数
count == 0
没有 slot 还需要 try_to_unuse
所以逐表项读取本来就是空跑
一次跳跃省掉什么
SWAPFILE_CLUSTER 次表项读
默认 256 次
1 次 count 读
整簇直接跳过
空簇越多,压缩比越可观
尾簇不会越界
min_t() 钳在 si->max
不完整的尾簇停在 si->max
每轮至多 SWAPFILE_CLUSTER 次
返回或 reschedule
与 swap_reclaim_full_clusters() 的扫描粒度一致
LATENCY_LIMIT 也随之删除
旧:每 256 个 offset
强行 cond_resched() 一次
新:按 cluster 推进
粒度天然一致
resched 不再靠计数器兜底
1 TiB swap 设备 · 先填充再 swapoff
剩余在用 slot
集中在已填充区域顶部
其下方
全是可整簇跳过的空 cluster
32/128 GiB 各 11 组中位数,256/512 GiB 各 3 组中位数
swapoff 耗时
Before
After
改善
填充 32 GiB
92.4
→
66.3
−28.2%
填充 128 GiB
157.6
→
94.4
−40.1%
填充 256 GiB
209.7
→
63.8
−69.6%
填充 512 GiB
391.8
→
73.4
−81.3%
单位:ms;填充量越大,旧实现越慢,新实现基本持平
收益来自跳过的空簇,而不是换回更快
常规场景:8 GiB 设备填 4 GiB
扫描只占 try_to_unuse 的 1.4%
时间花在把数据读回来
swap 里是真实数据
没有大片空 cluster
常规 swapoff 新旧没有差异
空格
播放/暂停
←
→
逐动作
R
重播
⚠
1x