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.
*