[RFC PATCH 0/7] mm: store zswap entries in swap table (Pointer entry)

From: Baoquan He

Date: Tue Jul 07 2026 - 03:33:45 EST


Currently zswap uses a per-swap-device xarray to map swap offsets to
zswap_entry pointers. This introduces an additional tree walk on every
zswap entry access.

This series replaces the xarray with a "Pointer" entry type stored
directly in the swap cluster table. The new swp_tb_t variant embeds
a zswap_entry pointer plus a entry count, eliminating the xarray and
its lookup overhead entirely.

Design overview
---------------
A swap table slot can now be one of five types:

NULL: |---------------- 0 ---------------|
Shadow: |SWAP_COUNT|Z|---- SHADOW_VAL ---|1|
PFN: |SWAP_COUNT|Z|------ PFN -------|10|
Pointer: |----------- Pointer ----------|100|
Bad: |------------- 1 -------------|1000|

When zswap compresses a page, it saves the original swap table entry
(PFN or Shadow) in zswap_entry->swp_tb_val, then writes the Pointer
entry into the swap table. The swap count and flags remain accessible
via helper functions that read them from swp_tb_val.

The zswap_entry is threaded through the swapin call chain via a new
void **zentry parameter, so zswap_load() receives it directly instead
of doing an xarray lookup.

Performance
-----------
[~]# free -h
total used free shared buff/cache available
Mem: 3.8Gi 155Mi 3.4Gi 656Ki 315Mi 3.7Gi
Swap: 4.0Gi 54Mi 3.9Gi

Scalability benchmark: 'usemem -O -y -x -n 1 5G', 10 runs under cgroup
MemoryMax=2G MemorySwapMax=3G. zswap enabled with zstd compressor.

Kernel A: cfb8731f5396 (xarray-based lookup, baseline)
Kernel B: (Pointer entry)

5G test â?? the gap widens significantly as swap pressure grows:

Metric xarray(A) Pointer(B)
--------------------------------------------------
Avg throughput 709,556 KB/s 792,681 KB/s (+11.7%)
Avg free time 167,994 µs 132,896 µs (-20.9%)

Free time trend across 10 rounds at 5G:

Pointer: 126â??124â??126â??126â??124â??129â??138â??145â??142â??148 (gradual, +17%)
xarray: 136â??139â??136â??160â??162â??161â??159â??230â??219â??178 (collapse, +31%)

xarray degrades sharply in the later rounds (last 3 avg free time
209 ms vs Pointer's 145 ms, 1.44Ã? worse) as the radix tree
insertion/deletion cost grows non-linearly under sustained swap
churn. Pointer entry avoids this entirely by looking up entries
directly in the swap cluster table, which is already hot in cache.

Baoquan He (7):
mm/swap_table: add Pointer type support for zswap entry storage
mm/zswap: add swp_tb_val to zswap_entry and Pointer accessor helpers
mm/swap_state: do zswap conversion in __swap_cache_do_del_folio
mm/zswap, mm/swap_state: handle Pointer entries in add_folio path
mm/swapfile: handle Pointer entries in count/dup/put paths
mm/zswap, mm/swap_state: migrate zswap to use swap_table for entry
indexing
mm/zswap, mm/swap_state: replace xarray indexing with swap table
lookups

include/linux/zswap.h | 4 +-
mm/page_io.c | 4 +-
mm/swap.h | 52 +++++-
mm/swap_state.c | 117 +++++++++++---
mm/swap_table.h | 35 ++++-
mm/swapfile.c | 107 ++++++++++---
mm/zswap.c | 358 +++++++++++++++++++++++++++++-------------
7 files changed, 508 insertions(+), 169 deletions(-)

--
2.54.0