[PATCH RFC v3 0/3] mm/zswap: shrink zswap_entry via a fixed pool index
Jianyue Wu <[email protected]>
| Newsgroups | org.kvack.linux-mm,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
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 call_rcu() 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; create
first stores a RESERVED placeholder and only publishes the real
pointer when the pool becomes current.
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. Creating a pool when all
usable slots are full fails with pr_err (15 live pools at once, since
slot 0 is reserved).
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.
This series is based on akpm/mm-unstable as of 2026-08-15
(e737cebb8de0).
To: Johannes Weiner <[email protected]>
To: Yosry Ahmed <[email protected]>
To: Nhat Pham <[email protected]>
To: Chengming Zhou <[email protected]>
To: Andrew Morton <[email protected]>
Cc: Chris Li <[email protected]>
Cc: [email protected]
Cc: [email protected]
Signed-off-by: Jianyue Wu <[email protected]>
Changes since RFC v2:
- Add a patch to retire pools via call_rcu() instead of
synchronize_rcu().
- Reserve array slots with a ZSWAP_SLOT_RESERVED placeholder and only
publish the real pool pointer when making it current, so find_get /
total_pages never see a not yet published pool.
- Leave slot 0 unused; start allocation at ZSWAP_FIRST_POOL_SLOT.
- Use guard(spinlock_bh) in zswap_pool_reserve_slot().
- Replace BUILD_BUG_ON in zswap_setup() with static_assert() next to
the pools array.
- Use entry->pool_idx != 0 as the rcu_dereference_protected()
condition in zswap_entry_pool() instead of true.
Link: https://lore.kernel.org/all/[email protected]/
Link: https://lore.kernel.org/all/[email protected]/
---
Jianyue Wu (3):
mm/zswap: release retired pools via call_rcu() 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 | 187 ++++++++++++++++++++++++++++++++++++++++++++++++++-----------
1 file changed, 153 insertions(+), 34 deletions(-)
---
base-commit: e737cebb8de0d38e8f64584a8bbfbcf9176c7537
change-id: 20260815-shrink_zswap_entry_0815_v2-dd3411824163
Best regards,
--
Jianyue Wu <[email protected]>