17877fdebSMatt Macy /*
27877fdebSMatt Macy * Gather top-level ZFS pool and resilver/scan statistics and print using
37877fdebSMatt Macy * influxdb line protocol
47877fdebSMatt Macy * usage: [options] [pool_name]
57877fdebSMatt Macy * where options are:
67877fdebSMatt Macy * --execd, -e run in telegraf execd input plugin mode, [CR] on
77877fdebSMatt Macy * stdin causes a sample to be printed and wait for
87877fdebSMatt Macy * the next [CR]
97877fdebSMatt Macy * --no-histograms, -n don't print histogram data (reduces cardinality
107877fdebSMatt Macy * if you don't care about histograms)
117877fdebSMatt Macy * --sum-histogram-buckets, -s sum histogram bucket values
127877fdebSMatt Macy *
137877fdebSMatt Macy * To integrate into telegraf use one of:
147877fdebSMatt Macy * 1. the `inputs.execd` plugin with the `--execd` option
157877fdebSMatt Macy * 2. the `inputs.exec` plugin to simply run with no options
167877fdebSMatt Macy *
177877fdebSMatt Macy * NOTE: libzfs is an unstable interface. YMMV.
187877fdebSMatt Macy *
197877fdebSMatt Macy * The design goals of this software include:
207877fdebSMatt Macy * + be as lightweight as possible
217877fdebSMatt Macy * + reduce the number of external dependencies as far as possible, hence
227877fdebSMatt Macy * there is no dependency on a client library for managing the metric
237877fdebSMatt Macy * collection -- info is printed, KISS
247877fdebSMatt Macy * + broken pools or kernel bugs can cause this process to hang in an
257877fdebSMatt Macy * unkillable state. For this reason, it is best to keep the damage limited
267877fdebSMatt Macy * to a small process like zpool_influxdb rather than a larger collector.
277877fdebSMatt Macy *
287877fdebSMatt Macy * Copyright 2018-2020 Richard Elling
297877fdebSMatt Macy *
307877fdebSMatt Macy * This software is dual-licensed MIT and CDDL.
317877fdebSMatt Macy *
327877fdebSMatt Macy * The MIT License (MIT)
337877fdebSMatt Macy *
347877fdebSMatt Macy * Permission is hereby granted, free of charge, to any person obtaining a copy
357877fdebSMatt Macy * of this software and associated documentation files (the "Software"), to deal
367877fdebSMatt Macy * in the Software without restriction, including without limitation the rights
377877fdebSMatt Macy * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
387877fdebSMatt Macy * copies of the Software, and to permit persons to whom the Software is
397877fdebSMatt Macy * furnished to do so, subject to the following conditions:
407877fdebSMatt Macy *
417877fdebSMatt Macy * The above copyright notice and this permission notice shall be included in
427877fdebSMatt Macy * all copies or substantial portions of the Software.
437877fdebSMatt Macy *
447877fdebSMatt Macy * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
457877fdebSMatt Macy * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
467877fdebSMatt Macy * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
477877fdebSMatt Macy * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
487877fdebSMatt Macy * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
497877fdebSMatt Macy * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
507877fdebSMatt Macy * SOFTWARE.
517877fdebSMatt Macy *
527877fdebSMatt Macy * CDDL HEADER START
537877fdebSMatt Macy *
547877fdebSMatt Macy * The contents of this file are subject to the terms of the
557877fdebSMatt Macy * Common Development and Distribution License (the "License").
567877fdebSMatt Macy * You may not use this file except in compliance with the License.
577877fdebSMatt Macy *
587877fdebSMatt Macy * The contents of this file are subject to the terms of the
597877fdebSMatt Macy * Common Development and Distribution License Version 1.0 (CDDL-1.0).
607877fdebSMatt Macy * You can obtain a copy of the license from the top-level file
617877fdebSMatt Macy * "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
627877fdebSMatt Macy * You may not use this file except in compliance with the license.
637877fdebSMatt Macy *
647877fdebSMatt Macy * See the License for the specific language governing permissions
657877fdebSMatt Macy * and limitations under the License.
667877fdebSMatt Macy *
677877fdebSMatt Macy * CDDL HEADER END
687877fdebSMatt Macy */
697877fdebSMatt Macy #include <string.h>
707877fdebSMatt Macy #include <getopt.h>
717877fdebSMatt Macy #include <stdio.h>
727877fdebSMatt Macy #include <stdint.h>
737877fdebSMatt Macy #include <inttypes.h>
7416038816SMartin Matuska #include <libzfs.h>
757877fdebSMatt Macy
767877fdebSMatt Macy #define POOL_MEASUREMENT "zpool_stats"
777877fdebSMatt Macy #define SCAN_MEASUREMENT "zpool_scan_stats"
787877fdebSMatt Macy #define VDEV_MEASUREMENT "zpool_vdev_stats"
797877fdebSMatt Macy #define POOL_LATENCY_MEASUREMENT "zpool_latency"
807877fdebSMatt Macy #define POOL_QUEUE_MEASUREMENT "zpool_vdev_queue"
817877fdebSMatt Macy #define MIN_LAT_INDEX 10 /* minimum latency index 10 = 1024ns */
827877fdebSMatt Macy #define POOL_IO_SIZE_MEASUREMENT "zpool_io_size"
837877fdebSMatt Macy #define MIN_SIZE_INDEX 9 /* minimum size index 9 = 512 bytes */
847877fdebSMatt Macy
857877fdebSMatt Macy /* global options */
867877fdebSMatt Macy int execd_mode = 0;
877877fdebSMatt Macy int no_histograms = 0;
887877fdebSMatt Macy int sum_histogram_buckets = 0;
897877fdebSMatt Macy char metric_data_type = 'u';
907877fdebSMatt Macy uint64_t metric_value_mask = UINT64_MAX;
917877fdebSMatt Macy uint64_t timestamp = 0;
927877fdebSMatt Macy int complained_about_sync = 0;
93a0b956f5SMartin Matuska const char *tags = "";
947877fdebSMatt Macy
957877fdebSMatt Macy typedef int (*stat_printer_f)(nvlist_t *, const char *, const char *);
967877fdebSMatt Macy
977877fdebSMatt Macy /*
987877fdebSMatt Macy * influxdb line protocol rules for escaping are important because the
997877fdebSMatt Macy * zpool name can include characters that need to be escaped
1007877fdebSMatt Macy *
1017877fdebSMatt Macy * caller is responsible for freeing result
1027877fdebSMatt Macy */
1037877fdebSMatt Macy static char *
escape_string(const char * s)10416038816SMartin Matuska escape_string(const char *s)
1057877fdebSMatt Macy {
10616038816SMartin Matuska const char *c;
10716038816SMartin Matuska char *d;
1087877fdebSMatt Macy char *t = (char *)malloc(ZFS_MAX_DATASET_NAME_LEN * 2);
1097877fdebSMatt Macy if (t == NULL) {
1107877fdebSMatt Macy fprintf(stderr, "error: cannot allocate memory\n");
1117877fdebSMatt Macy exit(1);
1127877fdebSMatt Macy }
1137877fdebSMatt Macy
1147877fdebSMatt Macy for (c = s, d = t; *c != '\0'; c++, d++) {
1157877fdebSMatt Macy switch (*c) {
1167877fdebSMatt Macy case ' ':
1177877fdebSMatt Macy case ',':
1187877fdebSMatt Macy case '=':
1197877fdebSMatt Macy case '\\':
1207877fdebSMatt Macy *d++ = '\\';
121c03c5b1cSMartin Matuska zfs_fallthrough;
1227877fdebSMatt Macy default:
1237877fdebSMatt Macy *d = *c;
1247877fdebSMatt Macy }
1257877fdebSMatt Macy }
1267877fdebSMatt Macy *d = '\0';
1277877fdebSMatt Macy return (t);
1287877fdebSMatt Macy }
1297877fdebSMatt Macy
1307877fdebSMatt Macy /*
1317877fdebSMatt Macy * print key=value where value is a uint64_t
1327877fdebSMatt Macy */
1337877fdebSMatt Macy static void
print_kv(const char * key,uint64_t value)134a0b956f5SMartin Matuska print_kv(const char *key, uint64_t value)
1357877fdebSMatt Macy {
1367877fdebSMatt Macy printf("%s=%llu%c", key,
1377877fdebSMatt Macy (u_longlong_t)value & metric_value_mask, metric_data_type);
1387877fdebSMatt Macy }
1397877fdebSMatt Macy
1407877fdebSMatt Macy /*
1417877fdebSMatt Macy * print_scan_status() prints the details as often seen in the "zpool status"
1427877fdebSMatt Macy * output. However, unlike the zpool command, which is intended for humans,
1437877fdebSMatt Macy * this output is suitable for long-term tracking in influxdb.
1447877fdebSMatt Macy * TODO: update to include issued scan data
1457877fdebSMatt Macy */
1467877fdebSMatt Macy static int
print_scan_status(nvlist_t * nvroot,const char * pool_name)1477877fdebSMatt Macy print_scan_status(nvlist_t *nvroot, const char *pool_name)
1487877fdebSMatt Macy {
1497877fdebSMatt Macy uint_t c;
1507877fdebSMatt Macy int64_t elapsed;
1517877fdebSMatt Macy uint64_t examined, pass_exam, paused_time, paused_ts, rate;
1527877fdebSMatt Macy uint64_t remaining_time;
1537877fdebSMatt Macy pool_scan_stat_t *ps = NULL;
1547877fdebSMatt Macy double pct_done;
155a0b956f5SMartin Matuska const char *const state[DSS_NUM_STATES] = {
1567877fdebSMatt Macy "none", "scanning", "finished", "canceled"};
157a0b956f5SMartin Matuska const char *func;
1587877fdebSMatt Macy
1597877fdebSMatt Macy (void) nvlist_lookup_uint64_array(nvroot,
1607877fdebSMatt Macy ZPOOL_CONFIG_SCAN_STATS,
1617877fdebSMatt Macy (uint64_t **)&ps, &c);
1627877fdebSMatt Macy
1637877fdebSMatt Macy /*
1647877fdebSMatt Macy * ignore if there are no stats
1657877fdebSMatt Macy */
1667877fdebSMatt Macy if (ps == NULL)
1677877fdebSMatt Macy return (0);
1687877fdebSMatt Macy
1697877fdebSMatt Macy /*
1707877fdebSMatt Macy * return error if state is bogus
1717877fdebSMatt Macy */
1727877fdebSMatt Macy if (ps->pss_state >= DSS_NUM_STATES ||
1737877fdebSMatt Macy ps->pss_func >= POOL_SCAN_FUNCS) {
1747877fdebSMatt Macy if (complained_about_sync % 1000 == 0) {
1757877fdebSMatt Macy fprintf(stderr, "error: cannot decode scan stats: "
1767877fdebSMatt Macy "ZFS is out of sync with compiled zpool_influxdb");
1777877fdebSMatt Macy complained_about_sync++;
1787877fdebSMatt Macy }
1797877fdebSMatt Macy return (1);
1807877fdebSMatt Macy }
1817877fdebSMatt Macy
1827877fdebSMatt Macy switch (ps->pss_func) {
1837877fdebSMatt Macy case POOL_SCAN_NONE:
1847877fdebSMatt Macy func = "none_requested";
1857877fdebSMatt Macy break;
1867877fdebSMatt Macy case POOL_SCAN_SCRUB:
1877877fdebSMatt Macy func = "scrub";
1887877fdebSMatt Macy break;
1897877fdebSMatt Macy case POOL_SCAN_RESILVER:
1907877fdebSMatt Macy func = "resilver";
1917877fdebSMatt Macy break;
1927877fdebSMatt Macy #ifdef POOL_SCAN_REBUILD
1937877fdebSMatt Macy case POOL_SCAN_REBUILD:
1947877fdebSMatt Macy func = "rebuild";
1957877fdebSMatt Macy break;
1967877fdebSMatt Macy #endif
1977877fdebSMatt Macy default:
1987877fdebSMatt Macy func = "scan";
1997877fdebSMatt Macy }
2007877fdebSMatt Macy
2017877fdebSMatt Macy /* overall progress */
2027877fdebSMatt Macy examined = ps->pss_examined ? ps->pss_examined : 1;
2037877fdebSMatt Macy pct_done = 0.0;
2047877fdebSMatt Macy if (ps->pss_to_examine > 0)
2057877fdebSMatt Macy pct_done = 100.0 * examined / ps->pss_to_examine;
2067877fdebSMatt Macy
2077877fdebSMatt Macy #ifdef EZFS_SCRUB_PAUSED
2087877fdebSMatt Macy paused_ts = ps->pss_pass_scrub_pause;
2097877fdebSMatt Macy paused_time = ps->pss_pass_scrub_spent_paused;
2107877fdebSMatt Macy #else
2117877fdebSMatt Macy paused_ts = 0;
2127877fdebSMatt Macy paused_time = 0;
2137877fdebSMatt Macy #endif
2147877fdebSMatt Macy
2157877fdebSMatt Macy /* calculations for this pass */
2167877fdebSMatt Macy if (ps->pss_state == DSS_SCANNING) {
2177877fdebSMatt Macy elapsed = (int64_t)time(NULL) - (int64_t)ps->pss_pass_start -
2187877fdebSMatt Macy (int64_t)paused_time;
2197877fdebSMatt Macy elapsed = (elapsed > 0) ? elapsed : 1;
2207877fdebSMatt Macy pass_exam = ps->pss_pass_exam ? ps->pss_pass_exam : 1;
2217877fdebSMatt Macy rate = pass_exam / elapsed;
2227877fdebSMatt Macy rate = (rate > 0) ? rate : 1;
2237877fdebSMatt Macy remaining_time = ps->pss_to_examine - examined / rate;
2247877fdebSMatt Macy } else {
2257877fdebSMatt Macy elapsed =
2267877fdebSMatt Macy (int64_t)ps->pss_end_time - (int64_t)ps->pss_pass_start -
2277877fdebSMatt Macy (int64_t)paused_time;
2287877fdebSMatt Macy elapsed = (elapsed > 0) ? elapsed : 1;
2297877fdebSMatt Macy pass_exam = ps->pss_pass_exam ? ps->pss_pass_exam : 1;
2307877fdebSMatt Macy rate = pass_exam / elapsed;
2317877fdebSMatt Macy remaining_time = 0;
2327877fdebSMatt Macy }
2337877fdebSMatt Macy rate = rate ? rate : 1;
2347877fdebSMatt Macy
2357877fdebSMatt Macy /* influxdb line protocol format: "tags metrics timestamp" */
2367877fdebSMatt Macy printf("%s%s,function=%s,name=%s,state=%s ",
2377877fdebSMatt Macy SCAN_MEASUREMENT, tags, func, pool_name, state[ps->pss_state]);
2387877fdebSMatt Macy print_kv("end_ts", ps->pss_end_time);
2397877fdebSMatt Macy print_kv(",errors", ps->pss_errors);
2407877fdebSMatt Macy print_kv(",examined", examined);
241*0a97523dSMartin Matuska print_kv(",skipped", ps->pss_skipped);
2427877fdebSMatt Macy print_kv(",issued", ps->pss_issued);
2437877fdebSMatt Macy print_kv(",pass_examined", pass_exam);
2447877fdebSMatt Macy print_kv(",pass_issued", ps->pss_pass_issued);
2457877fdebSMatt Macy print_kv(",paused_ts", paused_ts);
2467877fdebSMatt Macy print_kv(",paused_t", paused_time);
2477877fdebSMatt Macy printf(",pct_done=%.2f", pct_done);
2487877fdebSMatt Macy print_kv(",processed", ps->pss_processed);
2497877fdebSMatt Macy print_kv(",rate", rate);
2507877fdebSMatt Macy print_kv(",remaining_t", remaining_time);
2517877fdebSMatt Macy print_kv(",start_ts", ps->pss_start_time);
2527877fdebSMatt Macy print_kv(",to_examine", ps->pss_to_examine);
2537877fdebSMatt Macy printf(" %llu\n", (u_longlong_t)timestamp);
2547877fdebSMatt Macy return (0);
2557877fdebSMatt Macy }
2567877fdebSMatt Macy
2577877fdebSMatt Macy /*
2587877fdebSMatt Macy * get a vdev name that corresponds to the top-level vdev names
2597877fdebSMatt Macy * printed by `zpool status`
2607877fdebSMatt Macy */
2617877fdebSMatt Macy static char *
get_vdev_name(nvlist_t * nvroot,const char * parent_name)2627877fdebSMatt Macy get_vdev_name(nvlist_t *nvroot, const char *parent_name)
2637877fdebSMatt Macy {
2647877fdebSMatt Macy static char vdev_name[256];
2657877fdebSMatt Macy uint64_t vdev_id = 0;
2667877fdebSMatt Macy
2672a58b312SMartin Matuska const char *vdev_type = "unknown";
268be181ee2SMartin Matuska (void) nvlist_lookup_string(nvroot, ZPOOL_CONFIG_TYPE, &vdev_type);
269a0b956f5SMartin Matuska
2707877fdebSMatt Macy if (nvlist_lookup_uint64(
271a0b956f5SMartin Matuska nvroot, ZPOOL_CONFIG_ID, &vdev_id) != 0)
2727877fdebSMatt Macy vdev_id = UINT64_MAX;
273a0b956f5SMartin Matuska
2747877fdebSMatt Macy if (parent_name == NULL) {
2757877fdebSMatt Macy (void) snprintf(vdev_name, sizeof (vdev_name), "%s",
2767877fdebSMatt Macy vdev_type);
2777877fdebSMatt Macy } else {
2787877fdebSMatt Macy (void) snprintf(vdev_name, sizeof (vdev_name),
279c03c5b1cSMartin Matuska "%.220s/%s-%llu",
2807877fdebSMatt Macy parent_name, vdev_type, (u_longlong_t)vdev_id);
2817877fdebSMatt Macy }
2827877fdebSMatt Macy return (vdev_name);
2837877fdebSMatt Macy }
2847877fdebSMatt Macy
2857877fdebSMatt Macy /*
2867877fdebSMatt Macy * get a string suitable for an influxdb tag that describes this vdev
2877877fdebSMatt Macy *
2887877fdebSMatt Macy * By default only the vdev hierarchical name is shown, separated by '/'
2897877fdebSMatt Macy * If the vdev has an associated path, which is typical of leaf vdevs,
2907877fdebSMatt Macy * then the path is added.
2917877fdebSMatt Macy * It would be nice to have the devid instead of the path, but under
2927877fdebSMatt Macy * Linux we cannot be sure a devid will exist and we'd rather have
2937877fdebSMatt Macy * something than nothing, so we'll use path instead.
2947877fdebSMatt Macy */
2957877fdebSMatt Macy static char *
get_vdev_desc(nvlist_t * nvroot,const char * parent_name)2967877fdebSMatt Macy get_vdev_desc(nvlist_t *nvroot, const char *parent_name)
2977877fdebSMatt Macy {
2987877fdebSMatt Macy static char vdev_desc[2 * MAXPATHLEN];
2997877fdebSMatt Macy char vdev_value[MAXPATHLEN];
3007877fdebSMatt Macy char *s, *t;
3017877fdebSMatt Macy
3022a58b312SMartin Matuska const char *vdev_type = "unknown";
303a0b956f5SMartin Matuska uint64_t vdev_id = UINT64_MAX;
3042a58b312SMartin Matuska const char *vdev_path = NULL;
305be181ee2SMartin Matuska (void) nvlist_lookup_string(nvroot, ZPOOL_CONFIG_TYPE, &vdev_type);
306be181ee2SMartin Matuska (void) nvlist_lookup_uint64(nvroot, ZPOOL_CONFIG_ID, &vdev_id);
307be181ee2SMartin Matuska (void) nvlist_lookup_string(nvroot, ZPOOL_CONFIG_PATH, &vdev_path);
3087877fdebSMatt Macy
3097877fdebSMatt Macy if (parent_name == NULL) {
3107877fdebSMatt Macy s = escape_string(vdev_type);
3117877fdebSMatt Macy (void) snprintf(vdev_value, sizeof (vdev_value), "vdev=%s", s);
3127877fdebSMatt Macy free(s);
3137877fdebSMatt Macy } else {
3147877fdebSMatt Macy s = escape_string((char *)parent_name);
3157877fdebSMatt Macy t = escape_string(vdev_type);
3167877fdebSMatt Macy (void) snprintf(vdev_value, sizeof (vdev_value),
3177877fdebSMatt Macy "vdev=%s/%s-%llu", s, t, (u_longlong_t)vdev_id);
3187877fdebSMatt Macy free(s);
3197877fdebSMatt Macy free(t);
3207877fdebSMatt Macy }
3217877fdebSMatt Macy if (vdev_path == NULL) {
3227877fdebSMatt Macy (void) snprintf(vdev_desc, sizeof (vdev_desc), "%s",
3237877fdebSMatt Macy vdev_value);
3247877fdebSMatt Macy } else {
3257877fdebSMatt Macy s = escape_string(vdev_path);
3267877fdebSMatt Macy (void) snprintf(vdev_desc, sizeof (vdev_desc), "path=%s,%s",
3277877fdebSMatt Macy s, vdev_value);
3287877fdebSMatt Macy free(s);
3297877fdebSMatt Macy }
3307877fdebSMatt Macy return (vdev_desc);
3317877fdebSMatt Macy }
3327877fdebSMatt Macy
3337877fdebSMatt Macy /*
3347877fdebSMatt Macy * vdev summary stats are a combination of the data shown by
3357877fdebSMatt Macy * `zpool status` and `zpool list -v`
3367877fdebSMatt Macy */
3377877fdebSMatt Macy static int
print_summary_stats(nvlist_t * nvroot,const char * pool_name,const char * parent_name)3387877fdebSMatt Macy print_summary_stats(nvlist_t *nvroot, const char *pool_name,
3397877fdebSMatt Macy const char *parent_name)
3407877fdebSMatt Macy {
3417877fdebSMatt Macy uint_t c;
3427877fdebSMatt Macy vdev_stat_t *vs;
3437877fdebSMatt Macy char *vdev_desc = NULL;
3447877fdebSMatt Macy vdev_desc = get_vdev_desc(nvroot, parent_name);
3457877fdebSMatt Macy if (nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_VDEV_STATS,
3467877fdebSMatt Macy (uint64_t **)&vs, &c) != 0) {
3477877fdebSMatt Macy return (1);
3487877fdebSMatt Macy }
3497877fdebSMatt Macy printf("%s%s,name=%s,state=%s,%s ", POOL_MEASUREMENT, tags,
3507877fdebSMatt Macy pool_name, zpool_state_to_name((vdev_state_t)vs->vs_state,
3517877fdebSMatt Macy (vdev_aux_t)vs->vs_aux), vdev_desc);
3527877fdebSMatt Macy print_kv("alloc", vs->vs_alloc);
3537877fdebSMatt Macy print_kv(",free", vs->vs_space - vs->vs_alloc);
3547877fdebSMatt Macy print_kv(",size", vs->vs_space);
3557877fdebSMatt Macy print_kv(",read_bytes", vs->vs_bytes[ZIO_TYPE_READ]);
3567877fdebSMatt Macy print_kv(",read_errors", vs->vs_read_errors);
3577877fdebSMatt Macy print_kv(",read_ops", vs->vs_ops[ZIO_TYPE_READ]);
3587877fdebSMatt Macy print_kv(",write_bytes", vs->vs_bytes[ZIO_TYPE_WRITE]);
3597877fdebSMatt Macy print_kv(",write_errors", vs->vs_write_errors);
3607877fdebSMatt Macy print_kv(",write_ops", vs->vs_ops[ZIO_TYPE_WRITE]);
3617877fdebSMatt Macy print_kv(",checksum_errors", vs->vs_checksum_errors);
3627877fdebSMatt Macy print_kv(",fragmentation", vs->vs_fragmentation);
3637877fdebSMatt Macy printf(" %llu\n", (u_longlong_t)timestamp);
3647877fdebSMatt Macy return (0);
3657877fdebSMatt Macy }
3667877fdebSMatt Macy
3677877fdebSMatt Macy /*
3687877fdebSMatt Macy * vdev latency stats are histograms stored as nvlist arrays of uint64.
3697877fdebSMatt Macy * Latency stats include the ZIO scheduler classes plus lower-level
3707877fdebSMatt Macy * vdev latencies.
3717877fdebSMatt Macy *
3727877fdebSMatt Macy * In many cases, the top-level "root" view obscures the underlying
3737877fdebSMatt Macy * top-level vdev operations. For example, if a pool has a log, special,
3747877fdebSMatt Macy * or cache device, then each can behave very differently. It is useful
3757877fdebSMatt Macy * to see how each is responding.
3767877fdebSMatt Macy */
3777877fdebSMatt Macy static int
print_vdev_latency_stats(nvlist_t * nvroot,const char * pool_name,const char * parent_name)3787877fdebSMatt Macy print_vdev_latency_stats(nvlist_t *nvroot, const char *pool_name,
3797877fdebSMatt Macy const char *parent_name)
3807877fdebSMatt Macy {
3817877fdebSMatt Macy uint_t c, end = 0;
3827877fdebSMatt Macy nvlist_t *nv_ex;
3837877fdebSMatt Macy char *vdev_desc = NULL;
3847877fdebSMatt Macy
3857877fdebSMatt Macy /* short_names become part of the metric name and are influxdb-ready */
3867877fdebSMatt Macy struct lat_lookup {
387a0b956f5SMartin Matuska const char *name;
388a0b956f5SMartin Matuska const char *short_name;
3897877fdebSMatt Macy uint64_t sum;
3907877fdebSMatt Macy uint64_t *array;
3917877fdebSMatt Macy };
3927877fdebSMatt Macy struct lat_lookup lat_type[] = {
3937877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_TOT_R_LAT_HISTO, "total_read", 0},
3947877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_TOT_W_LAT_HISTO, "total_write", 0},
3957877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_DISK_R_LAT_HISTO, "disk_read", 0},
3967877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_DISK_W_LAT_HISTO, "disk_write", 0},
3977877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_SYNC_R_LAT_HISTO, "sync_read", 0},
3987877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_SYNC_W_LAT_HISTO, "sync_write", 0},
3997877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_ASYNC_R_LAT_HISTO, "async_read", 0},
4007877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_ASYNC_W_LAT_HISTO, "async_write", 0},
4017877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_SCRUB_LAT_HISTO, "scrub", 0},
4027877fdebSMatt Macy #ifdef ZPOOL_CONFIG_VDEV_TRIM_LAT_HISTO
4037877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_TRIM_LAT_HISTO, "trim", 0},
4047877fdebSMatt Macy #endif
40521b492edSMartin Matuska {ZPOOL_CONFIG_VDEV_REBUILD_LAT_HISTO, "rebuild", 0},
4067877fdebSMatt Macy {NULL, NULL}
4077877fdebSMatt Macy };
4087877fdebSMatt Macy
4097877fdebSMatt Macy if (nvlist_lookup_nvlist(nvroot,
4107877fdebSMatt Macy ZPOOL_CONFIG_VDEV_STATS_EX, &nv_ex) != 0) {
4117877fdebSMatt Macy return (6);
4127877fdebSMatt Macy }
4137877fdebSMatt Macy
4147877fdebSMatt Macy vdev_desc = get_vdev_desc(nvroot, parent_name);
4157877fdebSMatt Macy
4167877fdebSMatt Macy for (int i = 0; lat_type[i].name; i++) {
4177877fdebSMatt Macy if (nvlist_lookup_uint64_array(nv_ex,
4187877fdebSMatt Macy lat_type[i].name, &lat_type[i].array, &c) != 0) {
4197877fdebSMatt Macy fprintf(stderr, "error: can't get %s\n",
4207877fdebSMatt Macy lat_type[i].name);
4217877fdebSMatt Macy return (3);
4227877fdebSMatt Macy }
4237877fdebSMatt Macy /* end count count, all of the arrays are the same size */
4247877fdebSMatt Macy end = c - 1;
4257877fdebSMatt Macy }
4267877fdebSMatt Macy
4277877fdebSMatt Macy for (int bucket = 0; bucket <= end; bucket++) {
4287877fdebSMatt Macy if (bucket < MIN_LAT_INDEX) {
4297877fdebSMatt Macy /* don't print, but collect the sum */
4307877fdebSMatt Macy for (int i = 0; lat_type[i].name; i++) {
4317877fdebSMatt Macy lat_type[i].sum += lat_type[i].array[bucket];
4327877fdebSMatt Macy }
4337877fdebSMatt Macy continue;
4347877fdebSMatt Macy }
4357877fdebSMatt Macy if (bucket < end) {
4367877fdebSMatt Macy printf("%s%s,le=%0.6f,name=%s,%s ",
4377877fdebSMatt Macy POOL_LATENCY_MEASUREMENT, tags,
4387877fdebSMatt Macy (float)(1ULL << bucket) * 1e-9,
4397877fdebSMatt Macy pool_name, vdev_desc);
4407877fdebSMatt Macy } else {
4417877fdebSMatt Macy printf("%s%s,le=+Inf,name=%s,%s ",
4427877fdebSMatt Macy POOL_LATENCY_MEASUREMENT, tags, pool_name,
4437877fdebSMatt Macy vdev_desc);
4447877fdebSMatt Macy }
4457877fdebSMatt Macy for (int i = 0; lat_type[i].name; i++) {
4467877fdebSMatt Macy if (bucket <= MIN_LAT_INDEX || sum_histogram_buckets) {
4477877fdebSMatt Macy lat_type[i].sum += lat_type[i].array[bucket];
4487877fdebSMatt Macy } else {
4497877fdebSMatt Macy lat_type[i].sum = lat_type[i].array[bucket];
4507877fdebSMatt Macy }
4517877fdebSMatt Macy print_kv(lat_type[i].short_name, lat_type[i].sum);
4527877fdebSMatt Macy if (lat_type[i + 1].name != NULL) {
4537877fdebSMatt Macy printf(",");
4547877fdebSMatt Macy }
4557877fdebSMatt Macy }
4567877fdebSMatt Macy printf(" %llu\n", (u_longlong_t)timestamp);
4577877fdebSMatt Macy }
4587877fdebSMatt Macy return (0);
4597877fdebSMatt Macy }
4607877fdebSMatt Macy
4617877fdebSMatt Macy /*
4627877fdebSMatt Macy * vdev request size stats are histograms stored as nvlist arrays of uint64.
4637877fdebSMatt Macy * Request size stats include the ZIO scheduler classes plus lower-level
4647877fdebSMatt Macy * vdev sizes. Both independent (ind) and aggregated (agg) sizes are reported.
4657877fdebSMatt Macy *
4667877fdebSMatt Macy * In many cases, the top-level "root" view obscures the underlying
4677877fdebSMatt Macy * top-level vdev operations. For example, if a pool has a log, special,
4687877fdebSMatt Macy * or cache device, then each can behave very differently. It is useful
4697877fdebSMatt Macy * to see how each is responding.
4707877fdebSMatt Macy */
4717877fdebSMatt Macy static int
print_vdev_size_stats(nvlist_t * nvroot,const char * pool_name,const char * parent_name)4727877fdebSMatt Macy print_vdev_size_stats(nvlist_t *nvroot, const char *pool_name,
4737877fdebSMatt Macy const char *parent_name)
4747877fdebSMatt Macy {
4757877fdebSMatt Macy uint_t c, end = 0;
4767877fdebSMatt Macy nvlist_t *nv_ex;
4777877fdebSMatt Macy char *vdev_desc = NULL;
4787877fdebSMatt Macy
4797877fdebSMatt Macy /* short_names become the field name */
4807877fdebSMatt Macy struct size_lookup {
481a0b956f5SMartin Matuska const char *name;
482a0b956f5SMartin Matuska const char *short_name;
4837877fdebSMatt Macy uint64_t sum;
4847877fdebSMatt Macy uint64_t *array;
4857877fdebSMatt Macy };
4867877fdebSMatt Macy struct size_lookup size_type[] = {
4877877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_SYNC_IND_R_HISTO, "sync_read_ind"},
4887877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_SYNC_IND_W_HISTO, "sync_write_ind"},
4897877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_ASYNC_IND_R_HISTO, "async_read_ind"},
4907877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_ASYNC_IND_W_HISTO, "async_write_ind"},
4917877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_IND_SCRUB_HISTO, "scrub_read_ind"},
4927877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_SYNC_AGG_R_HISTO, "sync_read_agg"},
4937877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_SYNC_AGG_W_HISTO, "sync_write_agg"},
4947877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_ASYNC_AGG_R_HISTO, "async_read_agg"},
4957877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_ASYNC_AGG_W_HISTO, "async_write_agg"},
4967877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_AGG_SCRUB_HISTO, "scrub_read_agg"},
4977877fdebSMatt Macy #ifdef ZPOOL_CONFIG_VDEV_IND_TRIM_HISTO
4987877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_IND_TRIM_HISTO, "trim_write_ind"},
4997877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_AGG_TRIM_HISTO, "trim_write_agg"},
5007877fdebSMatt Macy #endif
50121b492edSMartin Matuska {ZPOOL_CONFIG_VDEV_IND_REBUILD_HISTO, "rebuild_write_ind"},
50221b492edSMartin Matuska {ZPOOL_CONFIG_VDEV_AGG_REBUILD_HISTO, "rebuild_write_agg"},
5037877fdebSMatt Macy {NULL, NULL}
5047877fdebSMatt Macy };
5057877fdebSMatt Macy
5067877fdebSMatt Macy if (nvlist_lookup_nvlist(nvroot,
5077877fdebSMatt Macy ZPOOL_CONFIG_VDEV_STATS_EX, &nv_ex) != 0) {
5087877fdebSMatt Macy return (6);
5097877fdebSMatt Macy }
5107877fdebSMatt Macy
5117877fdebSMatt Macy vdev_desc = get_vdev_desc(nvroot, parent_name);
5127877fdebSMatt Macy
5137877fdebSMatt Macy for (int i = 0; size_type[i].name; i++) {
5147877fdebSMatt Macy if (nvlist_lookup_uint64_array(nv_ex, size_type[i].name,
5157877fdebSMatt Macy &size_type[i].array, &c) != 0) {
5167877fdebSMatt Macy fprintf(stderr, "error: can't get %s\n",
5177877fdebSMatt Macy size_type[i].name);
5187877fdebSMatt Macy return (3);
5197877fdebSMatt Macy }
5207877fdebSMatt Macy /* end count count, all of the arrays are the same size */
5217877fdebSMatt Macy end = c - 1;
5227877fdebSMatt Macy }
5237877fdebSMatt Macy
5247877fdebSMatt Macy for (int bucket = 0; bucket <= end; bucket++) {
5257877fdebSMatt Macy if (bucket < MIN_SIZE_INDEX) {
5267877fdebSMatt Macy /* don't print, but collect the sum */
5277877fdebSMatt Macy for (int i = 0; size_type[i].name; i++) {
5287877fdebSMatt Macy size_type[i].sum += size_type[i].array[bucket];
5297877fdebSMatt Macy }
5307877fdebSMatt Macy continue;
5317877fdebSMatt Macy }
5327877fdebSMatt Macy
5337877fdebSMatt Macy if (bucket < end) {
5347877fdebSMatt Macy printf("%s%s,le=%llu,name=%s,%s ",
5357877fdebSMatt Macy POOL_IO_SIZE_MEASUREMENT, tags, 1ULL << bucket,
5367877fdebSMatt Macy pool_name, vdev_desc);
5377877fdebSMatt Macy } else {
5387877fdebSMatt Macy printf("%s%s,le=+Inf,name=%s,%s ",
5397877fdebSMatt Macy POOL_IO_SIZE_MEASUREMENT, tags, pool_name,
5407877fdebSMatt Macy vdev_desc);
5417877fdebSMatt Macy }
5427877fdebSMatt Macy for (int i = 0; size_type[i].name; i++) {
5437877fdebSMatt Macy if (bucket <= MIN_SIZE_INDEX || sum_histogram_buckets) {
5447877fdebSMatt Macy size_type[i].sum += size_type[i].array[bucket];
5457877fdebSMatt Macy } else {
5467877fdebSMatt Macy size_type[i].sum = size_type[i].array[bucket];
5477877fdebSMatt Macy }
5487877fdebSMatt Macy print_kv(size_type[i].short_name, size_type[i].sum);
5497877fdebSMatt Macy if (size_type[i + 1].name != NULL) {
5507877fdebSMatt Macy printf(",");
5517877fdebSMatt Macy }
5527877fdebSMatt Macy }
5537877fdebSMatt Macy printf(" %llu\n", (u_longlong_t)timestamp);
5547877fdebSMatt Macy }
5557877fdebSMatt Macy return (0);
5567877fdebSMatt Macy }
5577877fdebSMatt Macy
5587877fdebSMatt Macy /*
5597877fdebSMatt Macy * ZIO scheduler queue stats are stored as gauges. This is unfortunate
5607877fdebSMatt Macy * because the values can change very rapidly and any point-in-time
5617877fdebSMatt Macy * value will quickly be obsoleted. It is also not easy to downsample.
5627877fdebSMatt Macy * Thus only the top-level queue stats might be beneficial... maybe.
5637877fdebSMatt Macy */
5647877fdebSMatt Macy static int
print_queue_stats(nvlist_t * nvroot,const char * pool_name,const char * parent_name)5657877fdebSMatt Macy print_queue_stats(nvlist_t *nvroot, const char *pool_name,
5667877fdebSMatt Macy const char *parent_name)
5677877fdebSMatt Macy {
5687877fdebSMatt Macy nvlist_t *nv_ex;
5697877fdebSMatt Macy uint64_t value;
5707877fdebSMatt Macy
5717877fdebSMatt Macy /* short_names are used for the field name */
5727877fdebSMatt Macy struct queue_lookup {
573a0b956f5SMartin Matuska const char *name;
574a0b956f5SMartin Matuska const char *short_name;
5757877fdebSMatt Macy };
5767877fdebSMatt Macy struct queue_lookup queue_type[] = {
5777877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_SYNC_R_ACTIVE_QUEUE, "sync_r_active"},
5787877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_SYNC_W_ACTIVE_QUEUE, "sync_w_active"},
5797877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_ASYNC_R_ACTIVE_QUEUE, "async_r_active"},
5807877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_ASYNC_W_ACTIVE_QUEUE, "async_w_active"},
5817877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_SCRUB_ACTIVE_QUEUE, "async_scrub_active"},
58221b492edSMartin Matuska {ZPOOL_CONFIG_VDEV_REBUILD_ACTIVE_QUEUE, "rebuild_active"},
5837877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_SYNC_R_PEND_QUEUE, "sync_r_pend"},
5847877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_SYNC_W_PEND_QUEUE, "sync_w_pend"},
5857877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_ASYNC_R_PEND_QUEUE, "async_r_pend"},
5867877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_ASYNC_W_PEND_QUEUE, "async_w_pend"},
5877877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_SCRUB_PEND_QUEUE, "async_scrub_pend"},
58821b492edSMartin Matuska {ZPOOL_CONFIG_VDEV_REBUILD_PEND_QUEUE, "rebuild_pend"},
5897877fdebSMatt Macy {NULL, NULL}
5907877fdebSMatt Macy };
5917877fdebSMatt Macy
5927877fdebSMatt Macy if (nvlist_lookup_nvlist(nvroot,
5937877fdebSMatt Macy ZPOOL_CONFIG_VDEV_STATS_EX, &nv_ex) != 0) {
5947877fdebSMatt Macy return (6);
5957877fdebSMatt Macy }
5967877fdebSMatt Macy
5977877fdebSMatt Macy printf("%s%s,name=%s,%s ", POOL_QUEUE_MEASUREMENT, tags, pool_name,
5987877fdebSMatt Macy get_vdev_desc(nvroot, parent_name));
5997877fdebSMatt Macy for (int i = 0; queue_type[i].name; i++) {
6007877fdebSMatt Macy if (nvlist_lookup_uint64(nv_ex,
6017877fdebSMatt Macy queue_type[i].name, &value) != 0) {
6027877fdebSMatt Macy fprintf(stderr, "error: can't get %s\n",
6037877fdebSMatt Macy queue_type[i].name);
6047877fdebSMatt Macy return (3);
6057877fdebSMatt Macy }
6067877fdebSMatt Macy print_kv(queue_type[i].short_name, value);
6077877fdebSMatt Macy if (queue_type[i + 1].name != NULL) {
6087877fdebSMatt Macy printf(",");
6097877fdebSMatt Macy }
6107877fdebSMatt Macy }
6117877fdebSMatt Macy printf(" %llu\n", (u_longlong_t)timestamp);
6127877fdebSMatt Macy return (0);
6137877fdebSMatt Macy }
6147877fdebSMatt Macy
6157877fdebSMatt Macy /*
6167877fdebSMatt Macy * top-level vdev stats are at the pool level
6177877fdebSMatt Macy */
6187877fdebSMatt Macy static int
print_top_level_vdev_stats(nvlist_t * nvroot,const char * pool_name)6197877fdebSMatt Macy print_top_level_vdev_stats(nvlist_t *nvroot, const char *pool_name)
6207877fdebSMatt Macy {
6217877fdebSMatt Macy nvlist_t *nv_ex;
6227877fdebSMatt Macy uint64_t value;
6237877fdebSMatt Macy
6247877fdebSMatt Macy /* short_names become part of the metric name */
6257877fdebSMatt Macy struct queue_lookup {
626a0b956f5SMartin Matuska const char *name;
627a0b956f5SMartin Matuska const char *short_name;
6287877fdebSMatt Macy };
6297877fdebSMatt Macy struct queue_lookup queue_type[] = {
6307877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_SYNC_R_ACTIVE_QUEUE, "sync_r_active_queue"},
6317877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_SYNC_W_ACTIVE_QUEUE, "sync_w_active_queue"},
6327877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_ASYNC_R_ACTIVE_QUEUE, "async_r_active_queue"},
6337877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_ASYNC_W_ACTIVE_QUEUE, "async_w_active_queue"},
6347877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_SCRUB_ACTIVE_QUEUE, "async_scrub_active_queue"},
63521b492edSMartin Matuska {ZPOOL_CONFIG_VDEV_REBUILD_ACTIVE_QUEUE, "rebuild_active_queue"},
6367877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_SYNC_R_PEND_QUEUE, "sync_r_pend_queue"},
6377877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_SYNC_W_PEND_QUEUE, "sync_w_pend_queue"},
6387877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_ASYNC_R_PEND_QUEUE, "async_r_pend_queue"},
6397877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_ASYNC_W_PEND_QUEUE, "async_w_pend_queue"},
6407877fdebSMatt Macy {ZPOOL_CONFIG_VDEV_SCRUB_PEND_QUEUE, "async_scrub_pend_queue"},
64121b492edSMartin Matuska {ZPOOL_CONFIG_VDEV_REBUILD_PEND_QUEUE, "rebuild_pend_queue"},
6427877fdebSMatt Macy {NULL, NULL}
6437877fdebSMatt Macy };
6447877fdebSMatt Macy
6457877fdebSMatt Macy if (nvlist_lookup_nvlist(nvroot,
6467877fdebSMatt Macy ZPOOL_CONFIG_VDEV_STATS_EX, &nv_ex) != 0) {
6477877fdebSMatt Macy return (6);
6487877fdebSMatt Macy }
6497877fdebSMatt Macy
6507877fdebSMatt Macy printf("%s%s,name=%s,vdev=root ", VDEV_MEASUREMENT, tags,
6517877fdebSMatt Macy pool_name);
6527877fdebSMatt Macy for (int i = 0; queue_type[i].name; i++) {
6537877fdebSMatt Macy if (nvlist_lookup_uint64(nv_ex,
6547877fdebSMatt Macy queue_type[i].name, &value) != 0) {
6557877fdebSMatt Macy fprintf(stderr, "error: can't get %s\n",
6567877fdebSMatt Macy queue_type[i].name);
6577877fdebSMatt Macy return (3);
6587877fdebSMatt Macy }
6597877fdebSMatt Macy if (i > 0)
6607877fdebSMatt Macy printf(",");
6617877fdebSMatt Macy print_kv(queue_type[i].short_name, value);
6627877fdebSMatt Macy }
6637877fdebSMatt Macy
6647877fdebSMatt Macy printf(" %llu\n", (u_longlong_t)timestamp);
6657877fdebSMatt Macy return (0);
6667877fdebSMatt Macy }
6677877fdebSMatt Macy
6687877fdebSMatt Macy /*
6697877fdebSMatt Macy * recursive stats printer
6707877fdebSMatt Macy */
6717877fdebSMatt Macy static int
print_recursive_stats(stat_printer_f func,nvlist_t * nvroot,const char * pool_name,const char * parent_name,int descend)6727877fdebSMatt Macy print_recursive_stats(stat_printer_f func, nvlist_t *nvroot,
6737877fdebSMatt Macy const char *pool_name, const char *parent_name, int descend)
6747877fdebSMatt Macy {
6757877fdebSMatt Macy uint_t c, children;
6767877fdebSMatt Macy nvlist_t **child;
6777877fdebSMatt Macy char vdev_name[256];
6787877fdebSMatt Macy int err;
6797877fdebSMatt Macy
6807877fdebSMatt Macy err = func(nvroot, pool_name, parent_name);
6817877fdebSMatt Macy if (err)
6827877fdebSMatt Macy return (err);
6837877fdebSMatt Macy
6847877fdebSMatt Macy if (descend && nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,
6857877fdebSMatt Macy &child, &children) == 0) {
6863f9d360cSMartin Matuska (void) strlcpy(vdev_name, get_vdev_name(nvroot, parent_name),
6877877fdebSMatt Macy sizeof (vdev_name));
6887877fdebSMatt Macy
6897877fdebSMatt Macy for (c = 0; c < children; c++) {
690be181ee2SMartin Matuska err = print_recursive_stats(func, child[c], pool_name,
6917877fdebSMatt Macy vdev_name, descend);
692be181ee2SMartin Matuska if (err)
693be181ee2SMartin Matuska return (err);
6947877fdebSMatt Macy }
6957877fdebSMatt Macy }
6967877fdebSMatt Macy return (0);
6977877fdebSMatt Macy }
6987877fdebSMatt Macy
6997877fdebSMatt Macy /*
7007877fdebSMatt Macy * call-back to print the stats from the pool config
7017877fdebSMatt Macy *
7027877fdebSMatt Macy * Note: if the pool is broken, this can hang indefinitely and perhaps in an
7037877fdebSMatt Macy * unkillable state.
7047877fdebSMatt Macy */
7057877fdebSMatt Macy static int
print_stats(zpool_handle_t * zhp,void * data)7067877fdebSMatt Macy print_stats(zpool_handle_t *zhp, void *data)
7077877fdebSMatt Macy {
7087877fdebSMatt Macy uint_t c;
7097877fdebSMatt Macy int err;
7107877fdebSMatt Macy boolean_t missing;
7117877fdebSMatt Macy nvlist_t *config, *nvroot;
7127877fdebSMatt Macy vdev_stat_t *vs;
7137877fdebSMatt Macy struct timespec tv;
7147877fdebSMatt Macy char *pool_name;
7157877fdebSMatt Macy
7167877fdebSMatt Macy /* if not this pool return quickly */
7177877fdebSMatt Macy if (data &&
71816038816SMartin Matuska strncmp(data, zpool_get_name(zhp), ZFS_MAX_DATASET_NAME_LEN) != 0) {
7197877fdebSMatt Macy zpool_close(zhp);
7207877fdebSMatt Macy return (0);
7217877fdebSMatt Macy }
7227877fdebSMatt Macy
7237877fdebSMatt Macy if (zpool_refresh_stats(zhp, &missing) != 0) {
7247877fdebSMatt Macy zpool_close(zhp);
7257877fdebSMatt Macy return (1);
7267877fdebSMatt Macy }
7277877fdebSMatt Macy
7287877fdebSMatt Macy config = zpool_get_config(zhp, NULL);
7297877fdebSMatt Macy if (clock_gettime(CLOCK_REALTIME, &tv) != 0)
7307877fdebSMatt Macy timestamp = (uint64_t)time(NULL) * 1000000000;
7317877fdebSMatt Macy else
7327877fdebSMatt Macy timestamp =
7337877fdebSMatt Macy ((uint64_t)tv.tv_sec * 1000000000) + (uint64_t)tv.tv_nsec;
7347877fdebSMatt Macy
7357877fdebSMatt Macy if (nvlist_lookup_nvlist(
7367877fdebSMatt Macy config, ZPOOL_CONFIG_VDEV_TREE, &nvroot) != 0) {
7377877fdebSMatt Macy zpool_close(zhp);
7387877fdebSMatt Macy return (2);
7397877fdebSMatt Macy }
7407877fdebSMatt Macy if (nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_VDEV_STATS,
7417877fdebSMatt Macy (uint64_t **)&vs, &c) != 0) {
7427877fdebSMatt Macy zpool_close(zhp);
7437877fdebSMatt Macy return (3);
7447877fdebSMatt Macy }
7457877fdebSMatt Macy
74616038816SMartin Matuska pool_name = escape_string(zpool_get_name(zhp));
7477877fdebSMatt Macy err = print_recursive_stats(print_summary_stats, nvroot,
7487877fdebSMatt Macy pool_name, NULL, 1);
7497877fdebSMatt Macy /* if any of these return an error, skip the rest */
7507877fdebSMatt Macy if (err == 0)
7517877fdebSMatt Macy err = print_top_level_vdev_stats(nvroot, pool_name);
7527877fdebSMatt Macy
7537877fdebSMatt Macy if (no_histograms == 0) {
7547877fdebSMatt Macy if (err == 0)
7557877fdebSMatt Macy err = print_recursive_stats(print_vdev_latency_stats, nvroot,
7567877fdebSMatt Macy pool_name, NULL, 1);
7577877fdebSMatt Macy if (err == 0)
7587877fdebSMatt Macy err = print_recursive_stats(print_vdev_size_stats, nvroot,
7597877fdebSMatt Macy pool_name, NULL, 1);
7607877fdebSMatt Macy if (err == 0)
7617877fdebSMatt Macy err = print_recursive_stats(print_queue_stats, nvroot,
7627877fdebSMatt Macy pool_name, NULL, 0);
7637877fdebSMatt Macy }
7647877fdebSMatt Macy if (err == 0)
7657877fdebSMatt Macy err = print_scan_status(nvroot, pool_name);
7667877fdebSMatt Macy
7677877fdebSMatt Macy free(pool_name);
7687877fdebSMatt Macy zpool_close(zhp);
7697877fdebSMatt Macy return (err);
7707877fdebSMatt Macy }
7717877fdebSMatt Macy
7727877fdebSMatt Macy static void
usage(char * name)7737877fdebSMatt Macy usage(char *name)
7747877fdebSMatt Macy {
7757877fdebSMatt Macy fprintf(stderr, "usage: %s [--execd][--no-histograms]"
7767877fdebSMatt Macy "[--sum-histogram-buckets] [--signed-int] [poolname]\n", name);
7777877fdebSMatt Macy exit(EXIT_FAILURE);
7787877fdebSMatt Macy }
7797877fdebSMatt Macy
7807877fdebSMatt Macy int
main(int argc,char * argv[])7817877fdebSMatt Macy main(int argc, char *argv[])
7827877fdebSMatt Macy {
7837877fdebSMatt Macy int opt;
7847877fdebSMatt Macy int ret = 8;
785a0b956f5SMartin Matuska char *line = NULL, *ttags = NULL;
7867877fdebSMatt Macy size_t len, tagslen = 0;
7877877fdebSMatt Macy struct option long_options[] = {
7887877fdebSMatt Macy {"execd", no_argument, NULL, 'e'},
7897877fdebSMatt Macy {"help", no_argument, NULL, 'h'},
7907877fdebSMatt Macy {"no-histograms", no_argument, NULL, 'n'},
7917877fdebSMatt Macy {"signed-int", no_argument, NULL, 'i'},
7927877fdebSMatt Macy {"sum-histogram-buckets", no_argument, NULL, 's'},
7937877fdebSMatt Macy {"tags", required_argument, NULL, 't'},
7947877fdebSMatt Macy {0, 0, 0, 0}
7957877fdebSMatt Macy };
7967877fdebSMatt Macy while ((opt = getopt_long(
7977877fdebSMatt Macy argc, argv, "ehinst:", long_options, NULL)) != -1) {
7987877fdebSMatt Macy switch (opt) {
7997877fdebSMatt Macy case 'e':
8007877fdebSMatt Macy execd_mode = 1;
8017877fdebSMatt Macy break;
8027877fdebSMatt Macy case 'i':
8037877fdebSMatt Macy metric_data_type = 'i';
8047877fdebSMatt Macy metric_value_mask = INT64_MAX;
8057877fdebSMatt Macy break;
8067877fdebSMatt Macy case 'n':
8077877fdebSMatt Macy no_histograms = 1;
8087877fdebSMatt Macy break;
8097877fdebSMatt Macy case 's':
8107877fdebSMatt Macy sum_histogram_buckets = 1;
8117877fdebSMatt Macy break;
8127877fdebSMatt Macy case 't':
813a0b956f5SMartin Matuska free(ttags);
8147877fdebSMatt Macy tagslen = strlen(optarg) + 2;
815a0b956f5SMartin Matuska ttags = calloc(1, tagslen);
816a0b956f5SMartin Matuska if (ttags == NULL) {
8177877fdebSMatt Macy fprintf(stderr,
8187877fdebSMatt Macy "error: cannot allocate memory "
8197877fdebSMatt Macy "for tags\n");
8207877fdebSMatt Macy exit(1);
8217877fdebSMatt Macy }
822a0b956f5SMartin Matuska (void) snprintf(ttags, tagslen, ",%s", optarg);
823a0b956f5SMartin Matuska tags = ttags;
8247877fdebSMatt Macy break;
8257877fdebSMatt Macy default:
8267877fdebSMatt Macy usage(argv[0]);
8277877fdebSMatt Macy }
8287877fdebSMatt Macy }
8297877fdebSMatt Macy
8307877fdebSMatt Macy libzfs_handle_t *g_zfs;
8317877fdebSMatt Macy if ((g_zfs = libzfs_init()) == NULL) {
8327877fdebSMatt Macy fprintf(stderr,
8337877fdebSMatt Macy "error: cannot initialize libzfs. "
8347877fdebSMatt Macy "Is the zfs module loaded or zrepl running?\n");
8357877fdebSMatt Macy exit(EXIT_FAILURE);
8367877fdebSMatt Macy }
8377877fdebSMatt Macy if (execd_mode == 0) {
8387877fdebSMatt Macy ret = zpool_iter(g_zfs, print_stats, argv[optind]);
8397877fdebSMatt Macy return (ret);
8407877fdebSMatt Macy }
8417877fdebSMatt Macy while (getline(&line, &len, stdin) != -1) {
8427877fdebSMatt Macy ret = zpool_iter(g_zfs, print_stats, argv[optind]);
8437877fdebSMatt Macy fflush(stdout);
8447877fdebSMatt Macy }
8457877fdebSMatt Macy return (ret);
8467877fdebSMatt Macy }
847