Create zap for root vdev

And add it to the AVZ, this is not backwards compatible with older pools
due to an assertion in spa_sync() that verifies the number of ZAPs of
all vdevs matches the number of ZAPs in the AVZ.

Granted, the assertion only applies to #DEBUG builds - still, a feature
flag is introduced to avoid the assertion, com.klarasystems:vdev_zaps_v2

Notably, this allows to get/set properties on the root vdev:

    % zpool set user:prop=value <pool> root-0

Before this commit, it was already possible to get/set properties on
top-level vdevs with the syntax <type>-<vdev_id> (e.g. mirror-0):

    % zpool set user:prop=value <pool> mirror-0

This syntax also applies to the root vdev as it is is of type 'root'
with a vdev_id of 0, root-0. The keyword 'root' as an alias for
'root-0'.

The following tests have been added:

    - zpool get all properties from root vdev
    - zpool set a property on root vdev
    - verify root vdev ZAP is created

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Rob Wing <rob.wing@klarasystems.com>
Sponsored-by: Seagate Technology
Submitted-by: Klara, Inc.
Closes #14405
This commit is contained in:
rob-wing 2023-04-20 09:07:56 -08:00 committed by GitHub
parent 71d191ef25
commit 3e4ed4213d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 339 additions and 38 deletions

View File

