Add the ability to uninitialize

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
This commit is contained in:
Brian Behlendorf 2023-05-18 10:02:20 -07:00 committed by GitHub
parent e0d5007bcf
commit e34e15ed6d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 258 additions and 16 deletions

View 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);
}

View 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;

View 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,

View File

@ -5741,7 +5741,8 @@
<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'>

View 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;

View File

@ -1249,7 +1249,8 @@
<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'>

View 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

View 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);
}

View 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);

View 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));
}

View 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']

View 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 \

View File

@ -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"