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:
MorganaFuture authored and GitHub committed 2026-07-24 13:00:18 -07:00
1 parent 5833b24fcd
commit 50557cc803
22 files changed
+248 -44

No files matched your search

+22 -7
View File
@@ -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
View File
@@ -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
View File
@@ -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 *);
+1 -1
View File
@@ -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 *);
+3
View File
@@ -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
View File
@@ -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);
+2
View File
@@ -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;
+2 -1
View File
@@ -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);
+6
View File
@@ -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'>
+12 -8
View File
@@ -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
+2
View File
@@ -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'/>
+4 -1
View File
@@ -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
View File
@@ -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
+15 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+45 -9
View File
@@ -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
View File
@@ -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,
+2 -1
View File
@@ -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']
+5 -1
View File
@@ -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);
}
+1
View File
@@ -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 \
@@ -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