]> git.proxmox.com Git - mirror_zfs.git/commitdiff
Add the ability to uninitialize
authorBrian Behlendorf <behlendorf1@llnl.gov>
Thu, 18 May 2023 17:02:20 +0000 (10:02 -0700)
committerGitHub <noreply@github.com>
Thu, 18 May 2023 17:02:20 +0000 (10:02 -0700)
zpool initialize functions well for touching every free byte...once.
But if we want to do it again, we're currently out of luck.

So let's add zpool initialize -u to clear it.

Co-authored-by: Rich Ercolani <rincebrain@gmail.com>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Rich Ercolani <rincebrain@gmail.com>
Closes #12451
Closes #14873

13 files changed:
cmd/zpool/zpool_main.c
include/sys/fs/zfs.h
include/sys/vdev_initialize.h
lib/libzfs/libzfs.abi
lib/libzfs/libzfs_pool.c
lib/libzfs_core/libzfs_core.abi
man/man8/zpool-initialize.8
module/zfs/spa.c
module/zfs/vdev_initialize.c
module/zfs/zfs_ioctl.c
tests/runfiles/common.run
tests/zfs-tests/tests/Makefile.am
tests/zfs-tests/tests/functional/cli_root/zpool_initialize/zpool_initialize_uninit.ksh [new file with mode: 0755]

index 301c5f4bfc6fe4ab9bb8ee4c127e3260611a6d2f..3e08e031414d49b777bb918dc677de3204c043df 100644 (file)
@@ -398,7 +398,7 @@ get_usage(zpool_help_t idx)
        case HELP_REOPEN:
                return (gettext("\treopen [-n] <pool>\n"));
        case HELP_INITIALIZE:
