[PATCH RFC v3 1/3] mm/zswap: release retired pools via call_rcu() instead of synchronize_rcu()
From: Jianyue Wu
Date: Sat Aug 15 2026 - 02:55:39 EST
When a pool's last reference is dropped, __zswap_pool_empty() removes it
from the pool list and schedules __zswap_pool_release(), which calls
synchronize_rcu() to wait for readers before tearing the pool down.
synchronize_rcu() is a synchronous, potentially long wait. Replace it
with an asynchronous call_rcu(): __zswap_pool_empty() now hands the pool
to call_rcu(), and the RCU callback defers the sleepable teardown to the
release worker (the callback itself runs in softirq context and must not
block). The grace-period guarantee is unchanged; the retirement path
just no longer blocks on it.
Suggested-by: Yosry Ahmed <yosry@xxxxxxxxxx>
Signed-off-by: Jianyue Wu <wujianyue000@xxxxxxxxx>
---
mm/zswap.c | 20 ++++++++++++++++----
1 file changed, 16 insertions(+), 4 deletions(-)
diff --git a/mm/zswap.c b/mm/zswap.c
index 37f34e406c8e..cc4243356e21 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -155,6 +155,7 @@ struct zswap_pool {
struct crypto_acomp_ctx __percpu *acomp_ctx;
struct percpu_ref ref;
struct list_head list;
+ struct rcu_head rcu_head;
struct work_struct release_work;
struct hlist_node node;
char tfm_name[CRYPTO_MAX_ALG_NAME];
@@ -382,8 +383,6 @@ static void __zswap_pool_release(struct work_struct *work)
struct zswap_pool *pool = container_of(work, typeof(*pool),
release_work);
- synchronize_rcu();
-
/* nobody should have been able to get a ref... */
WARN_ON(!percpu_ref_is_zero(&pool->ref));
percpu_ref_exit(&pool->ref);
@@ -392,6 +391,20 @@ static void __zswap_pool_release(struct work_struct *work)
zswap_pool_destroy(pool);
}
+static void __zswap_pool_release_rcu(struct rcu_head *head)
+{
+ struct zswap_pool *pool = container_of(head, typeof(*pool), rcu_head);
+
+ /*
+ * The grace period has elapsed, so no RCU reader can still observe the
+ * pool through the list it was removed from in __zswap_pool_empty().
+ * Hand off to a worker for the sleepable teardown, since this callback
+ * runs in softirq context.
+ */
+ INIT_WORK(&pool->release_work, __zswap_pool_release);
+ schedule_work(&pool->release_work);
+}
+
static struct zswap_pool *zswap_pool_current(void);
static void __zswap_pool_empty(struct percpu_ref *ref)
@@ -406,8 +419,7 @@ static void __zswap_pool_empty(struct percpu_ref *ref)
list_del_rcu(&pool->list);
- INIT_WORK(&pool->release_work, __zswap_pool_release);
- schedule_work(&pool->release_work);
+ call_rcu(&pool->rcu_head, __zswap_pool_release_rcu);
spin_unlock_bh(&zswap_pools_lock);
}
--
2.43.0