背景

zswap 把换出的页压缩后存放在内存池里,swap 槽位与 zswap entry 的映射记录在 xarray 中,每 64MB swap 区域一棵。swap 槽位被批量释放时(swap_range_free),每个槽位对应的 zswap entry 都要失效:从所属的 tree 中擦除并释放。zswap_store() 存储失败或 zswap 被禁用时也要清掉 folio 各页偏移上可能残留的旧 entry,否则后续 writeback 会用旧数据覆盖新写入的 swapfile 内容。

失效接口按单槽设计的浪费
┌──────────────────────────────────┐
│    swap 槽位按 64MB 区域分树     │
└────────────────┬─────────────────┘
                 ▼
┌──────────────────────────────────┐
│ 释放槽位要从对应 tree 擦除 entry │
└────────────────┬─────────────────┘
                 ▼
┌──────────────────────────────────┐
│ 旧接口一次一个槽,调用方逐槽循环 │
└──────────────────────────────────┘

旧的 zswap_invalidate() 一次只处理一个槽,接口按 swp_entry 设计:调用方把 type 与 offset 打包成 swp_entry,函数进门第一件事就是再解开,而两个调用方手里本来就有这两个值。swap_range_free 只能逐槽循环调用,zswap 从未启用的系统上,每次调用仍然要做一次 xarray 的空检查。zswap_store 的回退路径则把同样的逐页查找擦除逻辑又手写了一遍,对空 tree 也照样发起 erase 查找。

问题

  • swap_range_free 逐槽调用 invalidate,每次打包再解开 swp_entry
  • zswap 从未启用时,槽位释放仍逐槽触碰 xarray
  • zswap_store 回退路径重复实现同一套失效逻辑
  • store 回退路径对空 tree 也发起 erase 查找

方案

invalidate 接口 range 化

zswap_invalidate() 的签名从单个 swp_entry 改为直传 type、offset 与 nr_entries,打包再解开的往返消失。逐槽循环收进函数内部,swap_range_free 对整个 range 只调用一次。槽位所属的 xarray 按 64MB 区域划分,一个 range 可能跨多棵 tree,内部循环按槽重算所属 tree,空 tree 跳过,否则逐个 xa_erase 并释放 entry。

未启用时整体跳过

函数入口新增 zswap_never_enabled() 判断,这是一个 static branch,zswap 从未启用时整个 range 的失效工作坍缩成一次分支判断,不再逐槽触碰 xarray。不开 zswap 却使用 swap 的系统,槽位批量释放路径上的这部分纯开销被整体移除。

整个 range 一次调用
┌─────────────────────────────────────┐
│ 调用方直传 type 与 offset,不再打包 │
└──────────────────┬──────────────────┘
                   ▼
┌─────────────────────────────────────┐
│   未启用时 static branch 直接返回   │
└──────────────────┬──────────────────┘
                   ▼
┌─────────────────────────────────────┐
│      内部逐槽重算 tree 并擦除       │
└─────────────────────────────────────┘

store 回退路径复用同一实现

zswap_store 的 check_old 路径直接调用 zswap_invalidate(),替换掉手写的逐页 tree 查找加 erase 循环。重复实现删除的同时,回退路径继承上 never-enabled 与 xa_empty 两道守卫:zswap 从未启用时直接返回,空 tree 不再发起 erase 查找。

store 回退路径复用 invalidate
┌──────────────────────────────────┐
│ 存储失败须清掉偏移上的残留 entry │
└────────────────┬─────────────────┘
                 ▼
┌──────────────────────────────────┐
│  复用 invalidate,删掉手写循环   │
└──────────────────────────────────┘

收益

作者未提供性能数据,从代码逻辑推断的预期收益:

  • zswap 未启用系统的 swap 槽位批量释放,从逐槽 xarray 访问降为一次 static branch 判断
  • range 一次调用,消除逐槽的 swp_entry 打包解包与函数调用开销
  • store 回退路径消除重复的失效实现,空 tree 不再做 erase 查找