Re: [PATCH RFC v3 1/3] mm/zswap: release retired pools via call_rcu() instead of synchronize_rcu()
From: Jianyue Wu
Date: Wed Aug 26 2026 - 11:25:26 EST
On Wed, Aug 26, 2026 at 12:55 AM Yosry Ahmed <yosry@xxxxxxxxxx> wrote:
>
> On Fri, Aug 14, 2026 at 11:54 PM Jianyue Wu <wujianyue000@xxxxxxxxx> wrote:
> >
> > 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.
> > + */
>
> Oh I didn't realize that call_rcu() cannot be used with blocking
> callbacks, sorry about that. What about queue_rcu_work()? Seems like
> if we use that we can drop the intermediary callback?
Thanks, that's a good idea. queue_rcu_work() is a better fit for this
pattern and avoids the extra callback, so I will switch to it in the next
version.
Best regards,
Jianyue