背景

swap 子系统为每个 swap slot 维护一份 swap count(有多少处引用了这个 swap entry),用来判断何时能真正释放。旧实现用一张静态的 swap_map(per-slot 数组)存计数,引用计数过高(超 SWAP_CONT_MAX)时还要靠 COUNT_CONTINUED 页接管。

┌──────────────────────────────┐
│  交换映射表是逐槽的静态数组  │
└──────────────┬───────────────┘
               ▼
┌──────────────────────────────┐
│ 交换表也得维护一份,双重更新 │  高引用计数还需连续页机制接管
└──────────────┬───────────────┘
               ▼
┌──────────────────────────────┐
│ 静态元数据随设备大小线性增长 │  1TB 交换设备占约 256MB
└──────────────────────────────┘

swap table phase I 引入了 per-cluster 的 swap table 后,slot 的状态已在表里维护,但 swap count 仍由 swap_map 单独记账。于是 swap table 和 swap_map 做着重复的 per-slot 更新,而 swap_map 这张静态数组随设备大小线性增长,挂一个 1TB 的 swap 设备就要占约 256MB。

问题

  • swap count 由静态 swap_map 逐槽记账
  • swap table 也维护状态,两套机制双重更新
  • 高引用计数还需连续页机制(COUNT_CONTINUED)
  • 静态元数据随设备大小线性增长(1TB 占 256MB)

方案

核心是让 swap table 直接承担 swap count 的记账,把 swap_map 整张表连同 SWP_CONTINUED/COUNT_CONTINUED 机制一并移除。

旧:双重计数,静态数组
┌──────────────────────────────┐
│      交换映射表逐槽计数      │
└──────────────┬───────────────┘
               ▼
┌──────────────────────────────┐
│ 交换表也得维护一份,双重更新 │
└──────────────────────────────┘

新:交换表直接跟踪计数
┌──────────────────────────────────────┐
│ 计数存进交换表高位,超限由扩展表接管 │
└──────────────────┬───────────────────┘
                   ▼
┌──────────────────────────────────────┐
│      移除静态映射表与连续页机制      │  基于簇,稀疏性好;双重更新消除
└──────────────────────────────────────┘

swap table 的每条目高位能存到 SWP_TB_COUNT_MAX-1 个计数,足以覆盖绝大多数场景。当某 slot 的计数真的超出这个上限,就给该 cluster 分配一个 extend_table(cluster 大小的 unsigned int 数组),把计数卸载过去,直到计数降回再收回。swap table 与 extend_table 都基于 cluster,稀疏性与性能都不错。计数搬进 swap table,它就成了唯一的 per-slot 账本,原本散落各处、各自为 swap_map 编织的逻辑随之收敛:条目要腾出高位存计数,workingset shadow 编码便预留出这些位;坏槽位不再单独记账,直接在表中标记;swapon 与设备启停等路径里的锁和参数,也因无须双重更新而精简。计数切换落定后,扫描边界与影子清理等既有逻辑顺势简化。

收益

作者在多组场景测试(封面信数据)。

内存(挂载 1TB brd swap 设备,free -m):

指标 Before After 变化
used 1,051 MB 795 MB 省 256 MB(约 30%)

内核构建系统时间(全局压力,32 次均值):

配置 Before After 变化
ZSWAP+NVME,x86_64 VM 5G,-j48,defconfig 1038.97s 1013.75s -2.4%
ZRAM,ARM64 VM 1.5G,-j12,tinyconfig 67.75s 66.65s -1.6%

Redis/Valkey(ZRAM,ARM64 VM 1.5G,全局压力,64 次均值):

场景 Before (RPS) After (RPS) 变化
无持久化 472,705.71 481,197.93 +1.8%
BGSAVE 369,451.68 374,922.32 +1.5%

封面信结论:性能全面更好("performance is better in all cases, and memory usage is much lower")。内存省约 30%(1TB 省 256MB);内核构建系统时间降 1.6%~2.4%;Redis/Valkey RPS 提升 1.5%~1.8%。后续阶段还会把 swap cgroup 数组合进 swap table,省另外约 60% 静态元数据。