[PATCH RFC v3 1/3] mm/zswap: release retired pools via call_rcu() instead of synchronize_rcu()

Jianyue Wu <[email protected]>
Newsgroups org.kvack.linux-mm,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
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 <[email protected]>
Signed-off-by: Jianyue Wu <[email protected]>
---
 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
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.