diff --git a/cmd/zpool/zpool_main.c b/cmd/zpool/zpool_main.c index 162aabdbc857..6b005562d79a 100644 --- a/cmd/zpool/zpool_main.c +++ b/cmd/zpool/zpool_main.c @@ -1148,11 +1148,16 @@ fill_vdev_info(nvlist_t *list, zpool_handle_t *zhp, char *name, vdev_stat_t *vs; uint_t c; nvlist_t *nvdev; + nvlist_t *nvdev_parent = NULL; + char *_name; if (strcmp(name, zpool_get_name(zhp)) != 0) - nvdev = zpool_find_vdev(zhp, name, NULL, &l2c, NULL); + _name = name; else - nvdev = zpool_find_vdev(zhp, "root-0", NULL, &l2c, NULL); + _name = (char *)"root-0"; + + nvdev = zpool_find_vdev(zhp, _name, NULL, &l2c, NULL); + fnvlist_add_string(list, "name", name); if (addtype) fnvlist_add_string(list, "type", "VDEV"); @@ -1197,8 +1202,32 @@ fill_vdev_info(nvlist_t *list, zpool_handle_t *zhp, char *name, ZPOOL_CONFIG_ALLOCATION_BIAS, &bias); if (bias != NULL) fnvlist_add_string(list, "class", bias); - else - fnvlist_add_string(list, "class", "normal"); + else { + nvdev_parent = NULL; + nvdev_parent = zpool_find_parent_vdev(zhp, + _name, NULL, NULL, NULL); + + /* + * With a mirrored special device, the parent + * "mirror" vdev will have + * ZPOOL_CONFIG_ALLOCATION_BIAS set to "special" + * not the leaf vdevs. If we're a leaf vdev + * in that case we need to look at our parent + * to see if they're "special" to know if we + * are "special" too. + */ + if (nvdev_parent) { + (void) nvlist_lookup_string( + nvdev_parent, + ZPOOL_CONFIG_ALLOCATION_BIAS, + &bias); + } + if (bias != NULL) + fnvlist_add_string(list, "class", bias); + else + fnvlist_add_string(list, "class", + "normal"); + } } if (nvlist_lookup_uint64_array(nvdev, ZPOOL_CONFIG_VDEV_STATS, (uint64_t **)&vs, &c) == 0) { diff --git a/include/libzfs.h b/include/libzfs.h index 8b12907077ff..3d99a4c62423 100644 --- a/include/libzfs.h +++ b/include/libzfs.h @@ -327,6 +327,8 @@ _LIBZFS_H int zpool_vdev_clear(zpool_handle_t *, uint64_t); _LIBZFS_H nvlist_t *zpool_find_vdev(zpool_handle_t *, const char *, boolean_t *, boolean_t *, boolean_t *); +_LIBZFS_H nvlist_t *zpool_find_parent_vdev(zpool_handle_t *, const char *, + boolean_t *, boolean_t *, boolean_t *); _LIBZFS_H nvlist_t *zpool_find_vdev_by_physpath(zpool_handle_t *, const char *, boolean_t *, boolean_t *, boolean_t *); _LIBZFS_H int zpool_label_disk(libzfs_handle_t *, zpool_handle_t *, diff --git a/lib/libzfs/libzfs.abi b/lib/libzfs/libzfs.abi index 6bc3b660062b..ba61ede3c83c 100644 --- a/lib/libzfs/libzfs.abi +++ b/lib/libzfs/libzfs.abi @@ -485,6 +485,7 @@ + @@ -6500,6 +6501,14 @@ + + + + + + + + diff --git a/lib/libzfs/libzfs_pool.c b/lib/libzfs/libzfs_pool.c index 7a2899f445a6..82b94fe29ee2 100644 --- a/lib/libzfs/libzfs_pool.c +++ b/lib/libzfs/libzfs_pool.c @@ -2805,10 +2805,13 @@ zpool_scan(zpool_handle_t *zhp, pool_scan_func_t func, pool_scrub_cmd_t cmd) * the nvpair name to determine how we should look for the device. * 'avail_spare' is set to TRUE if the provided guid refers to an AVAIL * spare; but FALSE if its an INUSE spare. + * + * If 'return_parent' is set, then return the *parent* of the vdev you're + * searching for rather than the vdev itself. */ static nvlist_t * vdev_to_nvlist_iter(nvlist_t *nv, nvlist_t *search, boolean_t *avail_spare, - boolean_t *l2cache, boolean_t *log) + boolean_t *l2cache, boolean_t *log, boolean_t return_parent) { uint_t c, children; nvlist_t **child; @@ -2816,6 +2819,8 @@ vdev_to_nvlist_iter(nvlist_t *nv, nvlist_t *search, boolean_t *avail_spare, uint64_t is_log; const char *srchkey; nvpair_t *pair = nvlist_next_nvpair(search, NULL); + const char *tmp = NULL; + boolean_t is_root; /* Nothing to look for */ if (search == NULL || pair == NULL) @@ -2824,6 +2829,12 @@ vdev_to_nvlist_iter(nvlist_t *nv, nvlist_t *search, boolean_t *avail_spare, /* Obtain the key we will use to search */ srchkey = nvpair_name(pair); + nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &tmp); + if (strcmp(tmp, "root") == 0) + is_root = B_TRUE; + else + is_root = B_FALSE; + switch (nvpair_type(pair)) { case DATA_TYPE_UINT64: if (strcmp(srchkey, ZPOOL_CONFIG_GUID) == 0) { @@ -2954,7 +2965,7 @@ vdev_to_nvlist_iter(nvlist_t *nv, nvlist_t *search, boolean_t *avail_spare, for (c = 0; c < children; c++) { if ((ret = vdev_to_nvlist_iter(child[c], search, - avail_spare, l2cache, NULL)) != NULL) { + avail_spare, l2cache, NULL, return_parent)) != NULL) { /* * The 'is_log' value is only set for the toplevel * vdev, not the leaf vdevs. So we always lookup the @@ -2967,7 +2978,7 @@ vdev_to_nvlist_iter(nvlist_t *nv, nvlist_t *search, boolean_t *avail_spare, is_log) { *log = B_TRUE; } - return (ret); + return (ret && return_parent && !is_root ? nv : ret); } } @@ -2975,9 +2986,11 @@ vdev_to_nvlist_iter(nvlist_t *nv, nvlist_t *search, boolean_t *avail_spare, &child, &children) == 0) { for (c = 0; c < children; c++) { if ((ret = vdev_to_nvlist_iter(child[c], search, - avail_spare, l2cache, NULL)) != NULL) { + avail_spare, l2cache, NULL, return_parent)) + != NULL) { *avail_spare = B_TRUE; - return (ret); + return (ret && return_parent && + !is_root ? nv : ret); } } } @@ -2986,9 +2999,11 @@ vdev_to_nvlist_iter(nvlist_t *nv, nvlist_t *search, boolean_t *avail_spare, &child, &children) == 0) { for (c = 0; c < children; c++) { if ((ret = vdev_to_nvlist_iter(child[c], search, - avail_spare, l2cache, NULL)) != NULL) { + avail_spare, l2cache, NULL, return_parent)) + != NULL) { *l2cache = B_TRUE; - return (ret); + return (ret && return_parent && + !is_root ? nv : ret); } } } @@ -3023,7 +3038,8 @@ zpool_find_vdev_by_physpath(zpool_handle_t *zhp, const char *ppath, *l2cache = B_FALSE; if (log != NULL) *log = B_FALSE; - ret = vdev_to_nvlist_iter(nvroot, search, avail_spare, l2cache, log); + ret = vdev_to_nvlist_iter(nvroot, search, avail_spare, l2cache, log, + B_FALSE); fnvlist_free(search); return (ret); @@ -3051,11 +3067,12 @@ zpool_vdev_is_interior(const char *name) } /* - * Lookup the nvlist for a given vdev. + * Lookup the nvlist for a given vdev or vdev's parent (depending on + * if 'return_parent' is set). */ -nvlist_t * -zpool_find_vdev(zpool_handle_t *zhp, const char *path, boolean_t *avail_spare, - boolean_t *l2cache, boolean_t *log) +static nvlist_t * +__zpool_find_vdev(zpool_handle_t *zhp, const char *path, boolean_t *avail_spare, + boolean_t *l2cache, boolean_t *log, boolean_t return_parent) { char *end; nvlist_t *nvroot, *search, *ret; @@ -3092,12 +3109,30 @@ zpool_find_vdev(zpool_handle_t *zhp, const char *path, boolean_t *avail_spare, *l2cache = B_FALSE; if (log != NULL) *log = B_FALSE; - ret = vdev_to_nvlist_iter(nvroot, search, avail_spare, l2cache, log); + ret = vdev_to_nvlist_iter(nvroot, search, avail_spare, l2cache, log, + return_parent); fnvlist_free(search); return (ret); } +nvlist_t * +zpool_find_vdev(zpool_handle_t *zhp, const char *path, boolean_t *avail_spare, + boolean_t *l2cache, boolean_t *log) +{ + return (__zpool_find_vdev(zhp, path, avail_spare, l2cache, log, + B_FALSE)); +} + +/* Given a vdev path, return its parent's nvlist */ +nvlist_t * +zpool_find_parent_vdev(zpool_handle_t *zhp, const char *path, + boolean_t *avail_spare, boolean_t *l2cache, boolean_t *log) +{ + return (__zpool_find_vdev(zhp, path, avail_spare, l2cache, log, + B_TRUE)); +} + /* * Convert a vdev path to a GUID. Returns GUID or 0 on error. *