summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cmd/zfs/zfs_main.c4
-rw-r--r--cmd/zpool/zpool_iter.c33
-rw-r--r--cmd/zpool/zpool_main.c32
-rw-r--r--cmd/zpool/zpool_util.h12
-rw-r--r--module/zfs/vdev_raidz_math.c2
-rw-r--r--module/zfs/vdev_raidz_math_avx512bw.c190
-rw-r--r--module/zfs/zfs_vfsops.c19
-rw-r--r--tests/runfiles/linux.run3
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_user/zpool_iostat/zpool_iostat_005_pos.ksh23
9 files changed, 174 insertions, 144 deletions
diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c
index 7379af280..6e818afcb 100644
--- a/cmd/zfs/zfs_main.c
+++ b/cmd/zfs/zfs_main.c
@@ -2529,7 +2529,7 @@ userspace_cb(void *arg, const char *domain, uid_t rid, uint64_t space)
namelen = strlen(name);
}
nameidx = us_field_index("name");
- if (namelen > cb->cb_width[nameidx])
+ if (nameidx >= 0 && namelen > cb->cb_width[nameidx])
cb->cb_width[nameidx] = namelen;
/*
@@ -2574,7 +2574,7 @@ userspace_cb(void *arg, const char *domain, uid_t rid, uint64_t space)
return (-1);
}
sizeidx = us_field_index(propname);
- if (sizelen > cb->cb_width[sizeidx])
+ if (sizeidx >= 0 && sizelen > cb->cb_width[sizeidx])
cb->cb_width[sizeidx] = sizelen;
if (nvlist_add_uint64(props, propname, space) != 0)
diff --git a/cmd/zpool/zpool_iter.c b/cmd/zpool/zpool_iter.c
index 1d376d4fb..309247b6b 100644
--- a/cmd/zpool/zpool_iter.c
+++ b/cmd/zpool/zpool_iter.c
@@ -360,7 +360,8 @@ for_each_vdev_run_cb(zpool_handle_t *zhp, nvlist_t *nv, void *cb_vcdl)
vdev_cmd_data_list_t *vcdl = cb_vcdl;
vdev_cmd_data_t *data;
char *path = NULL;
- int i;
+ char *vname = NULL;
+ int i, match = 0;
if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) != 0)
return (1);
@@ -374,6 +375,21 @@ for_each_vdev_run_cb(zpool_handle_t *zhp, nvlist_t *nv, void *cb_vcdl)
}
}
+ /* Check for whitelisted vdevs here, if any */
+ for (i = 0; i < vcdl->vdev_names_count; i++) {
+ vname = zpool_vdev_name(g_zfs, zhp, nv, vcdl->cb_name_flags);
+ if (strcmp(vcdl->vdev_names[i], vname) == 0) {
+ free(vname);
+ match = 1;
+ break; /* match */
+ }
+ free(vname);
+ }
+
+ /* If we whitelisted vdevs, and this isn't one of them, then bail out */
+ if (!match && vcdl->vdev_names_count)
+ return (0);
+
/*
* Resize our array and add in the new element.
*/
@@ -437,19 +453,28 @@ all_pools_for_each_vdev_run_vcdl(vdev_cmd_data_list_t *vcdl)
}
/*
- * Run command 'cmd' on all vdevs in all pools. Saves the first line of output
- * from the command in vcdk->data[].line for all vdevs.
+ * Run command 'cmd' on all vdevs in all pools in argv. Saves the first line of
+ * output from the command in vcdk->data[].line for all vdevs. If you want
+ * to run the command on only certain vdevs, fill in g_zfs, vdev_names,
+ * vdev_names_count, and cb_name_flags. Otherwise leave them as zero.
*
* Returns a vdev_cmd_data_list_t that must be freed with
* free_vdev_cmd_data_list();
*/
vdev_cmd_data_list_t *
-all_pools_for_each_vdev_run(int argc, char **argv, char *cmd)
+all_pools_for_each_vdev_run(int argc, char **argv, char *cmd,
+ libzfs_handle_t *g_zfs, char **vdev_names, int vdev_names_count,
+ int cb_name_flags)
{
vdev_cmd_data_list_t *vcdl;
vcdl = safe_malloc(sizeof (vdev_cmd_data_list_t));
vcdl->cmd = cmd;
+ vcdl->vdev_names = vdev_names;
+ vcdl->vdev_names_count = vdev_names_count;
+ vcdl->cb_name_flags = cb_name_flags;
+ vcdl->g_zfs = g_zfs;
+
/* Gather our list of all vdevs in all pools */
for_each_pool(argc, argv, B_TRUE, NULL,
all_pools_for_each_vdev_gather_cb, vcdl);
diff --git a/cmd/zpool/zpool_main.c b/cmd/zpool/zpool_main.c
index a8233e336..f5740c00e 100644
--- a/cmd/zpool/zpool_main.c
+++ b/cmd/zpool/zpool_main.c
@@ -312,7 +312,7 @@ get_usage(zpool_help_t idx) {
"[-R root] [-F [-n]]\n"
"\t <pool | id> [newpool]\n"));
case HELP_IOSTAT:
- return (gettext("\tiostat [-T d | u] [-ghHLpPvy] "
+ return (gettext("\tiostat [-c CMD] [-T d | u] [-ghHLpPvy] "
"[[-lq]|[-r|-w]]\n"
"\t [[pool ...]|[pool vdev ...]|[vdev ...]] "
"[interval [count]]\n"));
@@ -335,8 +335,8 @@ get_usage(zpool_help_t idx) {
case HELP_SCRUB:
return (gettext("\tscrub [-s] <pool> ...\n"));
case HELP_STATUS:
- return (gettext("\tstatus [-gLPvxD] [-T d|u] [pool] ... "
- "[interval [count]]\n"));
+ return (gettext("\tstatus [-c CMD] [-gLPvxD] [-T d|u] [pool]"
+ " ... [interval [count]]\n"));
case HELP_UPGRADE:
return (gettext("\tupgrade\n"
"\tupgrade -v\n"
@@ -4055,8 +4055,13 @@ zpool_do_iostat(int argc, char **argv)
usage(B_FALSE);
break;
case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
+ if (optopt == 'c') {
+ fprintf(stderr,
+ gettext("Missing CMD for -c\n"));
+ } else {
+ fprintf(stderr,
+ gettext("invalid option '%c'\n"), optopt);
+ }
usage(B_FALSE);
}
}
@@ -4256,9 +4261,10 @@ zpool_do_iostat(int argc, char **argv)
continue;
}
- if (cmd != NULL)
+ if (cmd != NULL && cb.cb_verbose)
cb.vcdl = all_pools_for_each_vdev_run(argc,
- argv, cmd);
+ argv, cmd, g_zfs, cb.cb_vdev_names,
+ cb.cb_vdev_names_count, cb.cb_name_flags);
pool_list_iter(list, B_FALSE, print_iostat, &cb);
@@ -6113,8 +6119,13 @@ zpool_do_status(int argc, char **argv)
get_timestamp_arg(*optarg);
break;
case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
+ if (optopt == 'c') {
+ fprintf(stderr,
+ gettext("Missing CMD for -c\n"));
+ } else {
+ fprintf(stderr,
+ gettext("invalid option '%c'\n"), optopt);
+ }
usage(B_FALSE);
}
}
@@ -6135,7 +6146,8 @@ zpool_do_status(int argc, char **argv)
print_timestamp(timestamp_fmt);
if (cmd != NULL)
- cb.vcdl = all_pools_for_each_vdev_run(argc, argv, cmd);
+ cb.vcdl = all_pools_for_each_vdev_run(argc, argv, cmd,
+ NULL, NULL, 0, 0);
ret = for_each_pool(argc, argv, B_TRUE, NULL,
status_callback, &cb);
diff --git a/cmd/zpool/zpool_util.h b/cmd/zpool/zpool_util.h
index 0d83b54d5..acdfd5075 100644
--- a/cmd/zpool/zpool_util.h
+++ b/cmd/zpool/zpool_util.h
@@ -86,11 +86,21 @@ typedef struct vdev_cmd_data_list
{
char *cmd; /* Command to run */
unsigned int count; /* Number of vdev_cmd_data items (vdevs) */
+
+ /* vars to whitelist only certain vdevs, if requested */
+ libzfs_handle_t *g_zfs;
+ char **vdev_names;
+ int vdev_names_count;
+ int cb_name_flags;
+
vdev_cmd_data_t *data; /* Array of vdevs */
+
} vdev_cmd_data_list_t;
vdev_cmd_data_list_t *all_pools_for_each_vdev_run(int argc, char **argv,
- char *cmd);
+ char *cmd, libzfs_handle_t *g_zfs, char **vdev_names, int vdev_names_count,
+ int cb_name_flags);
+
void free_vdev_cmd_data_list(vdev_cmd_data_list_t *vcdl);
#ifdef __cplusplus
diff --git a/module/zfs/vdev_raidz_math.c b/module/zfs/vdev_raidz_math.c
index 85dd15cc3..a175bcf77 100644
--- a/module/zfs/vdev_raidz_math.c
+++ b/module/zfs/vdev_raidz_math.c
@@ -61,7 +61,7 @@ const raidz_impl_ops_t *raidz_all_maths[] = {
&vdev_raidz_avx512f_impl,
#endif
#if defined(__x86_64) && defined(HAVE_AVX512BW) /* only x86_64 for now */
- // &vdev_raidz_avx512bw_impl,
+ &vdev_raidz_avx512bw_impl,
#endif
#if defined(__aarch64__)
&vdev_raidz_aarch64_neon_impl,
diff --git a/module/zfs/vdev_raidz_math_avx512bw.c b/module/zfs/vdev_raidz_math_avx512bw.c
index 33b2d388f..3d5326b9e 100644
--- a/module/zfs/vdev_raidz_math_avx512bw.c
+++ b/module/zfs/vdev_raidz_math_avx512bw.c
@@ -20,11 +20,12 @@
*/
/*
* Copyright (C) 2016 Romain Dolbeau. All rights reserved.
+ * Copyright (C) 2016 Gvozden Nešković. All rights reserved.
*/
#include <sys/isa_defs.h>
-#if 0 // defined(__x86_64) && defined(HAVE_AVX512BW)
+#if defined(__x86_64) && defined(HAVE_AVX512BW)
#include <sys/types.h>
#include <linux/simd_x86.h>
@@ -66,20 +67,6 @@ typedef struct v {
uint8_t b[ELEM_SIZE] __attribute__((aligned(ELEM_SIZE)));
} v_t;
-#define PREFETCHNTA(ptr, offset) \
-{ \
- __asm( \
- "prefetchnta " #offset "(%[MEM])\n" \
- : : [MEM] "r" (ptr)); \
-}
-
-#define PREFETCH(ptr, offset) \
-{ \
- __asm( \
- "prefetcht0 " #offset "(%[MEM])\n" \
- : : [MEM] "r" (ptr)); \
-}
-
#define XOR_ACC(src, r...) \
{ \
switch (REG_CNT(r)) { \
@@ -122,25 +109,7 @@ typedef struct v {
} \
}
-#define ZERO(r...) \
-{ \
- switch (REG_CNT(r)) { \
- case 4: \
- __asm( \
- "vpxorq %" VR0(r) ", %" VR0(r)", %" VR0(r) "\n" \
- "vpxorq %" VR1(r) ", %" VR1(r)", %" VR1(r) "\n" \
- "vpxorq %" VR2(r) ", %" VR2(r)", %" VR2(r) "\n" \
- "vpxorq %" VR3(r) ", %" VR3(r)", %" VR3(r)); \
- break; \
- case 2: \
- __asm( \
- "vpxorq %" VR0(r) ", %" VR0(r)", %" VR0(r) "\n" \
- "vpxorq %" VR1(r) ", %" VR1(r)", %" VR1(r)); \
- break; \
- default: \
- ASM_BUG(); \
- } \
-}
+#define ZERO(r...) XOR(r, r)
#define COPY(r...) \
{ \
@@ -206,20 +175,11 @@ typedef struct v {
} \
}
-#define FLUSH() \
-{ \
- __asm("vzeroupper"); \
-}
-
-#define MUL2_SETUP() \
-{ \
- __asm("vmovq %0, %%xmm14" :: "r"(0x1d1d1d1d1d1d1d1d)); \
- __asm("vpbroadcastq %xmm14, %zmm14"); \
- __asm("vmovq %0, %%xmm13" :: "r"(0x8080808080808080)); \
- __asm("vpbroadcastq %xmm13, %zmm13"); \
- __asm("vmovq %0, %%xmm12" :: "r"(0xfefefefefefefefe)); \
- __asm("vpbroadcastq %xmm12, %zmm12"); \
- __asm("vpxorq %zmm15, %zmm15 ,%zmm15"); \
+#define MUL2_SETUP() \
+{ \
+ __asm("vmovq %0, %%xmm22" :: "r"(0x1d1d1d1d1d1d1d1d)); \
+ __asm("vpbroadcastq %xmm22, %zmm22"); \
+ __asm("vpxord %zmm23, %zmm23 ,%zmm23"); \
}
#define _MUL2(r...) \
@@ -227,20 +187,14 @@ typedef struct v {
switch (REG_CNT(r)) { \
case 2: \
__asm( \
- "vpandq %" VR0(r)", %zmm13, %zmm10\n" \
- "vpandq %" VR1(r)", %zmm13, %zmm11\n" \
- "vpsrlq $7, %zmm10, %zmm8\n" \
- "vpsrlq $7, %zmm11, %zmm9\n" \
- "vpsllq $1, %zmm10, %zmm10\n" \
- "vpsllq $1, %zmm11, %zmm11\n" \
- "vpsubq %zmm8, %zmm10, %zmm10\n" \
- "vpsubq %zmm9, %zmm11, %zmm11\n" \
- "vpsllq $1, %" VR0(r)", %" VR0(r) "\n" \
- "vpsllq $1, %" VR1(r)", %" VR1(r) "\n" \
- "vpandq %zmm10, %zmm14, %zmm10\n" \
- "vpandq %zmm11, %zmm14, %zmm11\n" \
- "vpternlogd $0x6c,%zmm12, %zmm10, %" VR0(r) "\n" \
- "vpternlogd $0x6c,%zmm12, %zmm11, %" VR1(r)); \
+ "vpcmpb $1, %zmm23, %" VR0(r)", %k1\n" \
+ "vpcmpb $1, %zmm23, %" VR1(r)", %k2\n" \
+ "vpaddb %" VR0(r)", %" VR0(r)", %" VR0(r) "\n" \
+ "vpaddb %" VR1(r)", %" VR1(r)", %" VR1(r) "\n" \
+ "vpxord %zmm22, %" VR0(r)", %zmm12\n" \
+ "vpxord %zmm22, %" VR1(r)", %zmm13\n" \
+ "vmovdqu8 %zmm12, %" VR0(r) "{%k1}\n" \
+ "vmovdqu8 %zmm13, %" VR1(r) "{%k2}"); \
break; \
default: \
ASM_BUG(); \
@@ -276,7 +230,7 @@ typedef struct v {
#define _ta "zmm10"
#define _tb "zmm15"
-static const uint8_t __attribute__((aligned(32))) _mul_mask = 0x0F;
+static const uint8_t __attribute__((aligned(64))) _mul_mask = 0x0F;
#define _MULx2(c, r...) \
{ \
@@ -339,11 +293,15 @@ static const uint8_t __attribute__((aligned(32))) _mul_mask = 0x0F;
}
#define raidz_math_begin() kfpu_begin()
-#define raidz_math_end() \
-{ \
- FLUSH(); \
- kfpu_end(); \
-}
+#define raidz_math_end() kfpu_end()
+
+/*
+ * ZERO, COPY, and MUL operations are already 2x unrolled, which means that
+ * the stride of these operations for avx512 must not exceed 4. Otherwise, a
+ * single step would exceed 512B block size.
+ */
+
+#define SYN_STRIDE 4
#define ZERO_STRIDE 4
#define ZERO_DEFINE() {}
@@ -361,59 +319,67 @@ static const uint8_t __attribute__((aligned(32))) _mul_mask = 0x0F;
#define MUL_DEFINE() {}
#define MUL_D 0, 1, 2, 3
-#define GEN_P_DEFINE() {}
#define GEN_P_STRIDE 4
+#define GEN_P_DEFINE() {}
#define GEN_P_P 0, 1, 2, 3
-#define GEN_PQ_DEFINE() {}
#define GEN_PQ_STRIDE 4
+#define GEN_PQ_DEFINE() {}
#define GEN_PQ_D 0, 1, 2, 3
-#define GEN_PQ_P 4, 5, 6, 7
-#define GEN_PQ_Q 20, 21, 22, 23
-
-#define GEN_PQR_DEFINE() {}
-#define GEN_PQR_STRIDE 2
-#define GEN_PQR_D 0, 1
-#define GEN_PQR_P 2, 3
-#define GEN_PQR_Q 4, 5
-#define GEN_PQR_R 6, 7
-
-#define REC_P_DEFINE() {}
-#define REC_P_STRIDE 4
-#define REC_P_X 0, 1, 2, 3
-
-#define REC_Q_DEFINE() {}
-#define REC_Q_STRIDE 4
-#define REC_Q_X 0, 1, 2, 3
-
-#define REC_R_DEFINE() {}
-#define REC_R_STRIDE 4
-#define REC_R_X 0, 1, 2, 3
-
-#define REC_PQ_DEFINE() {}
-#define REC_PQ_STRIDE 4
-#define REC_PQ_X 0, 1, 2, 3
-#define REC_PQ_Y 4, 5, 6, 7
-#define REC_PQ_D 20, 21, 22, 23
-
-#define REC_PR_DEFINE() {}
-#define REC_PR_STRIDE 4
-#define REC_PR_X 0, 1, 2, 3
-#define REC_PR_Y 4, 5, 6, 7
-#define REC_PR_D 20, 21, 22, 23
-
-#define REC_QR_DEFINE() {}
-#define REC_QR_STRIDE 4
-#define REC_QR_X 0, 1, 2, 3
-#define REC_QR_Y 4, 5, 6, 7
-#define REC_QR_D 20, 21, 22, 23
-
-#define REC_PQR_DEFINE() {}
+#define GEN_PQ_C 4, 5, 6, 7
+
+#define GEN_PQR_STRIDE 4
+#define GEN_PQR_DEFINE() {}
+#define GEN_PQR_D 0, 1, 2, 3
+#define GEN_PQR_C 4, 5, 6, 7
+
+#define SYN_Q_DEFINE() {}
+#define SYN_Q_D 0, 1, 2, 3
+#define SYN_Q_X 4, 5, 6, 7
+
+#define SYN_R_DEFINE() {}
+#define SYN_R_D 0, 1, 2, 3
+#define SYN_R_X 4, 5, 6, 7
+
+#define SYN_PQ_DEFINE() {}
+#define SYN_PQ_D 0, 1, 2, 3
+#define SYN_PQ_X 4, 5, 6, 7
+
+#define REC_PQ_STRIDE 2
+#define REC_PQ_DEFINE() {}
+#define REC_PQ_X 0, 1
+#define REC_PQ_Y 2, 3
+#define REC_PQ_T 4, 5
+
+#define SYN_PR_DEFINE() {}
+#define SYN_PR_D 0, 1, 2, 3
+#define SYN_PR_X 4, 5, 6, 7
+
+#define REC_PR_STRIDE 2
+#define REC_PR_DEFINE() {}
+#define REC_PR_X 0, 1
+#define REC_PR_Y 2, 3
+#define REC_PR_T 4, 5
+
+#define SYN_QR_DEFINE() {}
+#define SYN_QR_D 0, 1, 2, 3
+#define SYN_QR_X 4, 5, 6, 7
+
+#define REC_QR_STRIDE 2
+#define REC_QR_DEFINE() {}
+#define REC_QR_X 0, 1
+#define REC_QR_Y 2, 3
+#define REC_QR_T 4, 5
+
+#define SYN_PQR_DEFINE() {}
+#define SYN_PQR_D 0, 1, 2, 3
+#define SYN_PQR_X 4, 5, 6, 7
+
#define REC_PQR_STRIDE 2
+#define REC_PQR_DEFINE() {}
#define REC_PQR_X 0, 1
#define REC_PQR_Y 2, 3
#define REC_PQR_Z 4, 5
-#define REC_PQR_D 6, 7
#define REC_PQR_XS 6, 7
#define REC_PQR_YS 8, 9
diff --git a/module/zfs/zfs_vfsops.c b/module/zfs/zfs_vfsops.c
index 1c3dccdca..3c76cfe6f 100644
--- a/module/zfs/zfs_vfsops.c
+++ b/module/zfs/zfs_vfsops.c
@@ -744,19 +744,17 @@ zfs_sb_create(const char *osname, zfs_mntopts_t *zmo, zfs_sb_t **zsbp)
zsb = kmem_zalloc(sizeof (zfs_sb_t), KM_SLEEP);
/*
- * We claim to always be readonly so we can open snapshots;
- * other ZPL code will prevent us from writing to snapshots.
+ * Optional temporary mount options, free'd in zfs_sb_free().
*/
- error = dmu_objset_own(osname, DMU_OST_ZFS, B_TRUE, zsb, &os);
- if (error) {
- kmem_free(zsb, sizeof (zfs_sb_t));
- return (error);
- }
+ zsb->z_mntopts = (zmo ? zmo : zfs_mntopts_alloc());
/*
- * Optional temporary mount options, free'd in zfs_sb_free().
+ * We claim to always be readonly so we can open snapshots;
+ * other ZPL code will prevent us from writing to snapshots.
*/
- zsb->z_mntopts = (zmo ? zmo : zfs_mntopts_alloc());
+ error = dmu_objset_own(osname, DMU_OST_ZFS, B_TRUE, zsb, &os);
+ if (error)
+ goto out_zmo;
/*
* Initialize the zfs-specific filesystem structure.
@@ -896,8 +894,9 @@ zfs_sb_create(const char *osname, zfs_mntopts_t *zmo, zfs_sb_t **zsbp)
out:
dmu_objset_disown(os, zsb);
+out_zmo:
*zsbp = NULL;
-
+ zfs_mntopts_free(zsb->z_mntopts);
kmem_free(zsb, sizeof (zfs_sb_t));
return (error);
}
diff --git a/tests/runfiles/linux.run b/tests/runfiles/linux.run
index 8a9785992..5e16395b9 100644
--- a/tests/runfiles/linux.run
+++ b/tests/runfiles/linux.run
@@ -365,7 +365,8 @@ user =
[tests/functional/cli_user/zpool_iostat]
tests = ['zpool_iostat_001_neg', 'zpool_iostat_002_pos',
- 'zpool_iostat_003_neg', 'zpool_iostat_004_pos']
+ 'zpool_iostat_003_neg', 'zpool_iostat_004_pos',
+ 'zpool_iostat_005_pos']
user =
[tests/functional/cli_user/zpool_list]
diff --git a/tests/zfs-tests/tests/functional/cli_user/zpool_iostat/zpool_iostat_005_pos.ksh b/tests/zfs-tests/tests/functional/cli_user/zpool_iostat/zpool_iostat_005_pos.ksh
index be38933c1..89a9a4975 100755
--- a/tests/zfs-tests/tests/functional/cli_user/zpool_iostat/zpool_iostat_005_pos.ksh
+++ b/tests/zfs-tests/tests/functional/cli_user/zpool_iostat/zpool_iostat_005_pos.ksh
@@ -54,10 +54,27 @@ fi
# a '/' when we specify the path (-P) flag. We check for "{}" to see if one
# of the VDEV variables isn't set.
#
-C1=$($ZPOOL iostat -Pv | $GREP -E '^\s+/' | $WC -l)
-C2=$($ZPOOL iostat -Pv -c 'echo vdev_test{$VDEV_PATH}{$VDEV_UPATH}' | $GREP -E '^\s+/' | $GREP -v '{}' | $WC -l)
+C1=$($ZPOOL iostat -Pv $testpool | $GREP -E '^\s+/' | $WC -l)
+C2=$($ZPOOL iostat -Pv -c 'echo vdev_test{$VDEV_PATH}{$VDEV_UPATH}' $testpool \
+ | $GREP -E '^\s+/' | $GREP -v '{}' | $WC -l)
if [ "$C1" != "$C2" ] ; then
log_fail "zpool iostat -c failed, expected $C1 vdevs, got $C2"
else
- log_pass "zpool iostat -c passed, expected $C1 vdevs, got $C2"
+ log_note "zpool iostat -c passed, expected $C1 vdevs, got $C2"
+fi
+
+# Call iostat on only a specific vdev, and verify that the command only gets
+# run on the vdev. We write the command results to a temp file to verify that
+# the command actually gets run, rather than just verifying that the results
+# are *displayed* for the specific vdev.
+TMP=$($MKTEMP)
+FIRST_VDEV=$($ZPOOL iostat -Pv $testpool | $GREP -Eo '^\s+/[^ ]+' | $HEAD -n 1)
+log_must $ZPOOL iostat -Pv -c "echo \$VDEV_PATH >> $TMP" $testpool \
+ $FIRST_VDEV > /dev/null
+C2=$($WC -w < $TMP)
+$RM $TMP
+if [ "$C2" != "1" ] ; then
+ log_fail "zpool iostat -c <VDEV> failed, expected 1 vdev, got $C2"
+else
+ log_note "zpool iostat -c <VDEV> passed, expected 1 vdev, got $C2"
fi