diff options
Diffstat (limited to 'cmd/zpool/zpool_iter.c')
-rw-r--r-- | cmd/zpool/zpool_iter.c | 160 |
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); +} |