[RFC PATCH v4 0/3] mm/zswap: shrink zswap_entry via a fixed pool index

From: Jianyue Wu

Date: Sun Aug 30 2026 - 07:47:50 EST


Every stored page has a struct zswap_entry, so its size is pure per-page
overhead. On x86_64 it is currently 56 bytes, of which 8 bytes are a
pointer to the owning zswap_pool.

Only a handful of pools are ever live: a new pool is created only when
the compressor is (re)set, and pools are reused across compressor
switches. That makes a per-entry pool pointer more expensive than it
needs to be, and the RCU list that currently tracks pools is more
machinery than this needs once each pool already has a stable slot.

This series:

1. Releases retired pools with queue_rcu_work() instead of
synchronize_rcu() so the last put no longer blocks on an RCU grace
period.
2. Replaces the zswap_pools list with a fixed ZSWAP_MAX_POOLS (16)
array and a separate RCU-protected current-pool pointer, giving
each pool a stable slot index. Slot 0 is left unused, and pool
creation publishes the fully constructed pool directly into a free
slot.
3. Stores that u8 slot index in each zswap_entry instead of the pool
pointer. The u8 fits in padding after the bool referenced field,
so the entry shrinks from 56 to 48 bytes on x86_64 (~2MiB of
metadata saved per 1GiB of data held in zswap).

Runtime compressor switching is preserved, but the fixed array now
bounds the number of simultaneously live distinct compressor pools to 15
(slot 0 is reserved). If all usable slots are full, creating a pool for
another compressor fails and the compressor switch is rejected.

Benchmark (x86_64, compressor=lzo, MADV_PAGEOUT store + fault-in load):

- zswap_entry object_size: 56 -> 48 bytes
- e2e store+load median latency: no measurable regression vs baseline
at matched stored_delta

The extra cost per store/free/decompress is one array-index load
instead of a pointer dereference. With a single (or few) live pool(s)
that does not show up against (de)compression.

Testing
=======

- sizeof_check: 56 -> 48 bytes on x86_64
- Boot with DEBUG_ATOMIC_SLEEP + lockdep/PROVE_RCU + KASAN:
zswap store/load and compressor switch (retire + reuse) pass

This series is based on akpm/mm-unstable as of 2026-08-30
(42d64d4fef83).

Signed-off-by: Jianyue Wu <wujianyue000@xxxxxxxxx>

Changes since RFC v3:
- Retire pools with queue_rcu_work() instead of call_rcu().
- Queue the deferred RCU release work on system_percpu_wq.
- Use rcu_dereference_check() with entry->pool_idx != 0 in
zswap_entry_pool() instead of rcu_dereference_protected().
- Simplify pool-slot publishing after confirming compressor parameter
updates are serialized by the module parameter lock.

Link: https://lore.kernel.org/all/20260815-shrink_zswap_entry_0815_v2-v3-3-0171bd86a667@xxxxxxxxx/
Link: https://lore.kernel.org/all/20260731-shrink_zswap_entry_v2-0-0-v2-0-e72083aa8734@xxxxxxxxx/
Link: https://lore.kernel.org/all/20260726-shrink_zswap_entry_v1-0-0-v1-1-30957e4d0cb6@xxxxxxxxx/

Jianyue Wu (3):
mm/zswap: release retired pools via queue_rcu_work() instead of
synchronize_rcu()
mm/zswap: replace the zswap_pools list with a fixed pools array
mm/zswap: reference the pool by index to shrink struct zswap_entry

mm/zswap.c | 140 ++++++++++++++++++++++++++++++++++++++---------------
1 file changed, 101 insertions(+), 39 deletions(-)


base-commit: 42d64d4fef83a241c919c8693fdf0a21b2cb6061
--
2.43.0