diff --git a/cmd/zpool/zpool_main.c b/cmd/zpool/zpool_main.c index b347cfba97c8..f6db464c1997 100644 --- a/cmd/zpool/zpool_main.c +++ b/cmd/zpool/zpool_main.c @@ -2161,6 +2161,7 @@ typedef struct status_cbdata { boolean_t cb_explain; boolean_t cb_first; boolean_t cb_dedup_stats; + boolean_t cb_print_unhealthy; boolean_t cb_print_status; boolean_t cb_print_slow_ios; boolean_t cb_print_vdev_init; @@ -2357,8 +2358,37 @@ health_str_to_color(const char *health) return (NULL); } +/* + * If printing out only unhealthy vdevs, then loop through children so that + * if any of them are unhealthy, we print our own line as a parent. + */ +static boolean_t +check_child_health(nvlist_t *nv) +{ + /* Loop through children and return true on first error found */ + uint_t c, num_children, vsc; + nvlist_t **children_nv_array; + vdev_stat_t *vs; + + (void) nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, + &children_nv_array, &num_children); + for (c = 0; c < num_children; c++) { + /* Need to grab vs info for each child vdev and check for errors */ + nvlist_t *child_nv = children_nv_array[c]; + verify(nvlist_lookup_uint64_array(child_nv, + ZPOOL_CONFIG_VDEV_STATS, + (uint64_t **)&vs, &vsc) == 0); + if (vs->vs_checksum_errors || vs->vs_read_errors || + vs->vs_write_errors || vs->vs_state != VDEV_STATE_HEALTHY) + return (B_TRUE); + } + return (B_FALSE); +} + + /* * Print out configuration state as requested by status_callback. + * If '-e' is specified, only print root and unhealthy vdevs */ static void print_status_config(zpool_handle_t *zhp, status_cbdata_t *cb, const char *name, @@ -2368,6 +2398,7 @@ print_status_config(zpool_handle_t *zhp, status_cbdata_t *cb, const char *name, uint_t c, i, vsc, children; pool_scan_stat_t *ps = NULL; vdev_stat_t *vs; + boolean_t print_parent = B_FALSE; char rbuf[6], wbuf[6], cbuf[6]; char *vname; uint64_t notpresent; @@ -2402,226 +2433,260 @@ print_status_config(zpool_handle_t *zhp, status_cbdata_t *cb, const char *name, state = gettext("AVAIL"); } - printf_color(health_str_to_color(state), - "\t%*s%-*s %-8s", depth, "", cb->cb_namewidth - depth, - name, state); - - if (!isspare) { - if (vs->vs_read_errors) - rcolor = ANSI_RED; - - if (vs->vs_write_errors) - wcolor = ANSI_RED; - - if (vs->vs_checksum_errors) - ccolor = ANSI_RED; - - if (cb->cb_literal) { - fputc(' ', stdout); - printf_color(rcolor, "%5llu", - (u_longlong_t)vs->vs_read_errors); - fputc(' ', stdout); - printf_color(wcolor, "%5llu", - (u_longlong_t)vs->vs_write_errors); - fputc(' ', stdout); - printf_color(ccolor, "%5llu", - (u_longlong_t)vs->vs_checksum_errors); - } else { - zfs_nicenum(vs->vs_read_errors, rbuf, sizeof (rbuf)); - zfs_nicenum(vs->vs_write_errors, wbuf, sizeof (wbuf)); - zfs_nicenum(vs->vs_checksum_errors, cbuf, - sizeof (cbuf)); - fputc(' ', stdout); - printf_color(rcolor, "%5s", rbuf); - fputc(' ', stdout); - printf_color(wcolor, "%5s", wbuf); - fputc(' ', stdout); - printf_color(ccolor, "%5s", cbuf); - } - if (cb->cb_print_slow_ios) { - if (children == 0) { - /* Only leafs vdevs have slow IOs */ - zfs_nicenum(vs->vs_slow_ios, rbuf, - sizeof (rbuf)); + /* Print columns 1-2, vdev name, state */ + if (cb->cb_print_unhealthy) { + if (children > 0) { + /* If check_child_health() is true, a child is unhealthy */ + if (check_child_health(nv)) + print_parent = B_TRUE; + } + } + if (! (depth > 0 && + cb->cb_print_unhealthy && + vs->vs_state == VDEV_STATE_HEALTHY && + vs->vs_read_errors == 0 && + vs->vs_write_errors == 0 && + vs->vs_checksum_errors == 0 && + print_parent == B_FALSE)) { + + printf_color(health_str_to_color(state), + "\t%*s%-*s %-8s", depth, "", cb->cb_namewidth - depth, + name, state); + + /* Print columns 3-5 ZFS errs */ + if (!isspare) { + if (vs->vs_read_errors) + rcolor = ANSI_RED; + + if (vs->vs_write_errors) + wcolor = ANSI_RED; + + if (vs->vs_checksum_errors) + ccolor = ANSI_RED; + + if (cb->cb_literal) { + fputc(' ', stdout); + printf_color(rcolor, "%5llu", + (u_longlong_t)vs->vs_read_errors); + fputc(' ', stdout); + printf_color(wcolor, "%5llu", + (u_longlong_t)vs->vs_write_errors); + fputc(' ', stdout); + printf_color(ccolor, "%5llu", + (u_longlong_t)vs->vs_checksum_errors); } else { - snprintf(rbuf, sizeof (rbuf), "-"); + zfs_nicenum(vs->vs_read_errors, rbuf, + sizeof (rbuf)); + zfs_nicenum(vs->vs_write_errors, wbuf, + sizeof (wbuf)); + zfs_nicenum(vs->vs_checksum_errors, cbuf, + sizeof (cbuf)); + fputc(' ', stdout); + printf_color(rcolor, "%5s", rbuf); + fputc(' ', stdout); + printf_color(wcolor, "%5s", wbuf); + fputc(' ', stdout); + printf_color(ccolor, "%5s", cbuf); } + if (cb->cb_print_slow_ios) { + if (children == 0) { + /* Only leafs vdevs have slow IOs */ + zfs_nicenum(vs->vs_slow_ios, rbuf, + sizeof (rbuf)); + } else { + snprintf(rbuf, sizeof (rbuf), "-"); + } - if (cb->cb_literal) - printf(" %5llu", (u_longlong_t)vs->vs_slow_ios); - else - printf(" %5s", rbuf); - } - if (cb->cb_print_power) { - if (children == 0) { + if (cb->cb_literal) + printf(" %5llu", + (u_longlong_t)vs->vs_slow_ios); + else + printf(" %5s", rbuf); + } + if (cb->cb_print_power) { + if (children == 0) { /* Only leaf vdevs have physical slots */ - switch (zpool_power_current_state(zhp, (char *) - fnvlist_lookup_string(nv, - ZPOOL_CONFIG_PATH))) { - case 0: - printf_color(ANSI_RED, " %5s", - gettext("off")); - break; - case 1: - printf(" %5s", gettext("on")); - break; - default: + switch (zpool_power_current_state(zhp, + (char *)fnvlist_lookup_string(nv, + ZPOOL_CONFIG_PATH))) { + case 0: + printf_color(ANSI_RED, " %5s", + gettext("off")); + break; + case 1: + printf(" %5s", gettext("on")); + break; + default: + printf(" %5s", "-"); + } + } else { printf(" %5s", "-"); } - } else { - printf(" %5s", "-"); } } - } - - if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NOT_PRESENT, - ¬present) == 0) { - verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0); - (void) printf(" %s %s", gettext("was"), path); - } else if (vs->vs_aux != 0) { - (void) printf(" "); - color_start(ANSI_RED); - switch (vs->vs_aux) { - case VDEV_AUX_OPEN_FAILED: - (void) printf(gettext("cannot open")); - break; - case VDEV_AUX_BAD_GUID_SUM: - (void) printf(gettext("missing device")); - break; + if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NOT_PRESENT, + ¬present) == 0) { + verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, + &path) == 0); + (void) printf(" %s %s", gettext("was"), path); + } else if (vs->vs_aux != 0) { + (void) printf(" "); + color_start(ANSI_RED); + switch (vs->vs_aux) { + case VDEV_AUX_OPEN_FAILED: + (void) printf(gettext("cannot open")); + break; - case VDEV_AUX_NO_REPLICAS: - (void) printf(gettext("insufficient replicas")); - break; + case VDEV_AUX_BAD_GUID_SUM: + (void) printf(gettext("missing device")); + break; - case VDEV_AUX_VERSION_NEWER: - (void) printf(gettext("newer version")); - break; + case VDEV_AUX_NO_REPLICAS: + (void) printf(gettext("insufficient replicas")); + break; - case VDEV_AUX_UNSUP_FEAT: - (void) printf(gettext("unsupported feature(s)")); - break; + case VDEV_AUX_VERSION_NEWER: + (void) printf(gettext("newer version")); + break; - case VDEV_AUX_ASHIFT_TOO_BIG: - (void) printf(gettext("unsupported minimum blocksize")); - break; + case VDEV_AUX_UNSUP_FEAT: + (void) printf(gettext( + "unsupported feature(s)")); + break; - case VDEV_AUX_SPARED: - verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, - &spare_cb.cb_guid) == 0); - if (zpool_iter(g_zfs, find_spare, &spare_cb) == 1) { - if (strcmp(zpool_get_name(spare_cb.cb_zhp), - zpool_get_name(zhp)) == 0) - (void) printf(gettext("currently in " - "use")); - else - (void) printf(gettext("in use by " - "pool '%s'"), - zpool_get_name(spare_cb.cb_zhp)); - zpool_close(spare_cb.cb_zhp); - } else { - (void) printf(gettext("currently in use")); - } - break; + case VDEV_AUX_ASHIFT_TOO_BIG: + (void) printf(gettext( + "unsupported minimum blocksize")); + break; - case VDEV_AUX_ERR_EXCEEDED: - (void) printf(gettext("too many errors")); - break; + case VDEV_AUX_SPARED: + verify(nvlist_lookup_uint64(nv, + ZPOOL_CONFIG_GUID, + &spare_cb.cb_guid) == 0); + if (zpool_iter(g_zfs, find_spare, + &spare_cb) == 1) { + if (strcmp(zpool_get_name( + spare_cb.cb_zhp), + zpool_get_name(zhp)) == 0) + (void) printf(gettext( + "currently in use")); + else + (void) printf(gettext( + "in use by pool '%s'"), + zpool_get_name( + spare_cb.cb_zhp)); + zpool_close(spare_cb.cb_zhp); + } else { + (void) printf( + gettext("currently in use")); + } + break; - case VDEV_AUX_IO_FAILURE: - (void) printf(gettext("experienced I/O failures")); - break; + case VDEV_AUX_ERR_EXCEEDED: + (void) printf(gettext("too many errors")); + break; - case VDEV_AUX_BAD_LOG: - (void) printf(gettext("bad intent log")); - break; + case VDEV_AUX_IO_FAILURE: + (void) printf( + gettext("experienced I/O failures")); + break; - case VDEV_AUX_EXTERNAL: - (void) printf(gettext("external device fault")); - break; + case VDEV_AUX_BAD_LOG: + (void) printf(gettext("bad intent log")); + break; - case VDEV_AUX_SPLIT_POOL: - (void) printf(gettext("split into new pool")); - break; + case VDEV_AUX_EXTERNAL: + (void) printf(gettext("external device fault")); + break; - case VDEV_AUX_ACTIVE: - (void) printf(gettext("currently in use")); - break; + case VDEV_AUX_SPLIT_POOL: + (void) printf(gettext("split into new pool")); + break; - case VDEV_AUX_CHILDREN_OFFLINE: - (void) printf(gettext("all children offline")); - break; + case VDEV_AUX_ACTIVE: + (void) printf(gettext("currently in use")); + break; - case VDEV_AUX_BAD_LABEL: - (void) printf(gettext("invalid label")); - break; + case VDEV_AUX_CHILDREN_OFFLINE: + (void) printf(gettext("all children offline")); + break; - default: - (void) printf(gettext("corrupted data")); - break; - } - color_end(); - } else if (children == 0 && !isspare && - getenv("ZPOOL_STATUS_NON_NATIVE_ASHIFT_IGNORE") == NULL && - VDEV_STAT_VALID(vs_physical_ashift, vsc) && - vs->vs_configured_ashift < vs->vs_physical_ashift) { - (void) printf( - gettext(" block size: %dB configured, %dB native"), - 1 << vs->vs_configured_ashift, 1 << vs->vs_physical_ashift); - } + case VDEV_AUX_BAD_LABEL: + (void) printf(gettext("invalid label")); + break; - if (vs->vs_scan_removing != 0) { - (void) printf(gettext(" (removing)")); - } else if (VDEV_STAT_VALID(vs_noalloc, vsc) && vs->vs_noalloc != 0) { - (void) printf(gettext(" (non-allocating)")); - } + default: + (void) printf(gettext("corrupted data")); + break; + } + color_end(); + } else if (children == 0 && !isspare && + getenv("ZPOOL_STATUS_NON_NATIVE_ASHIFT_IGNORE") == NULL && + VDEV_STAT_VALID(vs_physical_ashift, vsc) && + vs->vs_configured_ashift < vs->vs_physical_ashift) { + (void) printf( + gettext(" block size: %dB configured, %dB native"), + 1 << vs->vs_configured_ashift, + 1 << vs->vs_physical_ashift); + } + + if (vs->vs_scan_removing != 0) { + (void) printf(gettext(" (removing)")); + } else if (VDEV_STAT_VALID(vs_noalloc, vsc) && + vs->vs_noalloc != 0) { + (void) printf(gettext(" (non-allocating)")); + } + + /* The root vdev has the scrub/resilver stats */ + root = fnvlist_lookup_nvlist(zpool_get_config(zhp, NULL), + ZPOOL_CONFIG_VDEV_TREE); + (void) nvlist_lookup_uint64_array(root, ZPOOL_CONFIG_SCAN_STATS, + (uint64_t **)&ps, &c); - /* The root vdev has the scrub/resilver stats */ - root = fnvlist_lookup_nvlist(zpool_get_config(zhp, NULL), - ZPOOL_CONFIG_VDEV_TREE); - (void) nvlist_lookup_uint64_array(root, ZPOOL_CONFIG_SCAN_STATS, - (uint64_t **)&ps, &c); + /* + * If you force fault a drive that's resilvering, its scan stats + * can get frozen in time, giving the false impression that it's + * being resilvered. That's why we check the state to see if the + * vdev is healthy before reporting "resilvering" or "repairing" + */ + if (ps != NULL && ps->pss_state == DSS_SCANNING && + children == 0 && + vs->vs_state == VDEV_STATE_HEALTHY) { + if (vs->vs_scan_processed != 0) { + (void) printf(gettext(" (%s)"), + (ps->pss_func == POOL_SCAN_RESILVER) ? + "resilvering" : "repairing"); + } else if (vs->vs_resilver_deferred) { + (void) printf(gettext(" (awaiting resilver)")); + } + } - /* - * If you force fault a drive that's resilvering, its scan stats can - * get frozen in time, giving the false impression that it's - * being resilvered. That's why we check the state to see if the vdev - * is healthy before reporting "resilvering" or "repairing". - */ - if (ps != NULL && ps->pss_state == DSS_SCANNING && children == 0 && - vs->vs_state == VDEV_STATE_HEALTHY) { - if (vs->vs_scan_processed != 0) { - (void) printf(gettext(" (%s)"), - (ps->pss_func == POOL_SCAN_RESILVER) ? - "resilvering" : "repairing"); - } else if (vs->vs_resilver_deferred) { - (void) printf(gettext(" (awaiting resilver)")); + /* The top-level vdevs have the rebuild stats */ + if (vrs != NULL && vrs->vrs_state == VDEV_REBUILD_ACTIVE && + children == 0 && vs->vs_state == VDEV_STATE_HEALTHY) { + if (vs->vs_rebuild_processed != 0) { + (void) printf(gettext(" (resilvering)")); + } } - } - /* The top-level vdevs have the rebuild stats */ - if (vrs != NULL && vrs->vrs_state == VDEV_REBUILD_ACTIVE && - children == 0 && vs->vs_state == VDEV_STATE_HEALTHY) { - if (vs->vs_rebuild_processed != 0) { - (void) printf(gettext(" (resilvering)")); + if (cb->vcdl != NULL) { + if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, + &path) == 0) { + printf(" "); + zpool_print_cmd(cb->vcdl, + zpool_get_name(zhp), path); + } } - } - if (cb->vcdl != NULL) { - if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0) { - printf(" "); - zpool_print_cmd(cb->vcdl, zpool_get_name(zhp), path); + /* Display vdev initialization and trim status for leaves. */ + if (children == 0) { + print_status_initialize(vs, cb->cb_print_vdev_init); + print_status_trim(vs, cb->cb_print_vdev_trim); } - } - /* Display vdev initialization and trim status for leaves. */ - if (children == 0) { - print_status_initialize(vs, cb->cb_print_vdev_init); - print_status_trim(vs, cb->cb_print_vdev_trim); + (void) printf("\n"); } - - (void) printf("\n"); - for (c = 0; c < children; c++) { uint64_t islog = B_FALSE, ishole = B_FALSE; @@ -9106,9 +9171,11 @@ status_callback(zpool_handle_t *zhp, void *data) (void) printf(gettext( "errors: No known data errors\n")); } else if (!cbp->cb_verbose) { + color_start(ANSI_RED); (void) printf(gettext("errors: %llu data " "errors, use '-v' for a list\n"), (u_longlong_t)nerr); + color_end(); } else { print_error_log(zhp); } @@ -9129,6 +9196,7 @@ status_callback(zpool_handle_t *zhp, void *data) * [pool] [interval [count]] * * -c CMD For each vdev, run command CMD + * -e Display only unhealthy vdevs * -i Display vdev initialization status. * -g Display guid for individual vdev name. * -L Follow links when resolving vdev path name. @@ -9160,7 +9228,7 @@ zpool_do_status(int argc, char **argv) }; /* check options */ - while ((c = getopt_long(argc, argv, "c:igLpPsvxDtT:", long_options, + while ((c = getopt_long(argc, argv, "c:eigLpPsvxDtT:", long_options, NULL)) != -1) { switch (c) { case 'c': @@ -9187,6 +9255,9 @@ zpool_do_status(int argc, char **argv) } cmd = optarg; break; + case 'e': + cb.cb_print_unhealthy = B_TRUE; + break; case 'i': cb.cb_print_vdev_init = B_TRUE; break; diff --git a/man/man8/zpool-status.8 b/man/man8/zpool-status.8 index 965bf9d2b5e2..019a6898d1ac 100644 --- a/man/man8/zpool-status.8 +++ b/man/man8/zpool-status.8 @@ -36,7 +36,7 @@ .Sh SYNOPSIS .Nm zpool .Cm status -.Op Fl DigLpPstvx +.Op Fl DeigLpPstvx .Op Fl T Sy u Ns | Ns Sy d .Op Fl c Op Ar SCRIPT1 Ns Oo , Ns Ar SCRIPT2 Oc Ns … .Oo Ar pool Oc Ns … @@ -69,6 +69,8 @@ See the option of .Nm zpool Cm iostat for complete details. +.It Fl e +Only show unhealthy VDevs (not-ONLINE or with errors) .It Fl i Display vdev initialization status. .It Fl g