Re: [PATCH] mm/z3fold.c: Fix z3fold_destroy_pool() ordering

Henry Burns
2019-07-29T18:39:10Z

The constraint from the zpool use of z3fold_destroy_pool() is there
are no outstanding handles to memory (so no active allocations), but
it is possible for there to be outstanding work on either of the two
wqs in the pool.

If there is work queued on pool->compact_workqueue when it is called,
z3fold_destroy_pool() will do:

z3fold_destroy_pool()
destroy_workqueue(pool->release_wq)
destroy_workqueue(pool->compact_wq)
drain_workqueue(pool->compact_wq)
do_compact_page(zhdr)
kref_put(&zhdr->refcount)
__release_z3fold_page(zhdr, ...)
queue_work_on(pool->release_wq, &pool->work) *BOOM*

So compact_wq needs to be destroyed before release_wq.

Fixes: 5d03a6613957 ("mm/z3fold.c: use kref to prevent page free/compact race")

Signed-off-by: Henry Burns <henryburns@google.com>

> Reviewed-by: Shakeel Butt <shakeelb@google.com>
> Reviewed-by: Jonathan Adams <jwadams@google.com>
>
> > Cc: <stable@vger.kernel.org>
> > ---
> > mm/z3fold.c | 9 ++++++++-
> > 1 file changed, 8 insertions(+), 1 deletion(-)
> >
> > diff --git a/mm/z3fold.c b/mm/z3fold.c
> > index 1a029a7432ee..43de92f52961 100644
> > --- a/mm/z3fold.c
> > +++ b/mm/z3fold.c
> > @@ -818,8 +818,15 @@ static void z3fold_destroy_pool(struct z3fold_pool *pool)
> > {
> > kmem_cache_destroy(pool->c_handle);
> > z3fold_unregister_migration(pool);
> > - destroy_workqueue(pool->release_wq);
> > +
> > + /*
> > + * We need to destroy pool->compact_wq before pool->release_wq,
> > + * as any pending work on pool->compact_wq will call
> > + * queue_work(pool->release_wq, &pool->work).
> > + */
> > +
> > destroy_workqueue(pool->compact_wq);
> > + destroy_workqueue(pool->release_wq);
> > kfree(pool);
> > }
> >
> > --
> > 2.22.0.709.g102302147b-goog
> >