[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