-               return (gettext("\tinitialize [-c | -s] [-w] <pool> "
+               return (gettext("\tinitialize [-c | -s | -u] [-w] <pool> "
                    "[<device> ...]\n"));
        case HELP_SCRUB:
                return (gettext("\tscrub [-s | -p] [-w] <pool> ...\n"));
@@ -585,12 +585,13 @@ usage(boolean_t requested)
 }
 
 /*
- * zpool initialize [-c | -s] [-w] <pool> [<vdev> ...]
+ * zpool initialize [-c | -s | -u] [-w] <pool> [<vdev> ...]
  * 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
@@ -606,12 +607,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 &&
@@ -631,6 +634,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;
@@ -657,8 +669,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);
        }
 
index 0734ff12280e0cd49cc9fab32973959ecc1e4bf6..4c2097fb830e3dedb624ce9e6893f967a9e4e104 100644 (file)
@@ -1265,6 +1265,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;
 
index 4e63f063cb66ec37c6722e783443568f64f7a31f..78702b7325a008282292771627abac89c60b1461 100644 (file)
@@ -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,
index 732863dcffc73e987b0dea759130bfd313e9c6c3..57b096ca6e965c5f297ac8e482f5e0cb0ff6cca0 100644 (file)
       <enumerator name='POOL_INITIALIZE_START' value='0'/>
       <enumerator name='POOL_INITIALIZE_CANCEL' value='1'/>
       <enumerator name='POOL_INITIALIZE_SUSPEND' value='2'/>
-      <enumerator name='POOL_INITIALIZE_FUNCS' value='3'/>
+      <enumerator name='POOL_INITIALIZE_UNINIT' value='3'/>
+      <enumerator name='POOL_INITIALIZE_FUNCS' value='4'/>
     </enum-decl>
     <typedef-decl name='pool_initialize_func_t' type-id='5c246ad4' id='7063e1ab'/>
     <enum-decl name='pool_trim_func' id='54ed608a'>
index 4fb71b4e0dc8595dcaa9896aabe6cd3cf7c668ff..a71cb24736a9499b1ff4e5a9c016f42e805e946c 100644 (file)
@@ -2387,8 +2387,8 @@ xlate_init_err(int err)
 }
 
 /*
- * Begin, suspend, or cancel the initialization (initializing of all free
- * blocks) for the given vdevs in the given pool.
+ * Begin, suspend, cancel, or uninit (clear) the initialization (initializing
+ * of all free blocks) for the given vdevs in the given pool.
  */
 static int
 zpool_initialize_impl(zpool_handle_t *zhp, pool_initialize_func_t cmd_type,
@@ -2414,11 +2414,16 @@ zpool_initialize_impl(zpool_handle_t *zhp, pool_initialize_func_t cmd_type,
            vdev_guids, &errlist);
 
        if (err != 0) {
-               if (errlist != NULL) {
-                       vd_errlist = fnvlist_lookup_nvlist(errlist,
-                           ZPOOL_INITIALIZE_VDEVS);
+               if (errlist != NULL && nvlist_lookup_nvlist(errlist,
+                   ZPOOL_INITIALIZE_VDEVS, &vd_errlist) == 0) {
                        goto list_errors;
                }
+
+               if (err == EINVAL && cmd_type == POOL_INITIALIZE_UNINIT) {
+                       zfs_error_aux(zhp->zpool_hdl, dgettext(TEXT_DOMAIN,
+                           "uninitialize is not supported by kernel"));
+               }
+
                (void) zpool_standard_error(zhp->zpool_hdl, err,
                    dgettext(TEXT_DOMAIN, "operation failed"));
                goto out;
index ec94a46505532997f2cb1ecaceb0d4c3197bc352..33d794e3f8096d16f976800bd7a76b7b1b4d385a 100644 (file)
       <enumerator name='POOL_INITIALIZE_START' value='0'/>
       <enumerator name='POOL_INITIALIZE_CANCEL' value='1'/>
       <enumerator name='POOL_INITIALIZE_SUSPEND' value='2'/>
-      <enumerator name='POOL_INITIALIZE_FUNCS' value='3'/>
+      <enumerator name='POOL_INITIALIZE_UNINIT' value='3'/>
+      <enumerator name='POOL_INITIALIZE_FUNCS' value='4'/>
     </enum-decl>
     <typedef-decl name='pool_initialize_func_t' type-id='5c246ad4' id='7063e1ab'/>
     <enum-decl name='pool_trim_func' id='54ed608a'>
index eae711bff4298ff6b52b785d0bf1c0aaa1e7c8c9..a9c8fd35aec9da97d2e8764000a5f1a4d781ced2 100644 (file)
@@ -36,7 +36,7 @@
 .Sh SYNOPSIS
 .Nm zpool
 .Cm initialize
-.Op Fl c Ns | Ns Fl s
+.Op Fl c Ns | Ns Fl s | Ns Fl u
 .Op Fl w
 .Ar pool
 .Oo Ar device Oc Ns …
@@ -60,6 +60,14 @@ initialized, the command will fail and no suspension will occur on any device.
 Initializing can then be resumed by running
 .Nm zpool Cm initialize
 with no flags on the relevant target devices.
+.It Fl u , -uninit
+Clears the initialization state on the specified devices, or all eligible
+devices if none are specified.
+If the devices are being actively initialized the command will fail.
+After being cleared
+.Nm zpool Cm initialize
+with no flags can be used to re-initialize all unallocoated regions on
+the relevant target devices.
 .It Fl w , -wait
 Wait until the devices have finished initializing before returning.
 .El
index 1ca114783ce4af4e49eee910f9740960467d36e5..51d6de9105fb7120fd155bc3e836e6f433c7aefb 100644 (file)
@@ -7421,6 +7421,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) {
@@ -7433,6 +7437,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);
        }
index 75beb0cc3d12455e42320b9bc531b01b6641b5b1..ffdcef1972c3b2f12d5cb3237defee7885600e68 100644 (file)
@@ -96,6 +96,39 @@ vdev_initialize_zap_update_sync(void *arg, dmu_tx_t *tx)
            &initialize_state, tx));
 }
 
