[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]>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.