From 899662e344b150037180a6f61acd186fec773c5f Mon Sep 17 00:00:00 2001 From: Chunwei Chen Date: Wed, 30 Nov 2016 13:56:50 -0800 Subject: [PATCH] zvol_remove_minors do parallel zvol_free On some kernel version, blk_cleanup_queue and put_disk will wait for more then 10ms. So a pool with a lot of zvols will easily wait for more then 1 min if we do zvol_free sequentially. Signed-off-by: Chunwei Chen Requires-spl: refs/pull/588/head --- module/zfs/zvol.c | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/module/zfs/zvol.c b/module/zfs/zvol.c index 8bc0a608ced5..61d0538a34a4 100644 --- a/module/zfs/zvol.c +++ b/module/zfs/zvol.c @@ -1036,7 +1036,7 @@ zvol_open(struct block_device *bdev, fmode_t flag) /* * Obtain a copy of private_data under the lock to make sure - * that either the result of zvol_freeg() setting + * that either the result of zvol_free() setting * bdev->bd_disk->private_data to NULL is observed, or zvol_free() * is not called on this zv because of the positive zv_open_count. */ @@ -1316,12 +1316,13 @@ zvol_alloc(dev_t dev, const char *name) } /* - * Cleanup then free a zvol_state_t which was created by zvol_alloc(). + * Used for taskq, if used out side zvol_state_lock, you need to clear + * zv_disk->private_data inside lock first. */ static void -zvol_free(zvol_state_t *zv) +zvol_free_impl(void *arg) { - ASSERT(MUTEX_HELD(&zvol_state_lock)); + zvol_state_t *zv = arg; ASSERT(zv->zv_open_count == 0); zfs_rlock_destroy(&zv->zv_range_lock); @@ -1336,6 +1337,16 @@ zvol_free(zvol_state_t *zv) kmem_free(zv, sizeof (zvol_state_t)); } +/* + * Cleanup then free a zvol_state_t which was created by zvol_alloc(). + */ +static void +zvol_free(zvol_state_t *zv) +{ + ASSERT(MUTEX_HELD(&zvol_state_lock)); + zvol_free_impl(zv); +} + /* * Create a block device minor node and setup the linkage between it * and the specified volume. Once this function returns the block @@ -1691,6 +1702,7 @@ zvol_remove_minors_impl(const char *name) { zvol_state_t *zv, *zv_next; int namelen = ((name) ? strlen(name) : 0); + taskqid_t t, tid = TASKQID_INVALID; if (zvol_inhibit_dev) return; @@ -1710,11 +1722,22 @@ zvol_remove_minors_impl(const char *name) continue; zvol_remove(zv); - zvol_free(zv); + + /* clear this so zvol_open won't open it */ + zv->zv_disk->private_data = NULL; + + /* try parallel zv_free, if failed do it in place */ + t = taskq_dispatch(system_taskq, zvol_free_impl, zv, + TQ_SLEEP); + if (t == TASKQID_INVALID) + zvol_free(zv); + else + tid = t; } } - mutex_exit(&zvol_state_lock); + if (tid != TASKQID_INVALID) + taskq_wait_outstanding(system_taskq, tid); } /* Remove minor for this specific snapshot only */