+static void
+vdev_initialize_zap_remove_sync(void *arg, dmu_tx_t *tx)
+{
+       uint64_t guid = *(uint64_t *)arg;
+
+       kmem_free(arg, sizeof (uint64_t));
+
+       vdev_t *vd = spa_lookup_by_guid(tx->tx_pool->dp_spa, guid, B_FALSE);
+       if (vd == NULL || vd->vdev_top->vdev_removing || !vdev_is_concrete(vd))
+               return;
+
+       ASSERT3S(vd->vdev_initialize_state, ==, VDEV_INITIALIZE_NONE);
+       ASSERT3U(vd->vdev_leaf_zap, !=, 0);
+
+       vd->vdev_initialize_last_offset = 0;
+       vd->vdev_initialize_action_time = 0;
+
+       objset_t *mos = vd->vdev_spa->spa_meta_objset;
+       int error;
+
+       error = zap_remove(mos, vd->vdev_leaf_zap,
+           VDEV_LEAF_ZAP_INITIALIZE_LAST_OFFSET, tx);
+       VERIFY(error == 0 || error == ENOENT);
+
+       error = zap_remove(mos, vd->vdev_leaf_zap,
+           VDEV_LEAF_ZAP_INITIALIZE_STATE, tx);
+       VERIFY(error == 0 || error == ENOENT);
+
+       error = zap_remove(mos, vd->vdev_leaf_zap,
+           VDEV_LEAF_ZAP_INITIALIZE_ACTION_TIME, tx);
+       VERIFY(error == 0 || error == ENOENT);
+}
+
 static void
 vdev_initialize_change_state(vdev_t *vd, vdev_initializing_state_t new_state)
 {
@@ -123,8 +156,14 @@ vdev_initialize_change_state(vdev_t *vd, vdev_initializing_state_t new_state)
 
        dmu_tx_t *tx = dmu_tx_create_dd(spa_get_dsl(spa)->dp_mos_dir);
        VERIFY0(dmu_tx_assign(tx, TXG_WAIT));
-       dsl_sync_task_nowait(spa_get_dsl(spa), vdev_initialize_zap_update_sync,
-           guid, tx);
+
+       if (new_state != VDEV_INITIALIZE_NONE) {
+               dsl_sync_task_nowait(spa_get_dsl(spa),
+                   vdev_initialize_zap_update_sync, guid, tx);
+       } else {
+               dsl_sync_task_nowait(spa_get_dsl(spa),
+                   vdev_initialize_zap_remove_sync, guid, tx);
+       }
 
        switch (new_state) {
        case VDEV_INITIALIZE_ACTIVE:
@@ -145,6 +184,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_NONE:
+               spa_history_log_internal(spa, "uninitialize", tx,
+                   "vdev=%s", vd->vdev_path);
+               break;
        default:
                panic("invalid state %llu", (unsigned long long)new_state);
        }
@@ -594,6 +637,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_NONE);
+}
+
 /*
  * Wait for the initialize thread to be terminated (cancelled or stopped).
  */
@@ -750,6 +811,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);
index 3b1e2ae5fb5d67e5e9e8779fe57af59db32c557c..efaf6f9b390a21947c258482b48e2bf8a39781d7 100644 (file)
@@ -4070,7 +4070,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));
        }
 
index 1665e20e0e3945210225453b6cfbb9d399bc6bcf..62d9cbeb6d9003da1acd897fd196db7e10273bb1 100644 (file)
@@ -446,6 +446,7 @@ tests = ['zpool_initialize_attach_detach_add_remove',
     'zpool_initialize_start_and_cancel_neg',
     'zpool_initialize_start_and_cancel_pos',
     'zpool_initialize_suspend_resume',
+    'zpool_initialize_uninit',
     'zpool_initialize_unsupported_vdevs',
     'zpool_initialize_verify_checksums',
     'zpool_initialize_verify_initialized']
index a4932fc988acd3f3051c20cf7d5cfe4e7a43d0a8..3e4120f52ca5790d4822701b6b6ba275a9a2eb8f 100644 (file)
@@ -1102,6 +1102,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
        functional/cli_root/zpool_initialize/zpool_initialize_start_and_cancel_neg.ksh \
        functional/cli_root/zpool_initialize/zpool_initialize_start_and_cancel_pos.ksh \
        functional/cli_root/zpool_initialize/zpool_initialize_suspend_resume.ksh \
+       functional/cli_root/zpool_initialize/zpool_initialize_uninit.ksh \
        functional/cli_root/zpool_initialize/zpool_initialize_unsupported_vdevs.ksh \
        functional/cli_root/zpool_initialize/zpool_initialize_verify_checksums.ksh \
        functional/cli_root/zpool_initialize/zpool_initialize_verify_initialized.ksh \
diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_initialize/zpool_initialize_uninit.ksh b/tests/zfs-tests/tests/functional/cli_root/zpool_initialize/zpool_initialize_uninit.ksh
new file mode 100755 (executable)
index 0000000..17f776c
--- /dev/null
@@ -0,0 +1,141 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or https://opensource.org/licenses/CDDL-1.0.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2016 by Delphix. All rights reserved.
+# Copyright (C) 2023 Lawrence Livermore National Security, LLC.
+#
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/cli_root/zpool_initialize/zpool_initialize.kshlib
+
+#
+# DESCRIPTION:
+# Starting, stopping, uninitializing, and restart an initialize works.
+#
+# STRATEGY:
+# 1. Create a one-disk pool.
+# 2. Verify uninitialize succeeds for uninitialized pool.
+# 3. Verify pool wide cancel|suspend + uninit
+#   a. Start initializing and verify that initializing is active.
+#   b. Verify uninitialize fails when actively initializing.
+#   c. Cancel or suspend initializing and verify that initializing is not active.
+#   d. Verify uninitialize succeeds after being cancelled.
+# 4. Verify per-disk cancel|suspend + uninit
+#
+
+DISK1="$(echo $DISKS | cut -d' ' -f1)"
+DISK2="$(echo $DISKS | cut -d' ' -f2)"
+DISK3="$(echo $DISKS | cut -d' ' -f3)"
+
+function status_check # pool disk1-state disk2-state disk3-state
+{
+        typeset pool="$1"
+        typeset disk1_state="$2"
+        typeset disk2_state="$3"
+        typeset disk3_state="$4"
+
+       state=$(zpool status -i "$pool" | grep "$DISK1" | grep "$disk1_state")
+        if [[ -z "$state" ]]; then
+               log_fail "DISK1 state; expected='$disk1_state' got '$state'"
+       fi
+
+       state=$(zpool status -i "$pool" | grep "$DISK2" | grep "$disk2_state")
+        if [[ -z "$state" ]]; then
+               log_fail "DISK2 state; expected='$disk2_state' got '$state'"
+       fi
+
+       state=$(zpool status -i "$pool" | grep "$DISK3" | grep "$disk3_state")
+        if [[ -z "$state" ]]; then
+               log_fail "DISK3 state; expected='$disk3_state' got '$state'"
+       fi
+}
+
+function status_check_all # pool disk-state
+{
+        typeset pool="$1"
+        typeset disk_state="$2"
+
+       status_check "$pool" "$disk_state" "$disk_state" "$disk_state"
+}
+
+# 1. Create a one-disk pool.
+log_must zpool create -f $TESTPOOL $DISK1 $DISK2 $DISK3
+status_check_all $TESTPOOL "uninitialized"
+
+# 2. Verify uninitialize succeeds for uninitialized pool.
+log_must zpool initialize -u $TESTPOOL
+status_check_all $TESTPOOL "uninitialized"
+
+# 3. Verify pool wide cancel + uninit
+log_must zpool initialize $TESTPOOL
+status_check_all $TESTPOOL "[[:digit:]]* initialized"
+
+log_mustnot zpool initialize -u $TESTPOOL
+status_check_all $TESTPOOL "[[:digit:]]* initialized"
+
+log_must zpool initialize -c $TESTPOOL
+status_check_all $TESTPOOL "uninitialized"
+
+log_must zpool initialize -u $TESTPOOL
+status_check_all $TESTPOOL "uninitialized"
+
+# 3. Verify pool wide suspend + uninit
+log_must zpool initialize $TESTPOOL
+status_check_all $TESTPOOL "[[:digit:]]* initialized"
+
+log_mustnot zpool initialize -u $TESTPOOL
+status_check_all $TESTPOOL "[[:digit:]]* initialized"
+
+log_must zpool initialize -s $TESTPOOL
+status_check_all $TESTPOOL "suspended"
+
+log_must zpool initialize -u $TESTPOOL
+status_check_all $TESTPOOL "uninitialized"
+
+# 4. Verify per-disk cancel|suspend + uninit
+log_must zpool initialize $TESTPOOL
+status_check_all $TESTPOOL "[[:digit:]]* initialized"
+
+log_must zpool initialize -c $TESTPOOL $DISK1
+log_must zpool initialize -s $TESTPOOL $DISK2
+log_mustnot zpool initialize -u $TESTPOOL $DISK3
+status_check $TESTPOOL "uninitialized" "suspended" "[[:digit:]]* initialized"
+
+log_must zpool initialize -u $TESTPOOL $DISK1
+status_check $TESTPOOL "uninitialized" "suspended" "[[:digit:]]* initialized"
+
+log_must zpool initialize -u $TESTPOOL $DISK2
+status_check $TESTPOOL "uninitialized" "uninitialized" "[[:digit:]]* initialized"
+
+log_must zpool initialize $TESTPOOL $DISK1
+status_check $TESTPOOL "[[:digit:]]* initialized" "uninitialized" "[[:digit:]]* initialized"
+
+log_must zpool initialize $TESTPOOL $DISK2
+status_check_all $TESTPOOL "[[:digit:]]* initialized"
+
+log_must zpool initialize -s $TESTPOOL
+status_check_all $TESTPOOL "suspended"
+
+log_must zpool initialize -u $TESTPOOL $DISK1 $DISK2 $DISK3
+status_check_all $TESTPOOL "uninitialized"
+
+log_pass "Initialize start + cancel/suspend + uninit + start works"