@ -7630,6 +7630,9 @@ mos_leak_vdev(vdev_t *vd)
mos_obj_refd(space_map_object(ms->ms_sm)); mos_obj_refd(space_map_object(ms->ms_sm));
} }
if (vd->vdev_root_zap != 0)
mos_obj_refd(vd->vdev_root_zap);
if (vd->vdev_top_zap != 0) { if (vd->vdev_top_zap != 0) {
mos_obj_refd(vd->vdev_top_zap); mos_obj_refd(vd->vdev_top_zap);
mos_leak_vdev_top_zap(vd); mos_leak_vdev_top_zap(vd);

View File

@ -10001,33 +10001,33 @@ get_callback_vdev(zpool_handle_t *zhp, char *vdevname, void *data)
return (0); return (0);
} }
static int
get_callback_vdev_width_cb(void *zhp_data, nvlist_t *nv, void *data)
{
zpool_handle_t *zhp = zhp_data;
zprop_get_cbdata_t *cbp = (zprop_get_cbdata_t *)data;
char *vdevname = zpool_vdev_name(g_zfs, zhp, nv,
cbp->cb_vdevs.cb_name_flags);
int ret;
/* Adjust the column widths for the vdev properties */
ret = vdev_expand_proplist(zhp, vdevname, &cbp->cb_proplist);
return (ret);
}
static int static int
get_callback_vdev_cb(void *zhp_data, nvlist_t *nv, void *data) get_callback_vdev_cb(void *zhp_data, nvlist_t *nv, void *data)
{ {
zpool_handle_t *zhp = zhp_data; zpool_handle_t *zhp = zhp_data;
zprop_get_cbdata_t *cbp = (zprop_get_cbdata_t *)data; zprop_get_cbdata_t *cbp = (zprop_get_cbdata_t *)data;
char *vdevname = zpool_vdev_name(g_zfs, zhp, nv, char *vdevname;
cbp->cb_vdevs.cb_name_flags); const char *type;
int ret; int ret;
/* Display the properties */ /*
* zpool_vdev_name() transforms the root vdev name (i.e., root-0) to the
* pool name for display purposes, which is not desired. Fallback to
* zpool_vdev_name() when not dealing with the root vdev.
*/
type = fnvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE);
if (zhp != NULL && strcmp(type, "root") == 0)
vdevname = strdup("root-0");
else
vdevname = zpool_vdev_name(g_zfs, zhp, nv,
cbp->cb_vdevs.cb_name_flags);
(void) vdev_expand_proplist(zhp, vdevname, &cbp->cb_proplist);
ret = get_callback_vdev(zhp, vdevname, data); ret = get_callback_vdev(zhp, vdevname, data);
free(vdevname);
return (ret); return (ret);
} }
@ -10042,7 +10042,6 @@ get_callback(zpool_handle_t *zhp, void *data)
if (cbp->cb_type == ZFS_TYPE_VDEV) { if (cbp->cb_type == ZFS_TYPE_VDEV) {
if (strcmp(cbp->cb_vdevs.cb_names[0], "all-vdevs") == 0) { if (strcmp(cbp->cb_vdevs.cb_names[0], "all-vdevs") == 0) {
for_each_vdev(zhp, get_callback_vdev_width_cb, data);
for_each_vdev(zhp, get_callback_vdev_cb, data); for_each_vdev(zhp, get_callback_vdev_cb, data);
} else { } else {
/* Adjust column widths for vdev properties */ /* Adjust column widths for vdev properties */
@ -10119,6 +10118,7 @@ zpool_do_get(int argc, char **argv)
int ret; int ret;
int c, i; int c, i;
char *propstr = NULL; char *propstr = NULL;
char *vdev = NULL;
cb.cb_first = B_TRUE; cb.cb_first = B_TRUE;
@ -10216,10 +10216,17 @@ found:
} else if (are_all_pools(1, argv)) { } else if (are_all_pools(1, argv)) {
/* The first arg is a pool name */ /* The first arg is a pool name */
if ((argc == 2 && strcmp(argv[1], "all-vdevs") == 0) || if ((argc == 2 && strcmp(argv[1], "all-vdevs") == 0) ||
(argc == 2 && strcmp(argv[1], "root") == 0) ||
are_vdevs_in_pool(argc - 1, argv + 1, argv[0], are_vdevs_in_pool(argc - 1, argv + 1, argv[0],
&cb.cb_vdevs)) { &cb.cb_vdevs)) {
if (strcmp(argv[1], "root") == 0)
vdev = strdup("root-0");
else
vdev = strdup(argv[1]);
/* ... and the rest are vdev names */ /* ... and the rest are vdev names */
cb.cb_vdevs.cb_names = argv + 1; cb.cb_vdevs.cb_names = &vdev;
cb.cb_vdevs.cb_names_count = argc - 1; cb.cb_vdevs.cb_names_count = argc - 1;
cb.cb_type = ZFS_TYPE_VDEV; cb.cb_type = ZFS_TYPE_VDEV;
argc = 1; /* One pool to process */ argc = 1; /* One pool to process */
@ -10264,6 +10271,9 @@ found:
else else
zprop_free_list(cb.cb_proplist); zprop_free_list(cb.cb_proplist);
if (vdev != NULL)
free(vdev);
return (ret); return (ret);
} }
@ -10365,6 +10375,7 @@ zpool_do_set(int argc, char **argv)
{ {
set_cbdata_t cb = { 0 }; set_cbdata_t cb = { 0 };
int error; int error;
char *vdev = NULL;
current_prop_type = ZFS_TYPE_POOL; current_prop_type = ZFS_TYPE_POOL;
if (argc > 1 && argv[1][0] == '-') { if (argc > 1 && argv[1][0] == '-') {
@ -10413,13 +10424,20 @@ zpool_do_set(int argc, char **argv)
/* argv[1], when supplied, is vdev name */ /* argv[1], when supplied, is vdev name */
if (argc == 2) { if (argc == 2) {
if (!are_vdevs_in_pool(1, argv + 1, argv[0], &cb.cb_vdevs)) {
if (strcmp(argv[1], "root") == 0)
vdev = strdup("root-0");
else
vdev = strdup(argv[1]);
if (!are_vdevs_in_pool(1, &vdev, argv[0], &cb.cb_vdevs)) {
(void) fprintf(stderr, gettext( (void) fprintf(stderr, gettext(
"cannot find '%s' in '%s': device not in pool\n"), "cannot find '%s' in '%s': device not in pool\n"),
argv[1], argv[0]); vdev, argv[0]);
free(vdev);
return (EINVAL); return (EINVAL);
} }
cb.cb_vdevs.cb_names = argv + 1; cb.cb_vdevs.cb_names = &vdev;
cb.cb_vdevs.cb_names_count = 1; cb.cb_vdevs.cb_names_count = 1;
cb.cb_type = ZFS_TYPE_VDEV; cb.cb_type = ZFS_TYPE_VDEV;
} }
@ -10427,6 +10445,9 @@ zpool_do_set(int argc, char **argv)
error = for_each_pool(1, argv, B_TRUE, NULL, ZFS_TYPE_POOL, error = for_each_pool(1, argv, B_TRUE, NULL, ZFS_TYPE_POOL,
B_FALSE, set_callback, &cb); B_FALSE, set_callback, &cb);
if (vdev != NULL)
free(vdev);
return (error); return (error);
} }

View File

@ -816,6 +816,7 @@ typedef struct zpool_load_policy {
#define ZPOOL_CONFIG_FEATURES_FOR_READ "features_for_read" #define ZPOOL_CONFIG_FEATURES_FOR_READ "features_for_read"
#define ZPOOL_CONFIG_FEATURE_STATS "feature_stats" /* not stored on disk */ #define ZPOOL_CONFIG_FEATURE_STATS "feature_stats" /* not stored on disk */
#define ZPOOL_CONFIG_ERRATA "errata" /* not stored on disk */ #define ZPOOL_CONFIG_ERRATA "errata" /* not stored on disk */
#define ZPOOL_CONFIG_VDEV_ROOT_ZAP "com.klarasystems:vdev_zap_root"
#define ZPOOL_CONFIG_VDEV_TOP_ZAP "com.delphix:vdev_zap_top" #define ZPOOL_CONFIG_VDEV_TOP_ZAP "com.delphix:vdev_zap_top"
#define ZPOOL_CONFIG_VDEV_LEAF_ZAP "com.delphix:vdev_zap_leaf" #define ZPOOL_CONFIG_VDEV_LEAF_ZAP "com.delphix:vdev_zap_leaf"
#define ZPOOL_CONFIG_HAS_PER_VDEV_ZAPS "com.delphix:has_per_vdev_zaps" #define ZPOOL_CONFIG_HAS_PER_VDEV_ZAPS "com.delphix:has_per_vdev_zaps"

View File

@ -277,6 +277,7 @@ struct vdev {
kthread_t *vdev_open_thread; /* thread opening children */ kthread_t *vdev_open_thread; /* thread opening children */
kthread_t *vdev_validate_thread; /* thread validating children */ kthread_t *vdev_validate_thread; /* thread validating children */
uint64_t vdev_crtxg; /* txg when top-level was added */ uint64_t vdev_crtxg; /* txg when top-level was added */
uint64_t vdev_root_zap;
/* /*
* Top-level vdev state. * Top-level vdev state.

View File

@ -79,6 +79,7 @@ typedef enum spa_feature {
SPA_FEATURE_HEAD_ERRLOG, SPA_FEATURE_HEAD_ERRLOG,
SPA_FEATURE_BLAKE3, SPA_FEATURE_BLAKE3,
SPA_FEATURE_BLOCK_CLONING, SPA_FEATURE_BLOCK_CLONING,
SPA_FEATURE_AVZ_V2,
SPA_FEATURES SPA_FEATURES
} spa_feature_t; } spa_feature_t;

View File

@ -595,7 +595,7 @@
<elf-symbol name='fletcher_4_superscalar_ops' size='128' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/> <elf-symbol name='fletcher_4_superscalar_ops' size='128' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='libzfs_config_ops' size='16' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/> <elf-symbol name='libzfs_config_ops' size='16' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='sa_protocol_names' size='16' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/> <elf-symbol name='sa_protocol_names' size='16' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='spa_feature_table' size='2128' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/> <elf-symbol name='spa_feature_table' size='2184' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='zfeature_checks_disable' size='4' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/> <elf-symbol name='zfeature_checks_disable' size='4' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='zfs_deleg_perm_tab' size='512' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/> <elf-symbol name='zfs_deleg_perm_tab' size='512' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='zfs_history_event_names' size='328' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/> <elf-symbol name='zfs_history_event_names' size='328' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
@ -5808,7 +5808,8 @@
<enumerator name='SPA_FEATURE_HEAD_ERRLOG' value='35'/> <enumerator name='SPA_FEATURE_HEAD_ERRLOG' value='35'/>
<enumerator name='SPA_FEATURE_BLAKE3' value='36'/> <enumerator name='SPA_FEATURE_BLAKE3' value='36'/>
<enumerator name='SPA_FEATURE_BLOCK_CLONING' value='37'/> <enumerator name='SPA_FEATURE_BLOCK_CLONING' value='37'/>
<enumerator name='SPA_FEATURES' value='38'/> <enumerator name='SPA_FEATURE_AVZ_V2' value='38'/>
<enumerator name='SPA_FEATURES' value='39'/>
</enum-decl> </enum-decl>
<typedef-decl name='spa_feature_t' type-id='33ecb627' id='d6618c78'/> <typedef-decl name='spa_feature_t' type-id='33ecb627' id='d6618c78'/>
<qualified-type-def type-id='22cce67b' const='yes' id='d2816df0'/> <qualified-type-def type-id='22cce67b' const='yes' id='d2816df0'/>
@ -8694,8 +8695,8 @@
</function-decl> </function-decl>
</abi-instr> </abi-instr>
<abi-instr address-size='64' path='module/zcommon/zfeature_common.c' language='LANG_C99'> <abi-instr address-size='64' path='module/zcommon/zfeature_common.c' language='LANG_C99'>
<array-type-def dimensions='1' type-id='83f29ca2' size-in-bits='17024' id='9944fffc'> <array-type-def dimensions='1' type-id='83f29ca2' size-in-bits='17472' id='dd432c71'>
<subrange length='38' type-id='7359adad' id='aa4ccdac'/> <subrange length='39' type-id='7359adad' id='ae4a9561'/>
</array-type-def> </array-type-def>
<enum-decl name='zfeature_flags' id='6db816a4'> <enum-decl name='zfeature_flags' id='6db816a4'>
<underlying-type type-id='9cac1fee'/> <underlying-type type-id='9cac1fee'/>
@ -8772,7 +8773,7 @@
<pointer-type-def type-id='611586a1' size-in-bits='64' id='2e243169'/> <pointer-type-def type-id='611586a1' size-in-bits='64' id='2e243169'/>
<qualified-type-def type-id='eaa32e2f' const='yes' id='83be723c'/> <qualified-type-def type-id='eaa32e2f' const='yes' id='83be723c'/>
<pointer-type-def type-id='83be723c' size-in-bits='64' id='7acd98a2'/> <pointer-type-def type-id='83be723c' size-in-bits='64' id='7acd98a2'/>
<var-decl name='spa_feature_table' type-id='9944fffc' mangled-name='spa_feature_table' visibility='default' elf-symbol-id='spa_feature_table'/> <var-decl name='spa_feature_table' type-id='dd432c71' mangled-name='spa_feature_table' visibility='default' elf-symbol-id='spa_feature_table'/>
<var-decl name='zfeature_checks_disable' type-id='c19b74c3' mangled-name='zfeature_checks_disable' visibility='default' elf-symbol-id='zfeature_checks_disable'/> <var-decl name='zfeature_checks_disable' type-id='c19b74c3' mangled-name='zfeature_checks_disable' visibility='default' elf-symbol-id='zfeature_checks_disable'/>
<function-decl name='opendir' visibility='default' binding='global' size-in-bits='64'> <function-decl name='opendir' visibility='default' binding='global' size-in-bits='64'>
<parameter type-id='80f4b756'/> <parameter type-id='80f4b756'/>

View File

@ -2859,6 +2859,7 @@ zpool_vdev_is_interior(const char *name)
strncmp(name, VDEV_TYPE_SPARE, strlen(VDEV_TYPE_SPARE)) == 0 || strncmp(name, VDEV_TYPE_SPARE, strlen(VDEV_TYPE_SPARE)) == 0 ||
strncmp(name, strncmp(name,
VDEV_TYPE_REPLACING, strlen(VDEV_TYPE_REPLACING)) == 0 || VDEV_TYPE_REPLACING, strlen(VDEV_TYPE_REPLACING)) == 0 ||
strncmp(name, VDEV_TYPE_ROOT, strlen(VDEV_TYPE_ROOT)) == 0 ||
strncmp(name, VDEV_TYPE_MIRROR, strlen(VDEV_TYPE_MIRROR)) == 0) strncmp(name, VDEV_TYPE_MIRROR, strlen(VDEV_TYPE_MIRROR)) == 0)
return (B_TRUE); return (B_TRUE);

View File

@ -1927,9 +1927,8 @@ for_each_vdev_cb(void *zhp, nvlist_t *nv, pool_vdev_iter_f func,
if (nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) != 0) if (nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) != 0)
return (ret); return (ret);
/* Don't run our function on root or indirect vdevs */ /* Don't run our function on indirect vdevs */
if ((strcmp(type, VDEV_TYPE_ROOT) != 0) && if (strcmp(type, VDEV_TYPE_INDIRECT) != 0) {
(strcmp(type, VDEV_TYPE_INDIRECT) != 0)) {
ret |= func(zhp, nv, data); ret |= func(zhp, nv, data);
} }

View File

@ -858,6 +858,22 @@ by user and group.
\*[instant-never] \*[instant-never]
\*[remount-upgrade] \*[remount-upgrade]
. .
.feature com.klarasystems vdev_zaps_v2 no
This feature creates a ZAP object for the root vdev.
.Pp
This feature becomes active after the next
.Nm zpool Cm import
or
.Nm zpool reguid .
.
Properties can be retrieved or set on the root vdev using
.Nm zpool Cm get
and
.Nm zpool Cm set
with
.Sy root
as the vdev name which is an alias for
.Sy root-0 .
.feature org.openzfs zilsaxattr yes extensible_dataset .feature org.openzfs zilsaxattr yes extensible_dataset
This feature enables This feature enables
.Sy xattr Ns = Ns Sy sa .Sy xattr Ns = Ns Sy sa

View File

@ -731,6 +731,12 @@ zpool_feature_init(void)
ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL, ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL,
sfeatures); sfeatures);
zfeature_register(SPA_FEATURE_AVZ_V2,
"com.klarasystems:vdev_zaps_v2", "vdev_zaps_v2",
"Support for root vdev ZAP.",
ZFEATURE_FLAG_MOS, ZFEATURE_TYPE_BOOLEAN, NULL,
sfeatures);
zfs_mod_list_supported_free(sfeatures); zfs_mod_list_supported_free(sfeatures);
} }

View File

@ -3044,6 +3044,12 @@ vdev_count_verify_zaps(vdev_t *vd)
spa_t *spa = vd->vdev_spa; spa_t *spa = vd->vdev_spa;
uint64_t total = 0; uint64_t total = 0;
if (spa_feature_is_active(vd->vdev_spa, SPA_FEATURE_AVZ_V2) &&
vd->vdev_root_zap != 0) {
total++;
ASSERT0(zap_lookup_int(spa->spa_meta_objset,
spa->spa_all_vdev_zaps, vd->vdev_root_zap));
}
if (vd->vdev_top_zap != 0) { if (vd->vdev_top_zap != 0) {
total++; total++;
ASSERT0(zap_lookup_int(spa->spa_meta_objset, ASSERT0(zap_lookup_int(spa->spa_meta_objset,
@ -8626,6 +8632,11 @@ spa_avz_build(vdev_t *vd, uint64_t avz, dmu_tx_t *tx)
{ {
spa_t *spa = vd->vdev_spa; spa_t *spa = vd->vdev_spa;
if (vd->vdev_root_zap != 0 &&
spa_feature_is_active(spa, SPA_FEATURE_AVZ_V2)) {
VERIFY0(zap_add_int(spa->spa_meta_objset, avz,
vd->vdev_root_zap, tx));
}
if (vd->vdev_top_zap != 0) { if (vd->vdev_top_zap != 0) {
VERIFY0(zap_add_int(spa->spa_meta_objset, avz, VERIFY0(zap_add_int(spa->spa_meta_objset, avz,
vd->vdev_top_zap, tx)); vd->vdev_top_zap, tx));

View File

@ -397,7 +397,9 @@ vdev_prop_get_int(vdev_t *vd, vdev_prop_t prop, uint64_t *value)
uint64_t objid; uint64_t objid;
int err; int err;
if (vd->vdev_top_zap != 0) { if (vd->vdev_root_zap != 0) {
objid = vd->vdev_root_zap;
} else if (vd->vdev_top_zap != 0) {
objid = vd->vdev_top_zap; objid = vd->vdev_top_zap;
} else if (vd->vdev_leaf_zap != 0) { } else if (vd->vdev_leaf_zap != 0) {
objid = vd->vdev_leaf_zap; objid = vd->vdev_leaf_zap;
@ -898,6 +900,14 @@ vdev_alloc(spa_t *spa, vdev_t **vdp, nvlist_t *nv, vdev_t *parent, uint_t id,
(void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_CREATE_TXG, (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_CREATE_TXG,
&vd->vdev_crtxg); &vd->vdev_crtxg);
if (vd->vdev_ops == &vdev_root_ops &&
(alloctype == VDEV_ALLOC_LOAD ||
alloctype == VDEV_ALLOC_SPLIT ||
alloctype == VDEV_ALLOC_ROOTPOOL)) {
(void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_VDEV_ROOT_ZAP,
&vd->vdev_root_zap);
}
/* /*
* If we're a top-level vdev, try to load the allocation parameters. * If we're a top-level vdev, try to load the allocation parameters.
*/ */
@ -3347,6 +3357,12 @@ vdev_construct_zaps(vdev_t *vd, dmu_tx_t *tx)
vdev_zap_allocation_data(vd, tx); vdev_zap_allocation_data(vd, tx);
} }
} }
if (vd->vdev_ops == &vdev_root_ops && vd->vdev_root_zap == 0 &&
spa_feature_is_enabled(vd->vdev_spa, SPA_FEATURE_AVZ_V2)) {
if (!spa_feature_is_active(vd->vdev_spa, SPA_FEATURE_AVZ_V2))
spa_feature_incr(vd->vdev_spa, SPA_FEATURE_AVZ_V2, tx);
vd->vdev_root_zap = vdev_create_link_zap(vd, tx);
}
for (uint64_t i = 0; i < vd->vdev_children; i++) { for (uint64_t i = 0; i < vd->vdev_children; i++) {
vdev_construct_zaps(vd->vdev_child[i], tx); vdev_construct_zaps(vd->vdev_child[i], tx);
@ -5683,12 +5699,17 @@ vdev_props_set_sync(void *arg, dmu_tx_t *tx)
/* /*
* Set vdev property values in the vdev props mos object. * Set vdev property values in the vdev props mos object.
*/ */
if (vd->vdev_top_zap != 0) { if (vd->vdev_root_zap != 0) {
objid = vd->vdev_root_zap;
} else if (vd->vdev_top_zap != 0) {
objid = vd->vdev_top_zap; objid = vd->vdev_top_zap;
} else if (vd->vdev_leaf_zap != 0) { } else if (vd->vdev_leaf_zap != 0) {
objid = vd->vdev_leaf_zap; objid = vd->vdev_leaf_zap;
} else { } else {
panic("vdev not top or leaf"); /*
* XXX: implement vdev_props_set_check()
*/
panic("vdev not root/top/leaf");
} }
switch (prop = vdev_name_to_prop(propname)) { switch (prop = vdev_name_to_prop(propname)) {
@ -5891,7 +5912,9 @@ vdev_prop_get(vdev_t *vd, nvlist_t *innvl, nvlist_t *outnvl)
nvlist_lookup_nvlist(innvl, ZPOOL_VDEV_PROPS_GET_PROPS, &nvprops); nvlist_lookup_nvlist(innvl, ZPOOL_VDEV_PROPS_GET_PROPS, &nvprops);
if (vd->vdev_top_zap != 0) { if (vd->vdev_root_zap != 0) {
objid = vd->vdev_root_zap;
} else if (vd->vdev_top_zap != 0) {
objid = vd->vdev_top_zap; objid = vd->vdev_top_zap;
} else if (vd->vdev_leaf_zap != 0) { } else if (vd->vdev_leaf_zap != 0) {
objid = vd->vdev_leaf_zap; objid = vd->vdev_leaf_zap;

View File

@ -573,6 +573,12 @@ vdev_config_generate(spa_t *spa, vdev_t *vd, boolean_t getstats,
vd->vdev_top_zap); vd->vdev_top_zap);
} }
if (vd->vdev_ops == &vdev_root_ops && vd->vdev_root_zap != 0 &&
spa_feature_is_active(vd->vdev_spa, SPA_FEATURE_AVZ_V2)) {
fnvlist_add_uint64(nv, ZPOOL_CONFIG_VDEV_ROOT_ZAP,
vd->vdev_root_zap);
}
if (vd->vdev_resilver_deferred) { if (vd->vdev_resilver_deferred) {
ASSERT(vd->vdev_ops->vdev_op_leaf); ASSERT(vd->vdev_ops->vdev_op_leaf);
ASSERT(spa->spa_resilver_deferred); ASSERT(spa->spa_resilver_deferred);

View File

@ -394,7 +394,7 @@ tags = ['functional', 'cli_root', 'zpool_export']
[tests/functional/cli_root/zpool_get] [tests/functional/cli_root/zpool_get]
tests = ['zpool_get_001_pos', 'zpool_get_002_pos', 'zpool_get_003_pos', tests = ['zpool_get_001_pos', 'zpool_get_002_pos', 'zpool_get_003_pos',
'zpool_get_004_neg', 'zpool_get_005_pos'] 'zpool_get_004_neg', 'zpool_get_005_pos', 'vdev_get_001_pos']
tags = ['functional', 'cli_root', 'zpool_get'] tags = ['functional', 'cli_root', 'zpool_get']
[tests/functional/cli_root/zpool_history] [tests/functional/cli_root/zpool_history]
@ -482,7 +482,7 @@ tags = ['functional', 'cli_root', 'zpool_scrub']
[tests/functional/cli_root/zpool_set] [tests/functional/cli_root/zpool_set]
tests = ['zpool_set_001_pos', 'zpool_set_002_neg', 'zpool_set_003_neg', tests = ['zpool_set_001_pos', 'zpool_set_002_neg', 'zpool_set_003_neg',
'zpool_set_ashift', 'zpool_set_features'] 'zpool_set_ashift', 'zpool_set_features', 'vdev_set_001_pos']
tags = ['functional', 'cli_root', 'zpool_set'] tags = ['functional', 'cli_root', 'zpool_set']
[tests/functional/cli_root/zpool_split] [tests/functional/cli_root/zpool_split]

View File

@ -178,6 +178,7 @@ nobase_dist_datadir_zfs_tests_tests_DATA += \
functional/cli_root/zpool_expand/zpool_expand.cfg \ functional/cli_root/zpool_expand/zpool_expand.cfg \
functional/cli_root/zpool_export/zpool_export.cfg \ functional/cli_root/zpool_export/zpool_export.cfg \
functional/cli_root/zpool_export/zpool_export.kshlib \ functional/cli_root/zpool_export/zpool_export.kshlib \
functional/cli_root/zpool_get/vdev_get.cfg \
functional/cli_root/zpool_get/zpool_get.cfg \ functional/cli_root/zpool_get/zpool_get.cfg \
functional/cli_root/zpool_get/zpool_get_parsable.cfg \ functional/cli_root/zpool_get/zpool_get_parsable.cfg \
functional/cli_root/zpool_import/blockfiles/cryptv0.dat.bz2 \ functional/cli_root/zpool_import/blockfiles/cryptv0.dat.bz2 \
@ -1032,6 +1033,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
functional/cli_root/zpool_export/zpool_export_004_pos.ksh \ functional/cli_root/zpool_export/zpool_export_004_pos.ksh \
functional/cli_root/zpool_get/cleanup.ksh \ functional/cli_root/zpool_get/cleanup.ksh \
functional/cli_root/zpool_get/setup.ksh \ functional/cli_root/zpool_get/setup.ksh \
functional/cli_root/zpool_get/vdev_get_001_pos.ksh \
functional/cli_root/zpool_get/zpool_get_001_pos.ksh \ functional/cli_root/zpool_get/zpool_get_001_pos.ksh \
functional/cli_root/zpool_get/zpool_get_002_pos.ksh \ functional/cli_root/zpool_get/zpool_get_002_pos.ksh \
functional/cli_root/zpool_get/zpool_get_003_pos.ksh \ functional/cli_root/zpool_get/zpool_get_003_pos.ksh \
@ -1146,6 +1148,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
functional/cli_root/zpool_set/cleanup.ksh \ functional/cli_root/zpool_set/cleanup.ksh \
functional/cli_root/zpool_set/setup.ksh \ functional/cli_root/zpool_set/setup.ksh \
functional/cli_root/zpool/setup.ksh \ functional/cli_root/zpool/setup.ksh \
functional/cli_root/zpool_set/vdev_set_001_pos.ksh \
functional/cli_root/zpool_set/zpool_set_001_pos.ksh \ functional/cli_root/zpool_set/zpool_set_001_pos.ksh \
functional/cli_root/zpool_set/zpool_set_002_neg.ksh \ functional/cli_root/zpool_set/zpool_set_002_neg.ksh \
functional/cli_root/zpool_set/zpool_set_003_neg.ksh \ functional/cli_root/zpool_set/zpool_set_003_neg.ksh \

View File

@ -0,0 +1,73 @@
#
# 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) 2022, Klara Inc.
#
# Set the expected properties of a vdev
typeset -a properties=(
capacity
state
guid
asize
psize
ashift
size
free
allocated
comment
expandsize
fragmentation
bootsize
parity
path
devid
physpath
encpath
fru
parent
children
numchildren
read_errors
write_errors
checksum_errors
initialize_errors
null_ops
read_ops
write_ops
free_ops
claim_ops
trim_ops
null_bytes
read_bytes
write_bytes
free_bytes
claim_bytes
trim_bytes
removing
allocating
failfast
checksum_n
checksum_t
io_n
io_t
)

View File

@ -0,0 +1,62 @@
#!/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) 2022, Klara Inc.
#
. $STF_SUITE/include/libtest.shlib
. $STF_SUITE/tests/functional/cli_root/zpool_get/vdev_get.cfg
#
# DESCRIPTION:
#
# zpool get <pool> root works as expected
#
# STRATEGY:
#
# 1. use zpool get to retrieve properties from root vdev
# 2. verify expected properties match detected properties
#
log_assert "zpool get all on root vdev"
EXPECT="$(zpool get -H all ${TESTPOOL} root | wc -l)"
if [ $? -ne 0 ]; then
log_fail "cannot retrieve properties from root vdev"
fi
i=0;
while [ $i -lt "${#properties[@]}" ]
do
log_must zpool get -H "${properties[$i]}" "$TESTPOOL" root
i=$(($i+1))
done
EXPECT=$((EXPECT))
if [ $i -gt $EXPECT ]; then
log_fail "found vdev properties not in vdev_get.cfg: $i/$EXPECT."
elif [ $i -lt $EXPECT ]; then
log_fail "expected properties not found in vdev_get.cfg: $i/$EXPECT."
fi
log_pass "zpool get all on root vdev"

View File

@ -104,5 +104,6 @@ if is_linux || is_freebsd; then
"feature@head_errlog" "feature@head_errlog"
"feature@blake3" "feature@blake3"
"feature@block_cloning" "feature@block_cloning"
"feature@vdev_zaps_v2"
) )
fi fi

View File

@ -0,0 +1,52 @@
#!/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) 2022, Klara Inc.
#
. $STF_SUITE/include/libtest.shlib
#
# DESCRIPTION:
#
# zpool set comment property on root vdev
#
# STRATEGY:
# 1. set a property on root vdev
# 2. verify the property is set
#
log_assert "zpool set comment property on root vdev"
log_must zpool set comment="openzfs" ${TESTPOOL} root
COMMENT="$(zpool get -H -o value comment ${TESTPOOL} root)"
if [ $? -ne 0 ]; then
log_fail "cant retrieve comment property from root vdev"
fi
if [ "$COMMENT" != "openzfs" ]; then
log_fail "unexpected value for comment property: $COMMENT != \"openzfs\""
fi
log_pass "zpool set comment property on root vdev"

View File

@ -34,6 +34,10 @@ function get_top_vd_zap # dsk conf
{ {
get_conf_section "$1" "$2" | awk '/com.delphix:vdev_zap_top: [0-9]+/ {print $2}' get_conf_section "$1" "$2" | awk '/com.delphix:vdev_zap_top: [0-9]+/ {print $2}'
} }
function get_root_vd_zap # conf
{
awk '/com.klarasystems:vdev_zap_root: [0-9]+/ {print $2}' "$1"
}
function assert_has_sentinel # conf function assert_has_sentinel # conf
{ {
@ -54,6 +58,15 @@ function assert_zap_common # pool vd lvl zapobj
fi fi
} }
function assert_root_zap # pool conf
{
typeset pool=$1
typeset conf=$2
root_zap=$(get_root_vd_zap $conf)
assert_zap_common $pool "root vdev" "root" $root_zap
}
function assert_top_zap # pool vd conf function assert_top_zap # pool vd conf
{ {
typeset pool=$1 typeset pool=$1

View File

@ -21,7 +21,7 @@
# #
# Strategy: # Strategy:
# 1. Create a pool with one disk. # 1. Create a pool with one disk.
# 2. Verify that the disk has a top and leaf ZAP in its config and the MOS. # 2. Verify that the disk has a root, top and leaf ZAP in its config and the MOS.
# #
. $STF_SUITE/include/libtest.shlib . $STF_SUITE/include/libtest.shlib
@ -35,6 +35,7 @@ log_must zpool create -f $TESTPOOL $DISK
conf="$TESTDIR/vz001" conf="$TESTDIR/vz001"
log_must eval "zdb -PC $TESTPOOL > $conf" log_must eval "zdb -PC $TESTPOOL > $conf"
assert_root_zap $TESTPOOL "$conf"
assert_top_zap $TESTPOOL $DISK "$conf" assert_top_zap $TESTPOOL $DISK "$conf"
assert_leaf_zap $TESTPOOL $DISK "$conf" assert_leaf_zap $TESTPOOL $DISK "$conf"
assert_has_sentinel "$conf" assert_has_sentinel "$conf"

View File

@ -36,6 +36,7 @@ conf="$TESTDIR/vz002"
log_must eval "zdb -PC $TESTPOOL > $conf" log_must eval "zdb -PC $TESTPOOL > $conf"
assert_has_sentinel "$conf" assert_has_sentinel "$conf"
assert_root_zap $TESTPOOL "$conf"
for DISK in $DISKS; do for DISK in $DISKS; do
assert_top_zap $TESTPOOL $DISK "$conf" assert_top_zap $TESTPOOL $DISK "$conf"
assert_leaf_zap $TESTPOOL $DISK "$conf" assert_leaf_zap $TESTPOOL $DISK "$conf"

View File

@ -37,6 +37,7 @@ conf="$TESTDIR/vz003"
log_must eval "zdb -PC $TESTPOOL > $conf" log_must eval "zdb -PC $TESTPOOL > $conf"
assert_has_sentinel "$conf" assert_has_sentinel "$conf"
assert_root_zap $TESTPOOL "$conf"
assert_top_zap $TESTPOOL "type: 'mirror'" "$conf" assert_top_zap $TESTPOOL "type: 'mirror'" "$conf"
for DISK in $DISKS; do for DISK in $DISKS; do
assert_leaf_zap $TESTPOOL $DISK "$conf" assert_leaf_zap $TESTPOOL $DISK "$conf"

View File

@ -40,6 +40,7 @@ log_must zpool create -f $TESTPOOL $DISK
conf="$TESTDIR/vz004" conf="$TESTDIR/vz004"
log_must eval "zdb -PC $TESTPOOL > $conf" log_must eval "zdb -PC $TESTPOOL > $conf"
assert_has_sentinel "$conf" assert_has_sentinel "$conf"
assert_root_zap $TESTPOOL "$conf"
orig_top=$(get_top_vd_zap $DISK $conf) orig_top=$(get_top_vd_zap $DISK $conf)
orig_leaf=$(get_leaf_vd_zap $DISK $conf) orig_leaf=$(get_leaf_vd_zap $DISK $conf)
assert_zap_common $TESTPOOL $DISK "top" $orig_top assert_zap_common $TESTPOOL $DISK "top" $orig_top

View File

@ -37,6 +37,7 @@ log_must zpool create -f $TESTPOOL $DISK
conf="$TESTDIR/vz005" conf="$TESTDIR/vz005"
log_must eval "zdb -PC $TESTPOOL > $conf" log_must eval "zdb -PC $TESTPOOL > $conf"
assert_has_sentinel "$conf" assert_has_sentinel "$conf"
assert_root_zap $TESTPOOL "$conf"
orig_top=$(get_top_vd_zap $DISK $conf) orig_top=$(get_top_vd_zap $DISK $conf)
orig_leaf=$(get_leaf_vd_zap $DISK $conf) orig_leaf=$(get_leaf_vd_zap $DISK $conf)
assert_zap_common $TESTPOOL $DISK "top" $orig_top assert_zap_common $TESTPOOL $DISK "top" $orig_top

View File

@ -39,6 +39,7 @@ conf="$TESTDIR/vz006"
log_must eval "zdb -PC $TESTPOOL > $conf" log_must eval "zdb -PC $TESTPOOL > $conf"
assert_has_sentinel "$conf" assert_has_sentinel "$conf"
assert_root_zap $TESTPOOL "$conf"
orig_top=$(get_top_vd_zap ${DISK_ARR[1]} $conf) orig_top=$(get_top_vd_zap ${DISK_ARR[1]} $conf)
assert_zap_common $TESTPOOL ${DISK_ARR[1]} "top" $orig_top assert_zap_common $TESTPOOL ${DISK_ARR[1]} "top" $orig_top
assert_leaf_zap $TESTPOOL ${DISK_ARR[1]} "$conf" assert_leaf_zap $TESTPOOL ${DISK_ARR[1]} "$conf"

View File

@ -39,6 +39,7 @@ conf="$TESTDIR/vz007"
log_must eval "zdb -PC $TESTPOOL > $conf" log_must eval "zdb -PC $TESTPOOL > $conf"
assert_has_sentinel "$conf" assert_has_sentinel "$conf"
assert_root_zap $TESTPOOL "$conf"
orig_top=$(get_top_vd_zap "type: 'mirror'" $conf) orig_top=$(get_top_vd_zap "type: 'mirror'" $conf)
orig_leaf0=$(get_leaf_vd_zap ${DISK_ARR[0]} $conf) orig_leaf0=$(get_leaf_vd_zap ${DISK_ARR[0]} $conf)
orig_leaf1=$(get_leaf_vd_zap ${DISK_ARR[1]} $conf) orig_leaf1=$(get_leaf_vd_zap ${DISK_ARR[1]} $conf)