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

From: Jianyue Wu

Date: Thu Jul 30 2026 - 20:35:25 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 replaces the list with a fixed array of ZSWAP_MAX_POOLS (16)
slots, tracks the current pool with a separate RCU-protected pointer,
then stores a u8 slot index in each 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;
creating a 17th pool fails and warns.

Patch 1 is the list -> array conversion. Patch 2 is the per-entry
shrink. Looking for feedback on whether this is the right shape before
sending a non-RFC version.

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.

Changes from RFC v1:
- Drop the allocating xarray; use a fixed array of 16 pools indexed by
a u8, as suggested by Nhat and Yosry.
- Drop the zswap_pools list and the per-pool list_head; the array
carries stable slot numbers for entries, and a separate RCU pointer
tracks the current pool.
- Document the new pool-count cap; fail/warn when the array is full.
- Clear the array slot in __zswap_pool_empty() before scheduling the
release work (equivalent to the old list_del_rcu()), then
synchronize_rcu() before free.
- Use rcu_assign_pointer() / rcu_dereference*() for slot and current-
pool publish/load. Entry lookup uses rcu_dereference_protected()
because a live entry already pins its pool.
- Add NULL checks on the resolved pool in free/decompress.

Link: https://lore.kernel.org/all/20260726-shrink_zswap_entry_v1-0-0-v1-1-30957e4d0cb6@xxxxxxxxx/

Signed-off-by: Jianyue Wu <wujianyue000@xxxxxxxxx>
---
Jianyue Wu (2):
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 | 119 ++++++++++++++++++++++++++++++++++++++++++++++---------------
1 file changed, 91 insertions(+), 28 deletions(-)
---
base-commit: 2ed26e8a624f41887d64e2e37a2f6ab36a118d1f
change-id: 20260731-shrink_zswap_entry_v2-0-0-76ac3af12ec4

Best regards,
--
Jianyue Wu <wujianyue000@xxxxxxxxx>