diff --git a/cmd/zpool/zpool_main.c b/cmd/zpool/zpool_main.c index bfef6fc43285..b40af9629ac7 100644 --- a/cmd/zpool/zpool_main.c +++ b/cmd/zpool/zpool_main.c @@ -397,7 +397,7 @@ get_usage(zpool_help_t idx) case HELP_REOPEN: return (gettext("\treopen [-n] \n")); case HELP_INITIALIZE: - return (gettext("\tinitialize [-c | -s] [-w] " + return (gettext("\tinitialize [-c | -s | -u] [-w] " "[ ...]\n")); case HELP_SCRUB: return (gettext("\tscrub [-s | -p] [-w] ...\n")); @@ -552,12 +552,13 @@ usage(boolean_t requested) } /* - * zpool initialize [-c | -s] [-w] [ ...] + * zpool initialize [-c | -s | -u] [-w] [ ...] * Initialize all unused blocks in the specified vdevs, or all vdevs in the pool * if none specified. * * -c Cancel. Ends active initializing. * -s Suspend. Initializing can then be restarted with no flags. + * -u Uninitialize. Clears initialization state. * -w Wait. Blocks until initializing has completed. */ int @@ -573,12 +574,14 @@ zpool_do_initialize(int argc, char **argv) struct option long_options[] = { {"cancel", no_argument, NULL, 'c'}, {"suspend", no_argument, NULL, 's'}, + {"uninit", no_argument, NULL, 'u'}, {"wait", no_argument, NULL, 'w'}, {0, 0, 0, 0} }; pool_initialize_func_t cmd_type = POOL_INITIALIZE_START; - while ((c = getopt_long(argc, argv, "csw", long_options, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "csuw", long_options, + NULL)) != -1) { switch (c) { case 'c': if (cmd_type != POOL_INITIALIZE_START && @@ -598,6 +601,15 @@ zpool_do_initialize(int argc, char **argv) } cmd_type = POOL_INITIALIZE_SUSPEND; break; + case 'u': + if (cmd_type != POOL_INITIALIZE_START && + cmd_type != POOL_INITIALIZE_UNINIT) { + (void) fprintf(stderr, gettext("-u cannot be " + "combined with other options\n")); + usage(B_FALSE); + } + cmd_type = POOL_INITIALIZE_UNINIT; + break; case 'w': wait = B_TRUE; break; @@ -624,8 +636,8 @@ zpool_do_initialize(int argc, char **argv) } if (wait && (cmd_type != POOL_INITIALIZE_START)) { - (void) fprintf(stderr, gettext("-w cannot be used with -c or " - "-s\n")); + (void) fprintf(stderr, gettext("-w cannot be used with -c, -s" + "or -u\n")); usage(B_FALSE); } diff --git a/include/sys/fs/zfs.h b/include/sys/fs/zfs.h index 2af11fc7196d..f69c3c231da7 100644 --- a/include/sys/fs/zfs.h +++ b/include/sys/fs/zfs.h @@ -1176,6 +1176,7 @@ typedef enum pool_initialize_func { POOL_INITIALIZE_START, POOL_INITIALIZE_CANCEL, POOL_INITIALIZE_SUSPEND, + POOL_INITIALIZE_UNINIT, POOL_INITIALIZE_FUNCS } pool_initialize_func_t; @@ -1237,7 +1238,8 @@ typedef enum { VDEV_INITIALIZE_ACTIVE, VDEV_INITIALIZE_CANCELED, VDEV_INITIALIZE_SUSPENDED, - VDEV_INITIALIZE_COMPLETE + VDEV_INITIALIZE_COMPLETE, + VDEV_INITIALIZE_UNINIT } vdev_initializing_state_t; typedef enum { diff --git a/include/sys/vdev_initialize.h b/include/sys/vdev_initialize.h index 81d39ebebcb2..942fc71c52ae 100644 --- a/include/sys/vdev_initialize.h +++ b/include/sys/vdev_initialize.h @@ -33,6 +33,7 @@ extern "C" { #endif extern void vdev_initialize(vdev_t *vd); +extern void vdev_uninitialize(vdev_t *vd); extern void vdev_initialize_stop(vdev_t *vd, vdev_initializing_state_t tgt_state, list_t *vd_list); extern void vdev_initialize_stop_all(vdev_t *vd, diff --git a/module/zfs/spa.c b/module/zfs/spa.c index a02fd198bed0..2c52021da3f1 100644 --- a/module/zfs/spa.c +++ b/module/zfs/spa.c @@ -7255,6 +7255,10 @@ spa_vdev_initialize_impl(spa_t *spa, uint64_t guid, uint64_t cmd_type, vd->vdev_initialize_state != VDEV_INITIALIZE_ACTIVE) { mutex_exit(&vd->vdev_initialize_lock); return (SET_ERROR(ESRCH)); + } else if (cmd_type == POOL_INITIALIZE_UNINIT && + vd->vdev_initialize_thread != NULL) { + mutex_exit(&vd->vdev_initialize_lock); + return (SET_ERROR(EBUSY)); } switch (cmd_type) { @@ -7267,6 +7271,9 @@ spa_vdev_initialize_impl(spa_t *spa, uint64_t guid, uint64_t cmd_type, case POOL_INITIALIZE_SUSPEND: vdev_initialize_stop(vd, VDEV_INITIALIZE_SUSPENDED, vd_list); break; + case POOL_INITIALIZE_UNINIT: + vdev_uninitialize(vd); + break; default: panic("invalid cmd_type %llu", (unsigned long long)cmd_type); } diff --git a/module/zfs/vdev_initialize.c b/module/zfs/vdev_initialize.c index e9156c32f384..8bc20e1dacd9 100644 --- a/module/zfs/vdev_initialize.c +++ b/module/zfs/vdev_initialize.c @@ -81,7 +81,19 @@ vdev_initialize_zap_update_sync(void *arg, dmu_tx_t *tx) objset_t *mos = vd->vdev_spa->spa_meta_objset; - if (last_offset > 0) { + uint64_t initialize_state = vd->vdev_initialize_state; + + /* + * XXX: need to double-check that there's really no way around this + * + * It turns out we call this with last_offset = 0 and + * VDEV_INITIALIZE_NONE when resuming, so we can't just lob + * VDEV_INITIALIZE_NONE out if we see it or we'll clobber that. + */ + if (last_offset > 0 || initialize_state == VDEV_INITIALIZE_UNINIT) { + if (initialize_state == VDEV_INITIALIZE_UNINIT) { + vd->vdev_initialize_state = VDEV_INITIALIZE_NONE; + } vd->vdev_initialize_last_offset = last_offset; VERIFY0(zap_update(mos, vd->vdev_leaf_zap, VDEV_LEAF_ZAP_INITIALIZE_LAST_OFFSET, @@ -94,7 +106,7 @@ vdev_initialize_zap_update_sync(void *arg, dmu_tx_t *tx) 1, &val, tx)); } - uint64_t initialize_state = vd->vdev_initialize_state; + initialize_state = vd->vdev_initialize_state; VERIFY0(zap_update(mos, vd->vdev_leaf_zap, VDEV_LEAF_ZAP_INITIALIZE_STATE, sizeof (initialize_state), 1, &initialize_state, tx)); @@ -149,6 +161,10 @@ vdev_initialize_change_state(vdev_t *vd, vdev_initializing_state_t new_state) spa_history_log_internal(spa, "initialize", tx, "vdev=%s complete", vd->vdev_path); break; + case VDEV_INITIALIZE_UNINIT: + spa_history_log_internal(spa, "uninitialize", tx, + "vdev=%s", vd->vdev_path); + break; default: panic("invalid state %llu", (unsigned long long)new_state); } @@ -603,6 +619,24 @@ vdev_initialize(vdev_t *vd) vdev_initialize_thread, vd, 0, &p0, TS_RUN, maxclsyspri); } +/* + * Uninitializes a device. Caller must hold vdev_initialize_lock. + * Device must be a leaf and not already be initializing. + */ +void +vdev_uninitialize(vdev_t *vd) +{ + ASSERT(MUTEX_HELD(&vd->vdev_initialize_lock)); + ASSERT(vd->vdev_ops->vdev_op_leaf); + ASSERT(vdev_is_concrete(vd)); + ASSERT3P(vd->vdev_initialize_thread, ==, NULL); + ASSERT(!vd->vdev_detached); + ASSERT(!vd->vdev_initialize_exit_wanted); + ASSERT(!vd->vdev_top->vdev_removing); + + vdev_initialize_change_state(vd, VDEV_INITIALIZE_UNINIT); +} + /* * Wait for the initialize thread to be terminated (cancelled or stopped). */ @@ -758,6 +792,7 @@ vdev_initialize_restart(vdev_t *vd) } EXPORT_SYMBOL(vdev_initialize); +EXPORT_SYMBOL(vdev_uninitialize); EXPORT_SYMBOL(vdev_initialize_stop); EXPORT_SYMBOL(vdev_initialize_stop_all); EXPORT_SYMBOL(vdev_initialize_stop_wait); diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c index 96a021acbc95..ec8637cd9a18 100644 --- a/module/zfs/zfs_ioctl.c +++ b/module/zfs/zfs_ioctl.c @@ -3981,7 +3981,8 @@ zfs_ioc_pool_initialize(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) if (!(cmd_type == POOL_INITIALIZE_CANCEL || cmd_type == POOL_INITIALIZE_START || - cmd_type == POOL_INITIALIZE_SUSPEND)) { + cmd_type == POOL_INITIALIZE_SUSPEND || + cmd_type == POOL_INITIALIZE_UNINIT)) { return (SET_ERROR(EINVAL)); }