每个存入页的元数据
匿名页
换出前先压缩
struct zswap_entry
swpentry · 压缩长度 · pool
每个存入页都背一个 entry
x86_64 上的 entry
56 字节
pool 指针 8B
其余字段:swpentry、压缩长度等
8 字节是指向 zswap_pool 的指针
同时存活的 pool 仅个位数
current pool
zswap_pool A
一种压缩算法 + zsmalloc 池
zswap_pool B
切换算法后保留复用
zswap_pool C
身份长期稳定
只有设置或切换压缩算法才新建
旧实现:pool 串在 RCU 链表上,表头兼任 current pool
个位数的 pool,指针和链表都过度
56 字节的 per-page 开销
zswap_entry
56
字节
单 slab 容纳
73
个 entry
每 1GiB 元数据
14
MiB
entry 大小是纯 per-page 开销
开销随页数线性放大
pool 表换成按需分配 id 的 xarray
zswap_pool xarray
XA_FLAGS_ALLOC1 · XA_FLAGS_LOCK_BH
0
1
2
3
255 上限
0 号保留,永不分配
id 用 u8 存
current pool
单独 RCU 指针跟踪
id 从 1 开始分配;255 个占满则新建失败
entry 里的 pool 指针换成 u8 id
改动前
pool 指针 8B
56 字节
改动后
u8 id 1B
48 字节
u8 落进 bool referenced 之后的填充,不占新空间
pool_idx 的写入顺序
压缩成功
entry 已就绪
写 pool_idx
在发布之前写
xa_store() 发布
读者看到完整 entry
发布后再写,读者会短暂看到 slab 复用残留的旧 pool_idx
取 pool 时按 id 解析
zswap_entry_pool()
entry 里只存 id
xa_load(id)
自带 RCU 保护
entry 持有 percpu_ref
pool 不释放,id 不复用
xa_load() 自带 RCU 保护,调用者无需外加 rcu_read_lock()
创建 pool 的最后一步
创建 pool
id 从 1 起分配
分配 id 并发布
发布进 xarray 是最后一步
读者只见就绪 pool
失败则彻底回滚
运行时参数更新由模块参数锁串行化
pool 腾空时交还 id
pool 腾空
percpu_ref 释放回调
xa_erase_bh 擦除
交还 id,可被复用
queue_rcu_work
宽限期后拆除
腾空路径不再同步阻塞在宽限期上
x86_64 · compressor=lzo
Before
After
变化
zswap_entry 大小
56
→
48
-8 字节
单 slab 容纳 entry
73
→
85
+12
每 1GiB 元数据
14MiB
→
12MiB
省约 2MiB
store+load 中位延迟
基线
→
无可测回归
store / free / decompress
多一次 xa_load
存活 pool 少,xarray 层浅
开销淹没在压缩与解压成本里
空格
播放/暂停
←
→
逐动作
R
重播
⚠
1x