mirror of
https://github.com/freebsd/freebsd-src.git
synced 2026-10-28 11:25:21 +01:00
zpool initialize: add -z to write zeroes
zpool initialize writes a non-zero pattern (0xdeadbeefdeadbeee) to free space. The pattern can be changed globally with the zfs_initialize_value module parameter, but there was no per-command way to zero free space, which is what is wanted to reclaim thinly provisioned backing storage. Add a -z/--zero flag to "zpool initialize" that writes zeroes for that run. The fill value is threaded from the CLI through libzfs and the POOL_INITIALIZE ioctl into vdev_initialize(), and is recorded in the leaf ZAP so a run that is suspended and resumed (including across export and import) keeps writing the same value. When no value is supplied the zfs_initialize_value default is used, preserving existing behavior. -z is only valid when starting initialization. The zpool-initialize(8) page notes that -z targets backing storage which does not support TRIM and points at zpool-trim(8) as the more efficient option when the device does support it; zfs.4 documents zfs_initialize_value as the default fill value. Add a zpool_initialize_zero test that pre-fills a device, runs "initialize -z", and checks its free space is zeroed and the chosen value is persisted, and cover the optional ZPOOL_INITIALIZE_VALUE ioctl key in test_vdev_initialize(). Reviewed-by: Brian Behlendorf <[email protected]> Reviewed-by: Alexander Motin <[email protected]> Reviewed-by: Akash B <[email protected]> Signed-off-by: MorganaFuture <[email protected]> Closes #16778 Closes #18765
This commit is contained in:
1 parent
5833b24fcd
commit
50557cc803
22 files changed
+248
-44
No files matched your search
+22
-7
@@ -525,8 +525,8 @@ get_usage(zpool_help_t idx)
|
||||
case HELP_REOPEN:
|
||||
return (gettext("\treopen [-n] <pool>\n"));
|
||||
case HELP_INITIALIZE:
|
||||
return (gettext("\tinitialize [-c | -s | -u] [-w] <-a | <pool> "
|
||||
"[<device> ...]>\n"));
|
||||
return (gettext("\tinitialize [-c | -s | -u] [-w] [-z] "
|
||||
"<-a | <pool> [<device> ...]>\n"));
|
||||
case HELP_SCRUB:
|
||||
return (gettext("\tscrub [-e | -s | -p | -t | -C [-t] | "
|
||||
"[-S date] [-E date] [-t]] [-w]\n"
|
||||
@@ -772,7 +772,7 @@ usage(boolean_t requested)
|
||||
}
|
||||
|
||||
/*
|
||||
* zpool initialize [-c | -s | -u] [-w] <-a | pool> [<vdev> ...]
|
||||
* zpool initialize [-c | -s | -u] [-w] [-z] <-a | pool> [<vdev> ...]
|
||||
* Initialize all unused blocks in the specified vdevs, or all vdevs in the pool
|
||||
* if none specified.
|
||||
*
|
||||
@@ -791,6 +791,7 @@ zpool_do_initialize(int argc, char **argv)
|
||||
int err = 0;
|
||||
boolean_t wait = B_FALSE;
|
||||
boolean_t initialize_all = B_FALSE;
|
||||
boolean_t zero = B_FALSE;
|
||||
|
||||
struct option long_options[] = {
|
||||
{"cancel", no_argument, NULL, 'c'},
|
||||
@@ -798,16 +799,20 @@ zpool_do_initialize(int argc, char **argv)
|
||||
{"uninit", no_argument, NULL, 'u'},
|
||||
{"wait", no_argument, NULL, 'w'},
|
||||
{"all", no_argument, NULL, 'a'},
|
||||
{"zero", no_argument, NULL, 'z'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
pool_initialize_func_t cmd_type = POOL_INITIALIZE_START;
|
||||
while ((c = getopt_long(argc, argv, "acsuw", long_options,
|
||||
while ((c = getopt_long(argc, argv, "acsuwz", long_options,
|
||||
NULL)) != -1) {
|
||||
switch (c) {
|
||||
case 'a':
|
||||
initialize_all = B_TRUE;
|
||||
break;
|
||||
case 'z':
|
||||
zero = B_TRUE;
|
||||
break;
|
||||
case 'c':
|
||||
if (cmd_type != POOL_INITIALIZE_START &&
|
||||
cmd_type != POOL_INITIALIZE_CANCEL) {
|
||||
@@ -856,7 +861,9 @@ zpool_do_initialize(int argc, char **argv)
|
||||
|
||||
initialize_cbdata_t cbdata = {
|
||||
.wait = wait,
|
||||
.cmd_type = cmd_type
|
||||
.cmd_type = cmd_type,
|
||||
.value = 0,
|
||||
.value_provided = zero
|
||||
};
|
||||
|
||||
if (initialize_all && argc > 0) {
|
||||
@@ -876,6 +883,12 @@ zpool_do_initialize(int argc, char **argv)
|
||||
usage(B_FALSE);
|
||||
}
|
||||
|
||||
if (zero && (cmd_type != POOL_INITIALIZE_START)) {
|
||||
(void) fprintf(stderr, gettext("-z cannot be used with -c, -s "
|
||||
"or -u\n"));
|
||||
usage(B_FALSE);
|
||||
}
|
||||
|
||||
if (argc == 0 && initialize_all) {
|
||||
/* Initilize each pool recursively */
|
||||
err = for_each_pool(argc, argv, B_TRUE, NULL, ZFS_TYPE_POOL,
|
||||
@@ -899,9 +912,11 @@ zpool_do_initialize(int argc, char **argv)
|
||||
fnvlist_add_boolean(vdevs, argv[i]);
|
||||
}
|
||||
if (wait)
|
||||
err = zpool_initialize_wait(zhp, cmd_type, vdevs);
|
||||
err = zpool_initialize_wait(zhp, cmd_type, vdevs,
|
||||
cbdata.value, cbdata.value_provided);
|
||||
else
|
||||
err = zpool_initialize(zhp, cmd_type, vdevs);
|
||||
err = zpool_initialize(zhp, cmd_type, vdevs,
|
||||
cbdata.value, cbdata.value_provided);
|
||||
fnvlist_free(vdevs);
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -7253,7 +7253,8 @@ ztest_initialize(ztest_ds_t *zd, uint64_t id)
|
||||
nvlist_t *vdev_guids = fnvlist_alloc();
|
||||
nvlist_t *vdev_errlist = fnvlist_alloc();
|
||||
fnvlist_add_uint64(vdev_guids, path, guid);
|
||||
error = spa_vdev_initialize(spa, vdev_guids, cmd, vdev_errlist);
|
||||
error = spa_vdev_initialize(spa, vdev_guids, cmd, 0, B_FALSE,
|
||||
vdev_errlist);
|
||||
fnvlist_free(vdev_guids);
|
||||
fnvlist_free(vdev_errlist);
|
||||
|
||||
|
||||
+4
-2
@@ -299,6 +299,8 @@ typedef struct trim_cbdata {
|
||||
typedef struct initialize_cbdata {
|
||||
boolean_t wait;
|
||||
pool_initialize_func_t cmd_type;
|
||||
uint64_t value;
|
||||
boolean_t value_provided;
|
||||
} initialize_cbdata_t;
|
||||
/*
|
||||
* Functions to manipulate pool and vdev state
|
||||
@@ -308,9 +310,9 @@ _LIBZFS_H int zpool_scan_range(zpool_handle_t *, pool_scan_func_t,
|
||||
pool_scrub_cmd_t, pool_scrub_flags_t, time_t, time_t);
|
||||
_LIBZFS_H int zpool_initialize_one(zpool_handle_t *, void *);
|
||||
_LIBZFS_H int zpool_initialize(zpool_handle_t *, pool_initialize_func_t,
|
||||
nvlist_t *);
|
||||
nvlist_t *, uint64_t, boolean_t);
|
||||
_LIBZFS_H int zpool_initialize_wait(zpool_handle_t *, pool_initialize_func_t,
|
||||
nvlist_t *);
|
||||
nvlist_t *, uint64_t, boolean_t);
|
||||
_LIBZFS_H int zpool_trim(zpool_handle_t *, pool_trim_func_t, nvlist_t *,
|
||||
trimflags_t *);
|
||||
_LIBZFS_H int zpool_condense(zpool_handle_t *, const char *, const char *);
|
||||
|
||||
@@ -70,7 +70,7 @@ _LIBZFS_CORE_H int lzc_unload_key(const char *);
|
||||
_LIBZFS_CORE_H int lzc_change_key(const char *, uint64_t, nvlist_t *, uint8_t *,
|
||||
uint_t);
|
||||
_LIBZFS_CORE_H int lzc_initialize(const char *, pool_initialize_func_t,
|
||||
nvlist_t *, nvlist_t **);
|
||||
uint64_t, boolean_t, nvlist_t *, nvlist_t **);
|
||||
_LIBZFS_CORE_H int lzc_trim(const char *, pool_trim_func_t, uint64_t, boolean_t,
|
||||
nvlist_t *, nvlist_t **);
|
||||
_LIBZFS_CORE_H int lzc_redact(const char *, const char *, nvlist_t *);
|
||||
|
||||
@@ -1095,6 +1095,8 @@ typedef struct zpool_load_policy {
|
||||
"com.delphix:vdev_initialize_state"
|
||||
#define VDEV_LEAF_ZAP_INITIALIZE_ACTION_TIME \
|
||||
"com.delphix:vdev_initialize_action_time"
|
||||
#define VDEV_LEAF_ZAP_INITIALIZE_VALUE \
|
||||
"org.openzfs:vdev_initialize_value"
|
||||
|
||||
/* vdev TRIM state */
|
||||
#define VDEV_LEAF_ZAP_TRIM_LAST_OFFSET \
|
||||
@@ -1928,6 +1930,7 @@ typedef enum {
|
||||
*/
|
||||
#define ZPOOL_INITIALIZE_COMMAND "initialize_command"
|
||||
#define ZPOOL_INITIALIZE_VDEVS "initialize_vdevs"
|
||||
#define ZPOOL_INITIALIZE_VALUE "initialize_value"
|
||||
|
||||
/*
|
||||
* The following are names used when invoking ZFS_IOC_POOL_REGUID.
|
||||
|
||||
+1
-1
@@ -827,7 +827,7 @@ extern int spa_vdev_alloc(spa_t *spa, uint64_t guid);
|
||||
extern int spa_vdev_noalloc(spa_t *spa, uint64_t guid);
|
||||
extern boolean_t spa_vdev_remove_active(spa_t *spa);
|
||||
extern int spa_vdev_initialize(spa_t *spa, nvlist_t *nv, uint64_t cmd_type,
|
||||
nvlist_t *vdev_errlist);
|
||||
uint64_t value, boolean_t value_provided, nvlist_t *vdev_errlist);
|
||||
extern int spa_vdev_trim(spa_t *spa, nvlist_t *nv, uint64_t cmd_type,
|
||||
uint64_t rate, boolean_t partial, boolean_t secure, nvlist_t *vdev_errlist);
|
||||
extern int spa_vdev_setpath(spa_t *spa, uint64_t guid, const char *newpath);
|
||||
|
||||
@@ -291,6 +291,8 @@ struct vdev {
|
||||
kcondvar_t vdev_initialize_cv;
|
||||
uint64_t vdev_initialize_offset[TXG_SIZE];
|
||||
uint64_t vdev_initialize_last_offset;
|
||||
/* value written to free space by the initializing thread */
|
||||
uint64_t vdev_initialize_value;
|
||||
/* valid while initializing */
|
||||
zfs_range_tree_t *vdev_initialize_tree;
|
||||
uint64_t vdev_initialize_bytes_est;
|
||||
|
||||
@@ -33,7 +33,8 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern void vdev_initialize(vdev_t *vd);
|
||||
extern void vdev_initialize(vdev_t *vd, uint64_t value,
|
||||
boolean_t value_provided);
|
||||
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);
|
||||
|
||||
@@ -6767,6 +6767,8 @@
|
||||
<function-decl name='lzc_initialize' visibility='default' binding='global' size-in-bits='64'>
|
||||
<parameter type-id='80f4b756'/>
|
||||
<parameter type-id='7063e1ab'/>
|
||||
<parameter type-id='9c313c2d'/>
|
||||
<parameter type-id='c19b74c3'/>
|
||||
<parameter type-id='5ce45b60'/>
|
||||
<parameter type-id='857bb57e'/>
|
||||
<return type-id='95e97e5e'/>
|
||||
@@ -7237,12 +7239,16 @@
|
||||
<parameter type-id='4c81de99' name='zhp'/>
|
||||
<parameter type-id='7063e1ab' name='cmd_type'/>
|
||||
<parameter type-id='5ce45b60' name='vds'/>
|
||||
<parameter type-id='9c313c2d' name='value'/>
|
||||
<parameter type-id='c19b74c3' name='value_provided'/>
|
||||
<return type-id='95e97e5e'/>
|
||||
</function-decl>
|
||||
<function-decl name='zpool_initialize_wait' mangled-name='zpool_initialize_wait' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='zpool_initialize_wait'>
|
||||
<parameter type-id='4c81de99' name='zhp'/>
|
||||
<parameter type-id='7063e1ab' name='cmd_type'/>
|
||||
<parameter type-id='5ce45b60' name='vds'/>
|
||||
<parameter type-id='9c313c2d' name='value'/>
|
||||
<parameter type-id='c19b74c3' name='value_provided'/>
|
||||
<return type-id='95e97e5e'/>
|
||||
</function-decl>
|
||||
<function-decl name='zpool_collect_leaves' mangled-name='zpool_collect_leaves' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='zpool_collect_leaves'>
|
||||
|
||||
@@ -2604,9 +2604,11 @@ zpool_initialize_one(zpool_handle_t *zhp, void *data)
|
||||
ZPOOL_CONFIG_VDEV_TREE);
|
||||
zpool_collect_leaves(zhp, nvroot, vdevs);
|
||||
if (cb->wait)
|
||||
error = zpool_initialize_wait(zhp, cb->cmd_type, vdevs);
|
||||
error = zpool_initialize_wait(zhp, cb->cmd_type, vdevs,
|
||||
cb->value, cb->value_provided);
|
||||
else
|
||||
error = zpool_initialize(zhp, cb->cmd_type, vdevs);
|
||||
error = zpool_initialize(zhp, cb->cmd_type, vdevs,
|
||||
cb->value, cb->value_provided);
|
||||
fnvlist_free(vdevs);
|
||||
|
||||
return (error);
|
||||
@@ -2618,7 +2620,7 @@ zpool_initialize_one(zpool_handle_t *zhp, void *data)
|
||||
*/
|
||||
static int
|
||||
zpool_initialize_impl(zpool_handle_t *zhp, pool_initialize_func_t cmd_type,
|
||||
nvlist_t *vds, boolean_t wait)
|
||||
nvlist_t *vds, uint64_t value, boolean_t value_provided, boolean_t wait)
|
||||
{
|
||||
int err;
|
||||
|
||||
@@ -2637,7 +2639,7 @@ zpool_initialize_impl(zpool_handle_t *zhp, pool_initialize_func_t cmd_type,
|
||||
}
|
||||
|
||||
err = lzc_initialize(zhp->zpool_name, cmd_type,
|
||||
vdev_guids, &errlist);
|
||||
value, value_provided, vdev_guids, &errlist);
|
||||
|
||||
if (err != 0) {
|
||||
if (errlist != NULL && nvlist_lookup_nvlist(errlist,
|
||||
@@ -2705,9 +2707,10 @@ out:
|
||||
*/
|
||||
int
|
||||
zpool_initialize(zpool_handle_t *zhp, pool_initialize_func_t cmd_type,
|
||||
nvlist_t *vds)
|
||||
nvlist_t *vds, uint64_t value, boolean_t value_provided)
|
||||
{
|
||||
return (zpool_initialize_impl(zhp, cmd_type, vds, B_FALSE));
|
||||
return (zpool_initialize_impl(zhp, cmd_type, vds, value, value_provided,
|
||||
B_FALSE));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2715,9 +2718,10 @@ zpool_initialize(zpool_handle_t *zhp, pool_initialize_func_t cmd_type,
|
||||
*/
|
||||
int
|
||||
zpool_initialize_wait(zpool_handle_t *zhp, pool_initialize_func_t cmd_type,
|
||||
nvlist_t *vds)
|
||||
nvlist_t *vds, uint64_t value, boolean_t value_provided)
|
||||
{
|
||||
return (zpool_initialize_impl(zhp, cmd_type, vds, B_TRUE));
|
||||
return (zpool_initialize_impl(zhp, cmd_type, vds, value, value_provided,
|
||||
B_TRUE));
|
||||
}
|
||||
|
||||
static int
|
||||
|
||||
@@ -4282,6 +4282,8 @@
|
||||
<function-decl name='lzc_initialize' mangled-name='lzc_initialize' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='lzc_initialize'>
|
||||
<parameter type-id='80f4b756' name='poolname'/>
|
||||
<parameter type-id='7063e1ab' name='cmd_type'/>
|
||||
<parameter type-id='9c313c2d' name='value'/>
|
||||
<parameter type-id='c19b74c3' name='value_provided'/>
|
||||
<parameter type-id='5ce45b60' name='vdevs'/>
|
||||
<parameter type-id='857bb57e' name='errlist'/>
|
||||
<return type-id='95e97e5e'/>
|
||||
|
||||
@@ -1906,13 +1906,16 @@ lzc_reopen(const char *pool_name, boolean_t scrub_restart)
|
||||
*/
|
||||
int
|
||||
lzc_initialize(const char *poolname, pool_initialize_func_t cmd_type,
|
||||
nvlist_t *vdevs, nvlist_t **errlist)
|
||||
uint64_t value, boolean_t value_provided, nvlist_t *vdevs,
|
||||
nvlist_t **errlist)
|
||||
{
|
||||
int error;
|
||||
|
||||
nvlist_t *args = fnvlist_alloc();
|
||||
fnvlist_add_uint64(args, ZPOOL_INITIALIZE_COMMAND, (uint64_t)cmd_type);
|
||||
fnvlist_add_nvlist(args, ZPOOL_INITIALIZE_VDEVS, vdevs);
|
||||
if (value_provided)
|
||||
fnvlist_add_uint64(args, ZPOOL_INITIALIZE_VALUE, value);
|
||||
|
||||
error = lzc_ioctl(ZFS_IOC_POOL_INITIALIZE, poolname, args, errlist);
|
||||
|
||||
|
||||
+8
-3
@@ -20,7 +20,7 @@
|
||||
.\" own identifying information:
|
||||
.\" Portions Copyright [yyyy] [name of copyright owner]
|
||||
.\"
|
||||
.Dd May 26, 2026
|
||||
.Dd July 12, 2026
|
||||
.Dt ZFS 4
|
||||
.Os
|
||||
.
|
||||
@@ -1918,8 +1918,13 @@ The default value of 5 transaction groups typically provides sufficient time
|
||||
for import and mount operations to complete on most systems.
|
||||
.
|
||||
.It Sy zfs_initialize_value Ns = Ns Sy 16045690984833335022 Po 0xDEADBEEFDEADBEEE Pc Pq u64
|
||||
Pattern written to vdev free space by
|
||||
.Xr zpool-initialize 8 .
|
||||
Default pattern written to vdev free space by
|
||||
.Xr zpool-initialize 8 ,
|
||||
used unless
|
||||
.Nm zpool Cm initialize Fl z
|
||||
requests zeroes instead.
|
||||
The value in effect when a device starts initializing is recorded per device
|
||||
and reused if the run is suspended and later resumed.
|
||||
.
|
||||
.It Sy zfs_initialize_chunk_size Ns = Ns Sy 1048576 Ns B Po 1 MiB Pc Pq u64
|
||||
Size of writes used by
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
.\" Copyright (c) 2017 Open-E, Inc. All Rights Reserved.
|
||||
.\" Copyright (c) 2025 Hewlett Packard Enterprise Development LP.
|
||||
.\"
|
||||
.Dd July 30, 2025
|
||||
.Dd July 12, 2026
|
||||
.Dt ZPOOL-INITIALIZE 8
|
||||
.Os
|
||||
.
|
||||
@@ -40,6 +40,7 @@
|
||||
.Cm initialize
|
||||
.Op Fl c Ns | Ns Fl s | Ns Fl u
|
||||
.Op Fl w
|
||||
.Op Fl z
|
||||
.Fl a Ns | Ns Ar pool
|
||||
.Oo Ar device Oc Ns …
|
||||
.
|
||||
@@ -48,6 +49,9 @@ Begins initializing by writing to all unallocated regions on the specified
|
||||
devices, or all eligible devices in the pool if no individual devices are
|
||||
specified.
|
||||
Only leaf data or log devices may be initialized.
|
||||
By default a non-zero pattern is written; see
|
||||
.Fl z
|
||||
to write zeroes instead.
|
||||
.Bl -tag -width Ds
|
||||
.It Fl a , -all
|
||||
Begin, cancel, suspend initializing on
|
||||
@@ -76,6 +80,16 @@ with no flags can be used to re-initialize all unallocated regions on
|
||||
the relevant target devices.
|
||||
.It Fl w , -wait
|
||||
Wait until the devices have finished initializing before returning.
|
||||
.It Fl z , -zero
|
||||
Write zeroes to the unallocated regions instead of the default non-zero
|
||||
pattern.
|
||||
This is useful for reclaiming space on thinly provisioned backing storage which
|
||||
does not support TRIM.
|
||||
If the backing storage supports TRIM,
|
||||
.Xr zpool-trim 8
|
||||
is typically the more efficient way to return space to it.
|
||||
The chosen value is recorded per device, so a run that is suspended and later
|
||||
resumed keeps writing zeroes.
|
||||
.El
|
||||
.
|
||||
.Sh SEE ALSO
|
||||
|
||||
+4
-4
@@ -8898,7 +8898,7 @@ spa_vdev_detach(spa_t *spa, uint64_t guid, uint64_t pguid, int replace_done)
|
||||
|
||||
static int
|
||||
spa_vdev_initialize_impl(spa_t *spa, uint64_t guid, uint64_t cmd_type,
|
||||
list_t *vd_list)
|
||||
uint64_t value, boolean_t value_provided, list_t *vd_list)
|
||||
{
|
||||
ASSERT(spa_namespace_held());
|
||||
|
||||
@@ -8948,7 +8948,7 @@ spa_vdev_initialize_impl(spa_t *spa, uint64_t guid, uint64_t cmd_type,
|
||||
|
||||
switch (cmd_type) {
|
||||
case POOL_INITIALIZE_START:
|
||||
vdev_initialize(vd);
|
||||
vdev_initialize(vd, value, value_provided);
|
||||
break;
|
||||
case POOL_INITIALIZE_CANCEL:
|
||||
vdev_initialize_stop(vd, VDEV_INITIALIZE_CANCELED, vd_list);
|
||||
@@ -8969,7 +8969,7 @@ spa_vdev_initialize_impl(spa_t *spa, uint64_t guid, uint64_t cmd_type,
|
||||
|
||||
int
|
||||
spa_vdev_initialize(spa_t *spa, nvlist_t *nv, uint64_t cmd_type,
|
||||
nvlist_t *vdev_errlist)
|
||||
uint64_t value, boolean_t value_provided, nvlist_t *vdev_errlist)
|
||||
{
|
||||
int total_errors = 0;
|
||||
list_t vd_list;
|
||||
@@ -8991,7 +8991,7 @@ spa_vdev_initialize(spa_t *spa, nvlist_t *nv, uint64_t cmd_type,
|
||||
uint64_t vdev_guid = fnvpair_value_uint64(pair);
|
||||
|
||||
int error = spa_vdev_initialize_impl(spa, vdev_guid, cmd_type,
|
||||
&vd_list);
|
||||
value, value_provided, &vd_list);
|
||||
if (error != 0) {
|
||||
char guid_as_str[MAXNAMELEN];
|
||||
|
||||
|
||||
+2
-1
@@ -4579,7 +4579,8 @@ vdev_online(spa_t *spa, uint64_t guid, uint64_t flags, vdev_state_t *newstate)
|
||||
if (vdev_writeable(vd) &&
|
||||
vd->vdev_initialize_thread == NULL &&
|
||||
vd->vdev_initialize_state == VDEV_INITIALIZE_ACTIVE) {
|
||||
(void) vdev_initialize(vd);
|
||||
/* Preserve the fill value chosen when the run started. */
|
||||
vdev_initialize(vd, vd->vdev_initialize_value, B_TRUE);
|
||||
}
|
||||
mutex_exit(&vd->vdev_initialize_lock);
|
||||
|
||||
|
||||
@@ -97,6 +97,11 @@ vdev_initialize_zap_update_sync(void *arg, dmu_tx_t *tx)
|
||||
VERIFY0(zap_update(mos, vd->vdev_leaf_zap,
|
||||
VDEV_LEAF_ZAP_INITIALIZE_STATE, sizeof (initialize_state), 1,
|
||||
&initialize_state, tx));
|
||||
|
||||
uint64_t initialize_value = vd->vdev_initialize_value;
|
||||
VERIFY0(zap_update(mos, vd->vdev_leaf_zap,
|
||||
VDEV_LEAF_ZAP_INITIALIZE_VALUE, sizeof (initialize_value), 1,
|
||||
&initialize_value, tx));
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -130,6 +135,10 @@ vdev_initialize_zap_remove_sync(void *arg, dmu_tx_t *tx)
|
||||
error = zap_remove(mos, vd->vdev_leaf_zap,
|
||||
VDEV_LEAF_ZAP_INITIALIZE_ACTION_TIME, tx);
|
||||
VERIFY(error == 0 || error == ENOENT);
|
||||
|
||||
error = zap_remove(mos, vd->vdev_leaf_zap,
|
||||
VDEV_LEAF_ZAP_INITIALIZE_VALUE, tx);
|
||||
VERIFY(error == 0 || error == ENOENT);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -293,31 +302,31 @@ vdev_initialize_write(vdev_t *vd, uint64_t start, uint64_t size, abd_t *data)
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback to fill each ABD chunk with zfs_initialize_value. len must be
|
||||
* Callback to fill each ABD chunk with the requested fill value. len must be
|
||||
* divisible by sizeof (uint64_t), and buf must be 8-byte aligned. The ABD
|
||||
* allocation will guarantee these for us.
|
||||
*/
|
||||
static int
|
||||
vdev_initialize_block_fill(void *buf, size_t len, void *unused)
|
||||
vdev_initialize_block_fill(void *buf, size_t len, void *arg)
|
||||
{
|
||||
(void) unused;
|
||||
uint64_t value = *(uint64_t *)arg;
|
||||
|
||||
ASSERT0(len % sizeof (uint64_t));
|
||||
for (uint64_t i = 0; i < len; i += sizeof (uint64_t)) {
|
||||
*(uint64_t *)((char *)(buf) + i) = zfs_initialize_value;
|
||||
*(uint64_t *)((char *)(buf) + i) = value;
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static abd_t *
|
||||
vdev_initialize_block_alloc(void)
|
||||
vdev_initialize_block_alloc(uint64_t value)
|
||||
{
|
||||
/* Allocate ABD for filler data */
|
||||
abd_t *data = abd_alloc_for_io(zfs_initialize_chunk_size, B_FALSE);
|
||||
|
||||
ASSERT0(zfs_initialize_chunk_size % sizeof (uint64_t));
|
||||
(void) abd_iterate_func(data, 0, zfs_initialize_chunk_size,
|
||||
vdev_initialize_block_fill, NULL);
|
||||
vdev_initialize_block_fill, &value);
|
||||
|
||||
return (data);
|
||||
}
|
||||
@@ -539,7 +548,8 @@ vdev_initialize_thread(void *arg)
|
||||
vd->vdev_initialize_last_offset = 0;
|
||||
VERIFY0(vdev_initialize_load(vd));
|
||||
|
||||
abd_t *deadbeef = vdev_initialize_block_alloc();
|
||||
abd_t *deadbeef =
|
||||
vdev_initialize_block_alloc(vd->vdev_initialize_value);
|
||||
|
||||
vd->vdev_initialize_tree = zfs_range_tree_create_flags(
|
||||
NULL, ZFS_RANGE_SEG64, NULL, 0, 0,
|
||||
@@ -627,7 +637,7 @@ vdev_initialize_thread(void *arg)
|
||||
* Device must be a leaf and not already be initializing.
|
||||
*/
|
||||
void
|
||||
vdev_initialize(vdev_t *vd)
|
||||
vdev_initialize(vdev_t *vd, uint64_t value, boolean_t value_provided)
|
||||
{
|
||||
ASSERT(MUTEX_HELD(&vd->vdev_initialize_lock));
|
||||
ASSERT(vd->vdev_ops->vdev_op_leaf);
|
||||
@@ -638,6 +648,20 @@ vdev_initialize(vdev_t *vd)
|
||||
ASSERT(!vd->vdev_top->vdev_removing);
|
||||
ASSERT(!vd->vdev_top->vdev_rz_expanding);
|
||||
|
||||
/*
|
||||
* Fix the fill value for the whole run so it is stable across a
|
||||
* suspend/resume (it is persisted to the leaf ZAP). An explicit value
|
||||
* always wins. Otherwise, when resuming an existing run keep the value
|
||||
* chosen when it started (already loaded from the leaf ZAP); only a
|
||||
* fresh run falls back to the module default.
|
||||
*/
|
||||
if (value_provided) {
|
||||
vd->vdev_initialize_value = value;
|
||||
} else if (vd->vdev_initialize_state != VDEV_INITIALIZE_ACTIVE &&
|
||||
vd->vdev_initialize_state != VDEV_INITIALIZE_SUSPENDED) {
|
||||
vd->vdev_initialize_value = zfs_initialize_value;
|
||||
}
|
||||
|
||||
vdev_initialize_change_state(vd, VDEV_INITIALIZE_ACTIVE);
|
||||
vd->vdev_initialize_thread = thread_create(NULL, 0,
|
||||
vdev_initialize_thread, vd, 0, &p0, TS_RUN, maxclsyspri);
|
||||
@@ -801,6 +825,18 @@ vdev_initialize_restart(vdev_t *vd)
|
||||
ASSERT(err == 0 || err == ENOENT);
|
||||
vd->vdev_initialize_action_time = timestamp;
|
||||
|
||||
/*
|
||||
* Restore the fill value chosen when this run started. Pools
|
||||
* initialized before this field existed fall back to the
|
||||
* module default, preserving their previous behavior.
|
||||
*/
|
||||
uint64_t value = zfs_initialize_value;
|
||||
err = zap_lookup(vd->vdev_spa->spa_meta_objset,
|
||||
vd->vdev_leaf_zap, VDEV_LEAF_ZAP_INITIALIZE_VALUE,
|
||||
sizeof (value), 1, &value);
|
||||
ASSERT(err == 0 || err == ENOENT);
|
||||
vd->vdev_initialize_value = value;
|
||||
|
||||
if ((vd->vdev_initialize_state == VDEV_INITIALIZE_SUSPENDED ||
|
||||
vd->vdev_offline) && !vd->vdev_top->vdev_rz_expanding) {
|
||||
/* load progress for reporting, but don't resume */
|
||||
@@ -810,7 +846,7 @@ vdev_initialize_restart(vdev_t *vd)
|
||||
!vd->vdev_top->vdev_removing &&
|
||||
!vd->vdev_top->vdev_rz_expanding &&
|
||||
vd->vdev_initialize_thread == NULL) {
|
||||
vdev_initialize(vd);
|
||||
vdev_initialize(vd, value, B_TRUE);
|
||||
}
|
||||
|
||||
mutex_exit(&vd->vdev_initialize_lock);
|
||||
|
||||
+11
-2
@@ -4798,7 +4798,8 @@ zfs_ioc_destroy(zfs_cmd_t *zc)
|
||||
*/
|
||||
static const zfs_ioc_key_t zfs_keys_pool_initialize[] = {
|
||||
{ZPOOL_INITIALIZE_COMMAND, DATA_TYPE_UINT64, 0},
|
||||
{ZPOOL_INITIALIZE_VDEVS, DATA_TYPE_NVLIST, 0}
|
||||
{ZPOOL_INITIALIZE_VDEVS, DATA_TYPE_NVLIST, 0},
|
||||
{ZPOOL_INITIALIZE_VALUE, DATA_TYPE_UINT64, ZK_OPTIONAL}
|
||||
};
|
||||
|
||||
static int
|
||||
@@ -4831,6 +4832,14 @@ zfs_ioc_pool_initialize(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* An explicit fill value is optional; when absent the initializing
|
||||
* thread uses the zfs_initialize_value module default.
|
||||
*/
|
||||
uint64_t value = 0;
|
||||
boolean_t value_provided = (nvlist_lookup_uint64(innvl,
|
||||
ZPOOL_INITIALIZE_VALUE, &value) == 0);
|
||||
|
||||
spa_t *spa;
|
||||
int error = spa_open(poolname, &spa, FTAG);
|
||||
if (error != 0)
|
||||
@@ -4838,7 +4847,7 @@ zfs_ioc_pool_initialize(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl)
|
||||
|
||||
nvlist_t *vdev_errlist = fnvlist_alloc();
|
||||
int total_errors = spa_vdev_initialize(spa, vdev_guids, cmd_type,
|
||||
vdev_errlist);
|
||||
value, value_provided, vdev_errlist);
|
||||
|
||||
if (fnvlist_size(vdev_errlist) > 0) {
|
||||
fnvlist_add_nvlist(outnvl, ZPOOL_INITIALIZE_VDEVS,
|
||||
|
||||
@@ -530,7 +530,8 @@ tests = ['zpool_initialize_attach_detach_add_remove',
|
||||
'zpool_initialize_uninit',
|
||||
'zpool_initialize_unsupported_vdevs',
|
||||
'zpool_initialize_verify_checksums',
|
||||
'zpool_initialize_verify_initialized']
|
||||
'zpool_initialize_verify_initialized',
|
||||
'zpool_initialize_zero']
|
||||
pre =
|
||||
tags = ['functional', 'cli_root', 'zpool_initialize']
|
||||
|
||||
|
||||
@@ -812,15 +812,19 @@ static void
|
||||
test_vdev_initialize(const char *pool)
|
||||
{
|
||||
nvlist_t *required = fnvlist_alloc();
|
||||
nvlist_t *optional = fnvlist_alloc();
|
||||
nvlist_t *vdev_guids = fnvlist_alloc();
|
||||
|
||||
fnvlist_add_uint64(vdev_guids, "path", 0xdeadbeefdeadbeef);
|
||||
fnvlist_add_uint64(required, ZPOOL_INITIALIZE_COMMAND,
|
||||
POOL_INITIALIZE_START);
|
||||
fnvlist_add_nvlist(required, ZPOOL_INITIALIZE_VDEVS, vdev_guids);
|
||||
fnvlist_add_uint64(optional, ZPOOL_INITIALIZE_VALUE, 0);
|
||||
|
||||
IOC_INPUT_TEST(ZFS_IOC_POOL_INITIALIZE, pool, required, NULL, EINVAL);
|
||||
IOC_INPUT_TEST(ZFS_IOC_POOL_INITIALIZE, pool, required, optional,
|
||||
EINVAL);
|
||||
nvlist_free(vdev_guids);
|
||||
nvlist_free(optional);
|
||||
nvlist_free(required);
|
||||
}
|
||||
|
||||
|
||||
@@ -1278,6 +1278,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
|
||||
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 \
|
||||
functional/cli_root/zpool_initialize/zpool_initialize_zero.ksh \
|
||||
functional/cli_root/zpool_labelclear/zpool_labelclear_active.ksh \
|
||||
functional/cli_root/zpool_labelclear/zpool_labelclear_exported.ksh \
|
||||
functional/cli_root/zpool_labelclear/zpool_labelclear_removed.ksh \
|
||||
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
#!/bin/ksh -p
|
||||
# SPDX-License-Identifier: CDDL-1.0
|
||||
#
|
||||
# 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
|
||||
#
|
||||
|
||||
. $STF_SUITE/include/libtest.shlib
|
||||
|
||||
#
|
||||
# DESCRIPTION:
|
||||
# "zpool initialize -z" writes zeroes instead of the default pattern, and the
|
||||
# chosen value is persisted per device.
|
||||
#
|
||||
# STRATEGY:
|
||||
# 1. Pre-fill the backing file with a non-zero byte pattern, so that finding
|
||||
# zeroes afterwards actually proves -z did the work (rather than the check
|
||||
# passing on space that happened to already be zero).
|
||||
# 2. Create a one-disk pool and run "zpool initialize -w -z".
|
||||
# 3. Verify the zero fill value is persisted in the leaf ZAP.
|
||||
# 4. Verify the pre-existing non-zero pattern is gone from every metaslab.
|
||||
#
|
||||
|
||||
function cleanup
|
||||
{
|
||||
if datasetexists $TESTPOOL; then
|
||||
zpool destroy -f $TESTPOOL
|
||||
fi
|
||||
if [[ -d "$TESTDIR" ]]; then
|
||||
rm -rf "$TESTDIR"
|
||||
fi
|
||||
}
|
||||
log_onexit cleanup
|
||||
|
||||
log_assert "'zpool initialize -z' writes zeroes and persists the choice"
|
||||
|
||||
SMALLFILE="$TESTDIR/smallfile"
|
||||
|
||||
log_must mkdir "$TESTDIR"
|
||||
# Fill the whole device with 0xff so free space starts out non-zero.
|
||||
log_must eval "tr '\0' '\377' < /dev/zero | head -c $MINVDEVSIZE > $SMALLFILE"
|
||||
|
||||
log_must zpool create $TESTPOOL "$SMALLFILE"
|
||||
log_must zpool initialize -w -z $TESTPOOL
|
||||
|
||||
# The zero fill value must be recorded in the leaf ZAP.
|
||||
log_must eval "zdb -dddd $TESTPOOL | \
|
||||
grep -qE 'org.openzfs:vdev_initialize_value = 0( |\$)'"
|
||||
|
||||
log_must zpool export $TESTPOOL
|
||||
|
||||
# The device started out entirely 0xff. After zeroing, its free space (the
|
||||
# large majority of a freshly created pool) must actually contain zeroes: the
|
||||
# 0xff pre-fill must be gone AND zero words must dominate. Requiring zeroes to
|
||||
# dominate (rather than only "0xff is gone") also catches a regression that
|
||||
# wrote some other non-zero value. Use "-t x4" (not x8, unsupported on
|
||||
# FreeBSD) and count both patterns in a single pass.
|
||||
set -A counts $(od -An -tx4 -v "$SMALLFILE" | awk '
|
||||
{ for (i = 1; i <= NF; i++) {
|
||||
n++
|
||||
if ($i == "ffffffff") f++
|
||||
else if ($i == "00000000") z++
|
||||
} }
|
||||
END { print f + 0, z + 0, n + 0 }')
|
||||
ffwords=${counts[0]}
|
||||
zwords=${counts[1]}
|
||||
nwords=${counts[2]}
|
||||
log_note "after -z: 0xff words=$ffwords zero words=$zwords of $nwords"
|
||||
|
||||
if [[ $nwords -eq 0 ]]; then
|
||||
log_fail "Could not read back the device"
|
||||
elif [[ $ffwords -ge $((nwords / 2)) ]]; then
|
||||
log_fail "0xff pre-fill still present after 'initialize -z'"
|
||||
elif [[ $zwords -lt $((nwords / 2)) ]]; then
|
||||
log_fail "free space is not predominantly zero after 'initialize -z'"
|
||||
else
|
||||
log_pass "'zpool initialize -z' zeroed the pool's free space"
|
||||
fi
|
||||
Reference in new issue
Block a user