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 <david.chen@osnexus.com>
Requires-spl: refs/pull/588/head
This commit is contained in:
Chunwei Chen 2016-11-30 13:56:50 -08:00
parent 7ac557cef1
commit 899662e344

View File

@ -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 * 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() * 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. * is not called on this zv because of the positive zv_open_count.
*/ */
@ -1316,12 +1316,13 @@ out_kmem:
} }
/* /*
* 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 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); ASSERT(zv->zv_open_count == 0);
zfs_rlock_destroy(&zv->zv_range_lock); zfs_rlock_destroy(&zv->zv_range_lock);
@ -1336,6 +1337,16 @@ zvol_free(zvol_state_t *zv)
kmem_free(zv, sizeof (zvol_state_t)); 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 * Create a block device minor node and setup the linkage between it
* and the specified volume. Once this function returns the block * 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; zvol_state_t *zv, *zv_next;
int namelen = ((name) ? strlen(name) : 0); int namelen = ((name) ? strlen(name) : 0);
taskqid_t t, tid = TASKQID_INVALID;
if (zvol_inhibit_dev) if (zvol_inhibit_dev)
return; return;
@ -1710,11 +1722,22 @@ zvol_remove_minors_impl(const char *name)
continue; continue;
zvol_remove(zv); 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); mutex_exit(&zvol_state_lock);
if (tid != TASKQID_INVALID)
taskq_wait_outstanding(system_taskq, tid);
} }
/* Remove minor for this specific snapshot only */ /* Remove minor for this specific snapshot only */