From: Henry Burns henryburns@google.com
commit b997052bc3ac444a0bceab1093aff7ae71ed419e upstream.
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: 1f862989b04a ("mm/z3fold.c: support page migration") Signed-off-by: Henry Burns henryburns@google.com Reviewed-by: Shakeel Butt shakeelb@google.com Reviewed-by: Jonathan 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: Andrew Morton akpm@linux-foundation.org Signed-off-by: Linus Torvalds torvalds@linux-foundation.org Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org --- mm/z3fold.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/mm/z3fold.c b/mm/z3fold.c index d06d7f9560028..c4debbe683eba 100644 --- a/mm/z3fold.c +++ b/mm/z3fold.c @@ -819,16 +819,19 @@ static struct z3fold_pool *z3fold_create_pool(const char *name, gfp_t gfp, static void z3fold_destroy_pool(struct z3fold_pool *pool) { kmem_cache_destroy(pool->c_handle); - z3fold_unregister_migration(pool);
/* * 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). + * + * 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->release_wq); + z3fold_unregister_migration(pool); kfree(pool); }