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

Jianyue Wu posted 3 patches 1 month, 2 weeks ago
There is a newer version of this series
mm/zswap.c | 187 ++++++++++++++++++++++++++++++++++++++++++++++++++-----------
1 file changed, 153 insertions(+), 34 deletions(-)
[PATCH RFC v3 0/3] mm/zswap: shrink zswap_entry via a fixed pool index
Posted by Jianyue Wu 1 month, 2 weeks ago
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 <hannes@cmpxchg.org>
To: Yosry Ahmed <yosry@kernel.org>
To: Nhat Pham <nphamcs@gmail.com>
To: Chengming Zhou <chengming.zhou@linux.dev>
To: Andrew Morton <akpm@linux-foundation.org>
Cc: Chris Li <chrisl@kernel.org>
Cc: linux-mm@kvack.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Jianyue Wu <wujianyue000@gmail.com>

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/20260731-shrink_zswap_entry_v2-0-0-v2-0-e72083aa8734@gmail.com/
Link: https://lore.kernel.org/all/20260726-shrink_zswap_entry_v1-0-0-v1-1-30957e4d0cb6@gmail.com/
---
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 <wujianyue000@gmail.com>