diff --git a/module/zfs/zvol.c b/module/zfs/zvol.c index 50851e198826..2577dba4e5eb 100644 --- a/module/zfs/zvol.c +++ b/module/zfs/zvol.c @@ -87,6 +87,7 @@ typedef enum { ZVOL_ASYNC_CREATE_MINORS, ZVOL_ASYNC_REMOVE_MINORS, ZVOL_ASYNC_RENAME_MINORS, + ZVOL_ASYNC_SET_SNAPDEV, ZVOL_ASYNC_MAX } zvol_async_op_t; @@ -95,6 +96,8 @@ typedef struct { char pool[MAXNAMELEN]; char name1[MAXNAMELEN]; char name2[MAXNAMELEN]; + zprop_source_t source; + uint64_t snapdev; } zvol_task_t; #define ZVOL_RDONLY 0x1 @@ -1641,6 +1644,36 @@ zvol_remove_minors_impl(const char *name) mutex_exit(&zvol_state_lock); } +/* Remove minor for this specific snapshot only */ +static void +zvol_remove_minor_impl(const char *name) +{ + zvol_state_t *zv, *zv_next; + + if (zvol_inhibit_dev) + return; + + if (strchr(name, '@') == NULL) + return; + + mutex_enter(&zvol_state_lock); + + for (zv = list_head(&zvol_state_list); zv != NULL; zv = zv_next) { + zv_next = list_next(&zvol_state_list, zv); + + if (strcmp(zv->zv_name, name) == 0) { + /* If in use, leave alone */ + if (zv->zv_open_count > 0) + continue; + zvol_remove(zv); + zvol_free(zv); + break; + } + } + + mutex_exit(&zvol_state_lock); +} + /* * Rename minors for specified dataset including children and snapshots. */ @@ -1684,6 +1717,102 @@ zvol_rename_minors_impl(const char *oldname, const char *newname) kmem_free(name, MAXNAMELEN); } +typedef struct zvol_snapdev_cb_arg { + uint64_t snapdev; +} zvol_snapdev_cb_arg_t; + + +static int +zvol_set_snapdev_cb(const char *dsname, void *param) { + zvol_snapdev_cb_arg_t *arg = param; + + if (strchr(dsname, '@') == NULL) + return (0); + + switch (arg->snapdev) { + case ZFS_SNAPDEV_VISIBLE: + (void) zvol_create_minor_impl(dsname); + break; + case ZFS_SNAPDEV_HIDDEN: + (void) zvol_remove_minor_impl(dsname); + break; + } + + return (0); +} + +static void +zvol_set_snapdev_impl(char *name, uint64_t snapdev) +{ + zvol_snapdev_cb_arg_t arg = {snapdev}; + fstrans_cookie_t cookie = spl_fstrans_mark(); + /* + * The zvol_set_snapdev_sync() sets snapdev appropriately + * in the dataset hierarchy. Here, we only scan snapshots. + */ + dmu_objset_find(name, zvol_set_snapdev_cb, &arg, DS_FIND_SNAPSHOTS); + spl_fstrans_unmark(cookie); +} + +static zvol_task_t * +zvol_task_alloc(zvol_async_op_t op, const char *name1, const char *name2, + uint64_t snapdev) +{ + zvol_task_t *task; + char *delim; + + /* Never allow tasks on hidden names. */ + if (name1[0] == '$') + return (NULL); + + task = kmem_zalloc(sizeof (zvol_task_t), KM_SLEEP); + task->op = op; + task->snapdev = snapdev; + delim = strchr(name1, '/'); + strlcpy(task->pool, name1, delim ? (delim - name1 + 1) : MAXNAMELEN); + + strlcpy(task->name1, name1, MAXNAMELEN); + if (name2 != NULL) + strlcpy(task->name2, name2, MAXNAMELEN); + + return (task); +} + +static void +zvol_task_free(zvol_task_t *task) +{ + kmem_free(task, sizeof (zvol_task_t)); +} + +/* + * The worker thread function performed asynchronously. + */ +static void +zvol_task_cb(void *param) +{ + zvol_task_t *task = (zvol_task_t *)param; + + switch (task->op) { + case ZVOL_ASYNC_CREATE_MINORS: + (void) zvol_create_minors_impl(task->name1); + break; + case ZVOL_ASYNC_REMOVE_MINORS: + zvol_remove_minors_impl(task->name1); + break; + case ZVOL_ASYNC_RENAME_MINORS: + zvol_rename_minors_impl(task->name1, task->name2); + break; + case ZVOL_ASYNC_SET_SNAPDEV: + zvol_set_snapdev_impl(task->name1, task->snapdev); + break; + default: + VERIFY(0); + break; + } + + zvol_task_free(task); +} + typedef struct zvol_set_snapdev_arg { const char *zsda_name; uint64_t zsda_value; @@ -1717,24 +1846,20 @@ zvol_set_snapdev_sync_cb(dsl_pool_t *dp, dsl_dataset_t *ds, void *arg) { zvol_set_snapdev_arg_t *zsda = arg; char dsname[MAXNAMELEN]; + zvol_task_t *task; dsl_dataset_name(ds, dsname); dsl_prop_set_sync_impl(ds, zfs_prop_to_name(ZFS_PROP_SNAPDEV), zsda->zsda_source, sizeof (zsda->zsda_value), 1, &zsda->zsda_value, zsda->zsda_tx); - switch (zsda->zsda_value) { - case ZFS_SNAPDEV_VISIBLE: - zvol_create_minors(dp->dp_spa, dsname, B_TRUE); - break; - case ZFS_SNAPDEV_HIDDEN: - zvol_remove_minors(dp->dp_spa, dsname, B_TRUE); - zvol_create_minors(dp->dp_spa, dsname, B_TRUE); - break; - default: - break; - } + task = zvol_task_alloc(ZVOL_ASYNC_SET_SNAPDEV, dsname, + NULL, zsda->zsda_value); + if (task == NULL) + return (0); + (void) taskq_dispatch(dp->dp_spa->spa_zvol_taskq, zvol_task_cb, + task, TQ_SLEEP); return (0); } @@ -1770,67 +1895,13 @@ zvol_set_snapdev(const char *ddname, zprop_source_t source, uint64_t snapdev) zvol_set_snapdev_sync, &zsda, 0, ZFS_SPACE_CHECK_NONE)); } -static zvol_task_t * -zvol_task_alloc(zvol_async_op_t op, const char *name1, const char *name2) -{ - zvol_task_t *task; - char *delim; - - /* Never allow tasks on hidden names. */ - if (name1[0] == '$') - return (NULL); - - task = kmem_zalloc(sizeof (zvol_task_t), KM_SLEEP); - task->op = op; - delim = strchr(name1, '/'); - strlcpy(task->pool, name1, delim ? (delim - name1 + 1) : MAXNAMELEN); - - strlcpy(task->name1, name1, MAXNAMELEN); - if (name2 != NULL) - strlcpy(task->name2, name2, MAXNAMELEN); - - return (task); -} - -static void -zvol_task_free(zvol_task_t *task) -{ - kmem_free(task, sizeof (zvol_task_t)); -} - -/* - * The worker thread function performed asynchronously. - */ -static void -zvol_task_cb(void *param) -{ - zvol_task_t *task = (zvol_task_t *)param; - - switch (task->op) { - case ZVOL_ASYNC_CREATE_MINORS: - (void) zvol_create_minors_impl(task->name1); - break; - case ZVOL_ASYNC_REMOVE_MINORS: - zvol_remove_minors_impl(task->name1); - break; - case ZVOL_ASYNC_RENAME_MINORS: - zvol_rename_minors_impl(task->name1, task->name2); - break; - default: - VERIFY(0); - break; - } - - zvol_task_free(task); -} - void zvol_create_minors(spa_t *spa, const char *name, boolean_t async) { zvol_task_t *task; taskqid_t id; - task = zvol_task_alloc(ZVOL_ASYNC_CREATE_MINORS, name, NULL); + task = zvol_task_alloc(ZVOL_ASYNC_CREATE_MINORS, name, NULL, ~0ULL); if (task == NULL) return; @@ -1845,7 +1916,7 @@ zvol_remove_minors(spa_t *spa, const char *name, boolean_t async) zvol_task_t *task; taskqid_t id; - task = zvol_task_alloc(ZVOL_ASYNC_REMOVE_MINORS, name, NULL); + task = zvol_task_alloc(ZVOL_ASYNC_REMOVE_MINORS, name, NULL, ~0ULL); if (task == NULL) return; @@ -1861,7 +1932,7 @@ zvol_rename_minors(spa_t *spa, const char *name1, const char *name2, zvol_task_t *task; taskqid_t id; - task = zvol_task_alloc(ZVOL_ASYNC_RENAME_MINORS, name1, name2); + task = zvol_task_alloc(ZVOL_ASYNC_RENAME_MINORS, name1, name2, ~0ULL); if (task == NULL) return;