summaryrefslogtreecommitdiffstats
path: root/cmd/zpool/zpool_iter.c
diff options
context:
space:
mode:
authorTony Hutter <[email protected]>2016-11-29 13:45:38 -0800
committerBrian Behlendorf <[email protected]>2016-11-29 14:45:38 -0700
commit8720e9e7482fa2dce4f34c56d3c7451833413d7d (patch)
tree2475bb498ab7bc8e5fb016dbca1a337d10f257c5 /cmd/zpool/zpool_iter.c
parent2f71caf2d926249920d1b9162550c56715cc6461 (diff)
Add -c to zpool iostat & status to run command
This patch adds a command (-c) option to zpool status and zpool iostat. The -c option allows you to run an arbitrary command on each vdev and display the first line of output in zpool status/iostat. The environment vars VDEV_PATH and VDEV_UPATH are set to the vdev's path and "underlying path" before running the command. For device mapper, multipath, or partitioned vdevs, VDEV_UPATH is the actual underlying /dev/sd* disk. This can be useful if the command you're running requires a /dev/sd* device. The patch also uses /sys/block/<dev>/slaves/ to lookup the underlying device instead of using libdevmapper. This not only removes the libdevmapper requirement at build time, but also allows you to resolve device mapper devices without being root. This means that UDEV_UPATH get set correctly when running zpool status/iostat as an unprivileged user. Example: $ zpool status -c 'echo I am $VDEV_PATH, $VDEV_UPATH' NAME STATE READ WRITE CKSUM mypool ONLINE 0 0 0 mirror-0 ONLINE 0 0 0 mpatha ONLINE 0 0 0 I am /dev/mapper/mpatha, /dev/sdc sdb ONLINE 0 0 0 I am /dev/sdb1, /dev/sdb Reviewed-by: Giuseppe Di Natale <[email protected]> Reviewed-by: Brian Behlendorf <[email protected]> Signed-off-by: Tony Hutter <[email protected]> Closes #5368
Diffstat (limited to 'cmd/zpool/zpool_iter.c')
-rw-r--r--cmd/zpool/zpool_iter.c160
1 files changed, 160 insertions, 0 deletions
diff --git a/cmd/zpool/zpool_iter.c b/cmd/zpool/zpool_iter.c
index 1b64a5a5c..b397c7ec2 100644
--- a/cmd/zpool/zpool_iter.c
+++ b/cmd/zpool/zpool_iter.c
@@ -33,6 +33,7 @@
#include <strings.h>
#include <libzfs.h>
+#include <sys/zfs_context.h>
#include "zpool_util.h"
@@ -316,3 +317,162 @@ for_each_vdev(zpool_handle_t *zhp, pool_vdev_iter_f func, void *data)
}
return (for_each_vdev_cb(zhp, nvroot, func, data));
}
+
+/* Thread function run for each vdev */
+static void
+vdev_run_cmd_thread(void *cb_cmd_data)
+{
+ vdev_cmd_data_t *data = cb_cmd_data;
+ char *pos = NULL;
+ FILE *fp;
+ size_t len = 0;
+ char cmd[_POSIX_ARG_MAX];
+
+ /* Set our VDEV_PATH and VDEV_UPATH env vars and run command */
+ if (snprintf(cmd, sizeof (cmd), "VDEV_PATH=%s && VDEV_UPATH=%s && %s",
+ data->path, data->upath ? data->upath : "\"\"", data->cmd) >=
+ sizeof (cmd)) {
+ /* Our string was truncated */
+ return;
+ }
+
+ fp = popen(cmd, "r");
+ if (fp == NULL)
+ return;
+
+ data->line = NULL;
+
+ /* Save the first line of output from the command */
+ if (getline(&data->line, &len, fp) != -1) {
+ /* Success. Remove newline from the end, if necessary. */
+ if ((pos = strchr(data->line, '\n')) != NULL)
+ *pos = '\0';
+ } else {
+ data->line = NULL;
+ }
+ pclose(fp);
+}
+
+/* For each vdev in the pool run a command */
+static int
+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;
+
+ if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) != 0)
+ return (1);
+
+ /* Spares show more than once if they're in use, so skip if exists */
+ for (i = 0; i < vcdl->count; i++) {
+ if ((strcmp(vcdl->data[i].path, path) == 0) &&
+ (strcmp(vcdl->data[i].pool, zpool_get_name(zhp)) == 0)) {
+ /* vdev already exists, skip it */
+ return (0);
+ }
+ }
+
+ /*
+ * Resize our array and add in the new element.
+ */
+ if (!(vcdl->data = realloc(vcdl->data,
+ sizeof (*vcdl->data) * (vcdl->count + 1))))
+ return (ENOMEM); /* couldn't realloc */
+
+ data = &vcdl->data[vcdl->count];
+
+ data->pool = strdup(zpool_get_name(zhp));
+ data->path = strdup(path);
+ data->upath = zfs_get_underlying_path(path);
+ data->cmd = vcdl->cmd;
+
+ vcdl->count++;
+
+ return (0);
+}
+
+/* Get the names and count of the vdevs */
+static int
+all_pools_for_each_vdev_gather_cb(zpool_handle_t *zhp, void *cb_vcdl)
+{
+ return (for_each_vdev(zhp, for_each_vdev_run_cb, cb_vcdl));
+}
+
+/*
+ * Now that vcdl is populated with our complete list of vdevs, spawn
+ * off the commands.
+ */
+static void
+all_pools_for_each_vdev_run_vcdl(vdev_cmd_data_list_t *vcdl)
+{
+ taskq_t *t;
+ int i;
+ /* 5 * boot_ncpus selfishly chosen since it works best on LLNL's HW */
+ int max_threads = 5 * boot_ncpus;
+
+ /*
+ * Under Linux we use a taskq to parallelize running a command
+ * on each vdev. It is therefore necessary to initialize this
+ * functionality for the duration of the threads.
+ */
+ thread_init();
+
+ t = taskq_create("z_pool_cmd", max_threads, defclsyspri, max_threads,
+ INT_MAX, 0);
+ if (t == NULL)
+ return;
+
+ /* Spawn off the command for each vdev */
+ for (i = 0; i < vcdl->count; i++) {
+ (void) taskq_dispatch(t, vdev_run_cmd_thread,
+ (void *) &vcdl->data[i], TQ_SLEEP);
+ }
+
+ /* Wait for threads to finish */
+ taskq_wait(t);
+ taskq_destroy(t);
+ thread_fini();
+}
+
+/*
+ * 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.
+ *
+ * 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)
+{
+ vdev_cmd_data_list_t *vcdl;
+ vcdl = safe_malloc(sizeof (vcdl));
+ vcdl->cmd = cmd;
+
+ /* 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);
+
+ /* Run command on all vdevs in all pools */
+ all_pools_for_each_vdev_run_vcdl(vcdl);
+
+ return (vcdl);
+}
+
+/*
+ * Free the vdev_cmd_data_list_t created by all_pools_for_each_vdev_run()
+ */
+void
+free_vdev_cmd_data_list(vdev_cmd_data_list_t *vcdl)
+{
+ int i;
+ for (i = 0; i < vcdl->count; i++) {
+ free(vcdl->data[i].path);
+ free(vcdl->data[i].pool);
+ free(vcdl->data[i].upath);
+ free(vcdl->data[i].line);
+ }
+ free(vcdl->data);
+ free(vcdl);
+}