Commit b997052b authored by Henry Burns's avatar Henry Burns Committed by Linus Torvalds

mm/z3fold.c: fix z3fold_destroy_pool() race condition

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.

Calling z3fold_deregister_migration() before the workqueues are drained
means that there can be allocated pages referencing a freed inode,
causing any thread in compaction to be able to trip over the bad pointer
in PageMovable().

Link: http://lkml.kernel.org/r/20190726224810.79660-2-henryburns@google.com
Fixes: 1f862989 ("mm/z3fold.c: support page migration")
Signed-off-by: default avatarHenry Burns <henryburns@google.com>
Reviewed-by: default avatarShakeel Butt <shakeelb@google.com>
Reviewed-by: default avatarJonathan Adams <jwadams@google.com>
Cc: Vitaly Vul <vitaly.vul@sony.com>
Cc: Vitaly Wool <vitalywool@gmail.com>
Cc: David Howells <dhowells@redhat.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Henry Burns <henrywolfeburns@gmail.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 6051d3bd
...@@ -817,16 +817,19 @@ static struct z3fold_pool *z3fold_create_pool(const char *name, gfp_t gfp, ...@@ -817,16 +817,19 @@ static struct z3fold_pool *z3fold_create_pool(const char *name, gfp_t gfp,
static void z3fold_destroy_pool(struct z3fold_pool *pool) static void z3fold_destroy_pool(struct z3fold_pool *pool)
{ {
kmem_cache_destroy(pool->c_handle); kmem_cache_destroy(pool->c_handle);
z3fold_unregister_migration(pool);
/* /*
* We need to destroy pool->compact_wq before pool->release_wq, * We need to destroy pool->compact_wq before pool->release_wq,
* as any pending work on pool->compact_wq will call * as any pending work on pool->compact_wq will call
* queue_work(pool->release_wq, &pool->work). * queue_work(pool->release_wq, &pool->work).
*
* There are still outstanding pages until both workqueues are drained,
* so we cannot unregister migration until then.
*/ */
destroy_workqueue(pool->compact_wq); destroy_workqueue(pool->compact_wq);
destroy_workqueue(pool->release_wq); destroy_workqueue(pool->release_wq);
z3fold_unregister_migration(pool);
kfree(pool); kfree(pool);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment