X-Git-Url: https://git.camperquake.de/gitweb.cgi?a=blobdiff_plain;f=cmd%2Fzpool%2Fzpool_main.c;h=62c4be832b8bdf2725471a1c2a8cbbc61c8c2277;hb=428870ff734fdaccc342b33fc53cf94724409a46;hp=42c6aabb63fdff98ac77abd052c35459be72c326;hpb=d164b2093561a9771db07346e6fffc9ca19427a2;p=zfs.git diff --git a/cmd/zpool/zpool_main.c b/cmd/zpool/zpool_main.c index 42c6aab..62c4be8 100644 --- a/cmd/zpool/zpool_main.c +++ b/cmd/zpool/zpool_main.c @@ -20,8 +20,7 @@ */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. */ #include @@ -42,7 +41,6 @@ #include #include #include - #include #include @@ -50,6 +48,8 @@ #include "zpool_util.h" #include "zfs_comutil.h" +#include "statcommon.h" + static int zpool_do_create(int, char **); static int zpool_do_destroy(int, char **); @@ -67,6 +67,7 @@ static int zpool_do_clear(int, char **); static int zpool_do_attach(int, char **); static int zpool_do_detach(int, char **); static int zpool_do_replace(int, char **); +static int zpool_do_split(int, char **); static int zpool_do_scrub(int, char **); @@ -119,7 +120,8 @@ typedef enum { HELP_STATUS, HELP_UPGRADE, HELP_GET, - HELP_SET + HELP_SET, + HELP_SPLIT } zpool_help_t; @@ -156,6 +158,7 @@ static zpool_command_t command_table[] = { { "attach", zpool_do_attach, HELP_ATTACH }, { "detach", zpool_do_detach, HELP_DETACH }, { "replace", zpool_do_replace, HELP_REPLACE }, + { "split", zpool_do_split, HELP_SPLIT }, { NULL }, { "scrub", zpool_do_scrub, HELP_SCRUB }, { NULL }, @@ -173,6 +176,8 @@ static zpool_command_t command_table[] = { zpool_command_t *current_command; static char history_str[HIS_MAX_RECORD_LEN]; +static uint_t timestamp_fmt = NODATE; + static const char * get_usage(zpool_help_t idx) { switch (idx) { @@ -182,7 +187,7 @@ get_usage(zpool_help_t idx) { return (gettext("\tattach [-f] " "\n")); case HELP_CLEAR: - return (gettext("\tclear [device]\n")); + return (gettext("\tclear [-nF] [device]\n")); case HELP_CREATE: return (gettext("\tcreate [-fn] [-o property=value] ... \n" "\t [-O file-system-property=value] ... \n" @@ -197,17 +202,18 @@ get_usage(zpool_help_t idx) { return (gettext("\thistory [-il] [] ...\n")); case HELP_IMPORT: return (gettext("\timport [-d dir] [-D]\n" + "\timport [-d dir | -c cachefile] [-n] -F \n" "\timport [-o mntopts] [-o property=value] ... \n" "\t [-d dir | -c cachefile] [-D] [-f] [-R root] -a\n" "\timport [-o mntopts] [-o property=value] ... \n" "\t [-d dir | -c cachefile] [-D] [-f] [-R root] " " [newpool]\n")); case HELP_IOSTAT: - return (gettext("\tiostat [-v] [pool] ... [interval " + return (gettext("\tiostat [-v] [-T d|u] [pool] ... [interval " "[count]]\n")); case HELP_LIST: return (gettext("\tlist [-H] [-o property[,...]] " - "[pool] ...\n")); + "[-T d|u] [pool] ... [interval [count]]\n")); case HELP_OFFLINE: return (gettext("\toffline [-t] ...\n")); case HELP_ONLINE: @@ -220,7 +226,8 @@ get_usage(zpool_help_t idx) { case HELP_SCRUB: return (gettext("\tscrub [-s] ...\n")); case HELP_STATUS: - return (gettext("\tstatus [-vx] [pool] ...\n")); + return (gettext("\tstatus [-vx] [-T d|u] [pool] ... [interval " + "[count]]\n")); case HELP_UPGRADE: return (gettext("\tupgrade\n" "\tupgrade -v\n" @@ -230,6 +237,10 @@ get_usage(zpool_help_t idx) { " ...\n")); case HELP_SET: return (gettext("\tset \n")); + case HELP_SPLIT: + return (gettext("\tsplit [-n] [-R altroot] [-o mntopts]\n" + "\t [-o property=value] " + "[ ...]\n")); } abort(); @@ -245,12 +256,12 @@ print_prop_cb(int prop, void *cb) { FILE *fp = cb; - (void) fprintf(fp, "\t%-13s ", zpool_prop_to_name(prop)); + (void) fprintf(fp, "\t%-15s ", zpool_prop_to_name(prop)); if (zpool_prop_readonly(prop)) (void) fprintf(fp, " NO "); else - (void) fprintf(fp, " YES "); + (void) fprintf(fp, " YES "); if (zpool_prop_values(prop) == NULL) (void) fprintf(fp, "-\n"); @@ -297,7 +308,7 @@ usage(boolean_t requested) (void) fprintf(fp, gettext("\nthe following properties are supported:\n")); - (void) fprintf(fp, "\n\t%-13s %s %s\n\n", + (void) fprintf(fp, "\n\t%-15s %s %s\n\n", "PROPERTY", "EDIT", "VALUES"); /* Iterate over all properties */ @@ -339,7 +350,7 @@ print_vdev_tree(zpool_handle_t *zhp, const char *name, nvlist_t *nv, int indent, if ((is_log && !print_logs) || (!is_log && print_logs)) continue; - vname = zpool_vdev_name(g_zfs, zhp, child[c]); + vname = zpool_vdev_name(g_zfs, zhp, child[c], B_FALSE); print_vdev_tree(zhp, vname, child[c], indent + 2, B_FALSE); free(vname); @@ -376,12 +387,11 @@ add_prop_list(const char *propname, char *propval, nvlist_t **props, } normnm = zpool_prop_to_name(prop); } else { - if ((fprop = zfs_name_to_prop(propname)) == ZPROP_INVAL) { - (void) fprintf(stderr, gettext("property '%s' is " - "not a valid file system property\n"), propname); - return (2); + if ((fprop = zfs_name_to_prop(propname)) != ZPROP_INVAL) { + normnm = zfs_prop_to_name(fprop); + } else { + normnm = propname; } - normnm = zfs_prop_to_name(fprop); } if (nvlist_lookup_string(proplist, normnm, &strval) == 0 && @@ -508,11 +518,10 @@ zpool_do_add(int argc, char **argv) } /* - * zpool remove ... + * zpool remove ... * - * Removes the given vdev from the pool. Currently, this only supports removing - * spares and cache devices from the pool. Eventually, we'll want to support - * removing leaf vdevs (as an alias for 'detach') as well as toplevel vdevs. + * Removes the given vdev from the pool. Currently, this supports removing + * spares, cache, and log devices from the pool. */ int zpool_do_remove(int argc, char **argv) @@ -941,7 +950,7 @@ zpool_do_export(int argc, char **argv) static int max_width(zpool_handle_t *zhp, nvlist_t *nv, int depth, int max) { - char *name = zpool_vdev_name(g_zfs, zhp, nv); + char *name = zpool_vdev_name(g_zfs, zhp, nv, B_TRUE); nvlist_t **child; uint_t c, children; int ret; @@ -979,14 +988,202 @@ max_width(zpool_handle_t *zhp, nvlist_t *nv, int depth, int max) return (max); } +typedef struct spare_cbdata { + uint64_t cb_guid; + zpool_handle_t *cb_zhp; +} spare_cbdata_t; + +static boolean_t +find_vdev(nvlist_t *nv, uint64_t search) +{ + uint64_t guid; + nvlist_t **child; + uint_t c, children; + + if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &guid) == 0 && + search == guid) + return (B_TRUE); + + if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, + &child, &children) == 0) { + for (c = 0; c < children; c++) + if (find_vdev(child[c], search)) + return (B_TRUE); + } + + return (B_FALSE); +} + +static int +find_spare(zpool_handle_t *zhp, void *data) +{ + spare_cbdata_t *cbp = data; + nvlist_t *config, *nvroot; + + config = zpool_get_config(zhp, NULL); + verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, + &nvroot) == 0); + + if (find_vdev(nvroot, cbp->cb_guid)) { + cbp->cb_zhp = zhp; + return (1); + } + + zpool_close(zhp); + return (0); +} + +/* + * Print out configuration state as requested by status_callback. + */ +void +print_status_config(zpool_handle_t *zhp, const char *name, nvlist_t *nv, + int namewidth, int depth, boolean_t isspare) +{ + nvlist_t **child; + uint_t c, children; + pool_scan_stat_t *ps = NULL; + vdev_stat_t *vs; + char rbuf[6], wbuf[6], cbuf[6]; + char *vname; + uint64_t notpresent; + spare_cbdata_t cb; + char *state; + + if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, + &child, &children) != 0) + children = 0; + + verify(nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_VDEV_STATS, + (uint64_t **)&vs, &c) == 0); + + state = zpool_state_to_name(vs->vs_state, vs->vs_aux); + if (isspare) { + /* + * For hot spares, we use the terms 'INUSE' and 'AVAILABLE' for + * online drives. + */ + if (vs->vs_aux == VDEV_AUX_SPARED) + state = "INUSE"; + else if (vs->vs_state == VDEV_STATE_HEALTHY) + state = "AVAIL"; + } + + (void) printf("\t%*s%-*s %-8s", depth, "", namewidth - depth, + name, state); + + if (!isspare) { + 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)); + (void) printf(" %5s %5s %5s", rbuf, wbuf, cbuf); + } + + if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NOT_PRESENT, + ¬present) == 0) { + char *path; + verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0); + (void) printf(" was %s", path); + } else if (vs->vs_aux != 0) { + (void) printf(" "); + + 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; + + case VDEV_AUX_NO_REPLICAS: + (void) printf(gettext("insufficient replicas")); + break; + + case VDEV_AUX_VERSION_NEWER: + (void) printf(gettext("newer version")); + break; + + case VDEV_AUX_SPARED: + verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, + &cb.cb_guid) == 0); + if (zpool_iter(g_zfs, find_spare, &cb) == 1) { + if (strcmp(zpool_get_name(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(cb.cb_zhp)); + zpool_close(cb.cb_zhp); + } else { + (void) printf(gettext("currently in use")); + } + break; + + case VDEV_AUX_ERR_EXCEEDED: + (void) printf(gettext("too many errors")); + break; + + case VDEV_AUX_IO_FAILURE: + (void) printf(gettext("experienced I/O failures")); + break; + + case VDEV_AUX_BAD_LOG: + (void) printf(gettext("bad intent log")); + break; + + case VDEV_AUX_EXTERNAL: + (void) printf(gettext("external device fault")); + break; + + case VDEV_AUX_SPLIT_POOL: + (void) printf(gettext("split into new pool")); + break; + + default: + (void) printf(gettext("corrupted data")); + break; + } + } + + (void) nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_SCAN_STATS, + (uint64_t **)&ps, &c); + + if (ps && ps->pss_state == DSS_SCANNING && + vs->vs_scan_processed != 0 && children == 0) { + (void) printf(gettext(" (%s)"), + (ps->pss_func == POOL_SCAN_RESILVER) ? + "resilvering" : "repairing"); + } + + (void) printf("\n"); + + for (c = 0; c < children; c++) { + uint64_t islog = B_FALSE, ishole = B_FALSE; + + /* Don't print logs or holes here */ + (void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG, + &islog); + (void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_HOLE, + &ishole); + if (islog || ishole) + continue; + vname = zpool_vdev_name(g_zfs, zhp, child[c], B_TRUE); + print_status_config(zhp, vname, child[c], + namewidth, depth + 2, isspare); + free(vname); + } +} + /* * Print the configuration of an exported pool. Iterate over all vdevs in the * pool, printing out the name and status for each one. */ void -print_import_config(const char *name, nvlist_t *nv, int namewidth, int depth, - boolean_t print_logs) +print_import_config(const char *name, nvlist_t *nv, int namewidth, int depth) { nvlist_t **child; uint_t c, children; @@ -994,10 +1191,11 @@ print_import_config(const char *name, nvlist_t *nv, int namewidth, int depth, char *type, *vname; verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) == 0); - if (strcmp(type, VDEV_TYPE_MISSING) == 0) + if (strcmp(type, VDEV_TYPE_MISSING) == 0 || + strcmp(type, VDEV_TYPE_HOLE) == 0) return; - verify(nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_STATS, + verify(nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_VDEV_STATS, (uint64_t **)&vs, &c) == 0); (void) printf("\t%*s%-*s", depth, "", namewidth - depth, name); @@ -1043,12 +1241,11 @@ print_import_config(const char *name, nvlist_t *nv, int namewidth, int depth, (void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG, &is_log); - if ((is_log && !print_logs) || (!is_log && print_logs)) + if (is_log) continue; - vname = zpool_vdev_name(g_zfs, NULL, child[c]); - print_import_config(vname, child[c], - namewidth, depth + 2, B_FALSE); + vname = zpool_vdev_name(g_zfs, NULL, child[c], B_TRUE); + print_import_config(vname, child[c], namewidth, depth + 2); free(vname); } @@ -1056,7 +1253,7 @@ print_import_config(const char *name, nvlist_t *nv, int namewidth, int depth, &child, &children) == 0) { (void) printf(gettext("\tcache\n")); for (c = 0; c < children; c++) { - vname = zpool_vdev_name(g_zfs, NULL, child[c]); + vname = zpool_vdev_name(g_zfs, NULL, child[c], B_FALSE); (void) printf("\t %s\n", vname); free(vname); } @@ -1066,7 +1263,7 @@ print_import_config(const char *name, nvlist_t *nv, int namewidth, int depth, &child, &children) == 0) { (void) printf(gettext("\tspares\n")); for (c = 0; c < children; c++) { - vname = zpool_vdev_name(g_zfs, NULL, child[c]); + vname = zpool_vdev_name(g_zfs, NULL, child[c], B_FALSE); (void) printf("\t %s\n", vname); free(vname); } @@ -1074,6 +1271,44 @@ print_import_config(const char *name, nvlist_t *nv, int namewidth, int depth, } /* + * Print log vdevs. + * Logs are recorded as top level vdevs in the main pool child array + * but with "is_log" set to 1. We use either print_status_config() or + * print_import_config() to print the top level logs then any log + * children (eg mirrored slogs) are printed recursively - which + * works because only the top level vdev is marked "is_log" + */ +static void +print_logs(zpool_handle_t *zhp, nvlist_t *nv, int namewidth, boolean_t verbose) +{ + uint_t c, children; + nvlist_t **child; + + if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, &child, + &children) != 0) + return; + + (void) printf(gettext("\tlogs\n")); + + for (c = 0; c < children; c++) { + uint64_t is_log = B_FALSE; + char *name; + + (void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG, + &is_log); + if (!is_log) + continue; + name = zpool_vdev_name(g_zfs, zhp, child[c], B_TRUE); + if (verbose) + print_status_config(zhp, name, child[c], namewidth, + 2, B_FALSE); + else + print_import_config(name, child[c], namewidth, 2); + free(name); + } +} + +/* * Display the status for the given pool. */ static void @@ -1099,7 +1334,7 @@ show_import(nvlist_t *config) verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0); - verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_STATS, + verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_VDEV_STATS, (uint64_t **)&vs, &vsc) == 0); health = zpool_state_to_name(vs->vs_state, vs->vs_aux); @@ -1166,6 +1401,11 @@ show_import(nvlist_t *config) "read.\n")); break; + case ZPOOL_STATUS_RESILVERING: + (void) printf(gettext("status: One or more devices were being " + "resilvered.\n")); + break; + default: /* * No other status can be seen when importing pools. @@ -1241,11 +1481,9 @@ show_import(nvlist_t *config) if (namewidth < 10) namewidth = 10; - print_import_config(name, nvroot, namewidth, 0, B_FALSE); - if (num_logs(nvroot) > 0) { - (void) printf(gettext("\tlogs\n")); - print_import_config(name, nvroot, namewidth, 0, B_TRUE); - } + print_import_config(name, nvroot, namewidth, 0); + if (num_logs(nvroot) > 0) + print_logs(NULL, nvroot, namewidth, B_FALSE); if (reason == ZPOOL_STATUS_BAD_GUID_SUM) { (void) printf(gettext("\n\tAdditional devices are known to " @@ -1261,13 +1499,12 @@ show_import(nvlist_t *config) */ static int do_import(nvlist_t *config, const char *newname, const char *mntopts, - int force, nvlist_t *props, boolean_t allowfaulted) + int force, nvlist_t *props, boolean_t do_verbatim) { zpool_handle_t *zhp; char *name; uint64_t state; uint64_t version; - int error = 0; verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME, &name) == 0); @@ -1314,14 +1551,14 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts, } } - if (zpool_import_props(g_zfs, config, newname, props, - allowfaulted) != 0) + if (zpool_import_props(g_zfs, config, newname, props, do_verbatim) != 0) return (1); if (newname != NULL) name = (char *)newname; - verify((zhp = zpool_open_canfail(g_zfs, name)) != NULL); + if ((zhp = zpool_open_canfail(g_zfs, name)) == NULL) + return (1); if (zpool_get_state(zhp) != POOL_STATE_UNAVAIL && zpool_enable_datasets(zhp, mntopts, 0) != 0) { @@ -1330,7 +1567,7 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts, } zpool_close(zhp); - return (error); + return (0); } /* @@ -1338,7 +1575,7 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts, * import [-o mntopts] [-o prop=value] ... [-R root] [-D] * [-d dir | -c cachefile] [-f] -a * import [-o mntopts] [-o prop=value] ... [-R root] [-D] - * [-d dir | -c cachefile] [-f] [newpool] + * [-d dir | -c cachefile] [-f] [-n] [-F] [newpool] * * -c Read pool information from a cachefile instead of searching * devices. @@ -1353,12 +1590,17 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts, * the given root. The pool will remain exported when the machine * is rebooted. * - * -f Force import, even if it appears that the pool is active. - * - * -F Import even in the presence of faulted vdevs. This is an + * -V Import even in the presence of faulted vdevs. This is an * intentionally undocumented option for testing purposes, and * treats the pool configuration as complete, leaving any bad - * vdevs in the FAULTED state. + * vdevs in the FAULTED state. In other words, it does verbatim + * import. + * + * -f Force import, even if it appears that the pool is active. + * + * -F Attempt rewind if necessary. + * + * -n See if rewind would work, but don't actually rewind. * * -a Import all pools found. * @@ -1373,7 +1615,7 @@ zpool_do_import(int argc, char **argv) char **searchdirs = NULL; int nsearch = 0; int c; - int err; + int err = 0; nvlist_t *pools = NULL; boolean_t do_all = B_FALSE; boolean_t do_destroyed = B_FALSE; @@ -1385,14 +1627,20 @@ zpool_do_import(int argc, char **argv) char *searchname = NULL; char *propval; nvlist_t *found_config; + nvlist_t *policy = NULL; nvlist_t *props = NULL; boolean_t first; - boolean_t allow_faulted = B_FALSE; + boolean_t do_verbatim = B_FALSE; + uint32_t rewind_policy = ZPOOL_NO_REWIND; + boolean_t dryrun = B_FALSE; + boolean_t do_rewind = B_FALSE; + boolean_t xtreme_rewind = B_FALSE; uint64_t pool_state; char *cachefile = NULL; + importargs_t idata = { 0 }; /* check options */ - while ((c = getopt(argc, argv, ":ac:d:DfFo:p:R:")) != -1) { + while ((c = getopt(argc, argv, ":aCc:d:DEfFno:rR:VX")) != -1) { switch (c) { case 'a': do_all = B_TRUE; @@ -1420,7 +1668,10 @@ zpool_do_import(int argc, char **argv) do_force = B_TRUE; break; case 'F': - allow_faulted = B_TRUE; + do_rewind = B_TRUE; + break; + case 'n': + dryrun = B_TRUE; break; case 'o': if ((propval = strchr(optarg, '=')) != NULL) { @@ -1445,6 +1696,12 @@ zpool_do_import(int argc, char **argv) ZPOOL_PROP_CACHEFILE), "none", &props, B_TRUE)) goto error; break; + case 'V': + do_verbatim = B_TRUE; + break; + case 'X': + xtreme_rewind = B_TRUE; + break; case ':': (void) fprintf(stderr, gettext("missing argument for " "'%c' option\n"), optopt); @@ -1465,6 +1722,23 @@ zpool_do_import(int argc, char **argv) usage(B_FALSE); } + if ((dryrun || xtreme_rewind) && !do_rewind) { + (void) fprintf(stderr, + gettext("-n or -X only meaningful with -F\n")); + usage(B_FALSE); + } + if (dryrun) + rewind_policy = ZPOOL_TRY_REWIND; + else if (do_rewind) + rewind_policy = ZPOOL_DO_REWIND; + if (xtreme_rewind) + rewind_policy |= ZPOOL_EXTREME_REWIND; + + /* In the future, we can capture further policy and include it here */ + if (nvlist_alloc(&policy, NV_UNIQUE_NAME, 0) != 0 || + nvlist_add_uint32(policy, ZPOOL_REWIND_REQUEST, rewind_policy) != 0) + goto error; + if (searchdirs == NULL) { searchdirs = safe_malloc(sizeof (char *)); searchdirs[0] = "/dev/dsk"; @@ -1492,6 +1766,7 @@ zpool_do_import(int argc, char **argv) (void) fprintf(stderr, gettext("cannot " "discover pools: permission denied\n")); free(searchdirs); + nvlist_free(policy); return (1); } } @@ -1517,28 +1792,49 @@ zpool_do_import(int argc, char **argv) if (errno != 0 || *endptr != '\0') searchname = argv[0]; found_config = NULL; - } - if (cachefile) { - pools = zpool_find_import_cached(g_zfs, cachefile, searchname, - searchguid); - } else if (searchname != NULL) { - pools = zpool_find_import_byname(g_zfs, nsearch, searchdirs, - searchname); - } else { /* - * It's OK to search by guid even if searchguid is 0. + * User specified a name or guid. Ensure it's unique. */ - pools = zpool_find_import_byguid(g_zfs, nsearch, searchdirs, - searchguid); - } - - if (pools == NULL) { + idata.unique = B_TRUE; + } + + + idata.path = searchdirs; + idata.paths = nsearch; + idata.poolname = searchname; + idata.guid = searchguid; + idata.cachefile = cachefile; + + pools = zpool_search_import(g_zfs, &idata); + + if (pools != NULL && idata.exists && + (argc == 1 || strcmp(argv[0], argv[1]) == 0)) { + (void) fprintf(stderr, gettext("cannot import '%s': " + "a pool with that name already exists\n"), + argv[0]); + (void) fprintf(stderr, gettext("use the form '%s " + " ' to give it a new name\n"), + "zpool import"); + err = 1; + } else if (pools == NULL && idata.exists) { + (void) fprintf(stderr, gettext("cannot import '%s': " + "a pool with that name is already created/imported,\n"), + argv[0]); + (void) fprintf(stderr, gettext("and no additional pools " + "with that name were found\n")); + err = 1; + } else if (pools == NULL) { if (argc != 0) { (void) fprintf(stderr, gettext("cannot import '%s': " "no such pool available\n"), argv[0]); } + err = 1; + } + + if (err == 1) { free(searchdirs); + nvlist_free(policy); return (1); } @@ -1562,17 +1858,21 @@ zpool_do_import(int argc, char **argv) if (do_destroyed && pool_state != POOL_STATE_DESTROYED) continue; + verify(nvlist_add_nvlist(config, ZPOOL_REWIND_POLICY, + policy) == 0); + if (argc == 0) { if (first) first = B_FALSE; else if (!do_all) (void) printf("\n"); - if (do_all) + if (do_all) { err |= do_import(config, NULL, mntopts, - do_force, props, allow_faulted); - else + do_force, props, do_verbatim); + } else { show_import(config); + } } else if (searchname != NULL) { char *name; @@ -1618,7 +1918,7 @@ zpool_do_import(int argc, char **argv) err = B_TRUE; } else { err |= do_import(found_config, argc == 1 ? NULL : - argv[1], mntopts, do_force, props, allow_faulted); + argv[1], mntopts, do_force, props, do_verbatim); } } @@ -1633,6 +1933,7 @@ zpool_do_import(int argc, char **argv) error: nvlist_free(props); nvlist_free(pools); + nvlist_free(policy); free(searchdirs); return (err ? 1 : 0); @@ -1660,7 +1961,7 @@ print_iostat_header(iostat_cbdata_t *cb) { (void) printf("%*s capacity operations bandwidth\n", cb->cb_namewidth, ""); - (void) printf("%-*s used avail read write read write\n", + (void) printf("%-*s alloc free read write read write\n", cb->cb_namewidth, "pool"); print_iostat_separator(cb); } @@ -1695,13 +1996,13 @@ print_vdev_stats(zpool_handle_t *zhp, const char *name, nvlist_t *oldnv, char *vname; if (oldnv != NULL) { - verify(nvlist_lookup_uint64_array(oldnv, ZPOOL_CONFIG_STATS, - (uint64_t **)&oldvs, &c) == 0); + verify(nvlist_lookup_uint64_array(oldnv, + ZPOOL_CONFIG_VDEV_STATS, (uint64_t **)&oldvs, &c) == 0); } else { oldvs = &zerovs; } - verify(nvlist_lookup_uint64_array(newnv, ZPOOL_CONFIG_STATS, + verify(nvlist_lookup_uint64_array(newnv, ZPOOL_CONFIG_VDEV_STATS, (uint64_t **)&newvs, &c) == 0); if (strlen(name) + depth > cb->cb_namewidth) @@ -1751,7 +2052,13 @@ print_vdev_stats(zpool_handle_t *zhp, const char *name, nvlist_t *oldnv, return; for (c = 0; c < children; c++) { - vname = zpool_vdev_name(g_zfs, zhp, newchild[c]); + uint64_t ishole = B_FALSE; + + if (nvlist_lookup_uint64(newchild[c], + ZPOOL_CONFIG_IS_HOLE, &ishole) == 0 && ishole) + continue; + + vname = zpool_vdev_name(g_zfs, zhp, newchild[c], B_FALSE); print_vdev_stats(zhp, vname, oldnv ? oldchild[c] : NULL, newchild[c], cb, depth + 2); free(vname); @@ -1772,7 +2079,8 @@ print_vdev_stats(zpool_handle_t *zhp, const char *name, nvlist_t *oldnv, (void) printf("%-*s - - - - - " "-\n", cb->cb_namewidth, "cache"); for (c = 0; c < children; c++) { - vname = zpool_vdev_name(g_zfs, zhp, newchild[c]); + vname = zpool_vdev_name(g_zfs, zhp, newchild[c], + B_FALSE); print_vdev_stats(zhp, vname, oldnv ? oldchild[c] : NULL, newchild[c], cb, depth + 2); free(vname); @@ -1861,42 +2169,14 @@ get_namewidth(zpool_handle_t *zhp, void *data) } /* - * zpool iostat [-v] [pool] ... [interval [count]] - * - * -v Display statistics for individual vdevs - * - * This command can be tricky because we want to be able to deal with pool - * creation/destruction as well as vdev configuration changes. The bulk of this - * processing is handled by the pool_list_* routines in zpool_iter.c. We rely - * on pool_list_update() to detect the addition of new pools. Configuration - * changes are all handled within libzfs. + * Parse the input string, get the 'interval' and 'count' value if there is one. */ -int -zpool_do_iostat(int argc, char **argv) +static void +get_interval_count(int *argcp, char **argv, unsigned long *iv, + unsigned long *cnt) { - int c; - int ret; - int npools; unsigned long interval = 0, count = 0; - zpool_list_t *list; - boolean_t verbose = B_FALSE; - iostat_cbdata_t cb; - - /* check options */ - while ((c = getopt(argc, argv, "v")) != -1) { - switch (c) { - case 'v': - verbose = B_TRUE; - break; - case '?': - (void) fprintf(stderr, gettext("invalid option '%c'\n"), - optopt); - usage(B_FALSE); - } - } - - argc -= optind; - argv += optind; + int argc = *argcp, errno; /* * Determine if the last argument is an integer or a pool name @@ -1913,7 +2193,6 @@ zpool_do_iostat(int argc, char **argv) "cannot be zero\n")); usage(B_FALSE); } - /* * Ignore the last parameter */ @@ -1930,7 +2209,7 @@ zpool_do_iostat(int argc, char **argv) /* * If the last argument is also an integer, then we have both a count - * and an integer. + * and an interval. */ if (argc > 0 && isdigit(argv[argc - 1][0])) { char *end; @@ -1955,6 +2234,66 @@ zpool_do_iostat(int argc, char **argv) } } + *iv = interval; + *cnt = count; + *argcp = argc; +} + +static void +get_timestamp_arg(char c) +{ + if (c == 'u') + timestamp_fmt = UDATE; + else if (c == 'd') + timestamp_fmt = DDATE; + else + usage(B_FALSE); +} + +/* + * zpool iostat [-v] [-T d|u] [pool] ... [interval [count]] + * + * -v Display statistics for individual vdevs + * -T Display a timestamp in date(1) or Unix format + * + * This command can be tricky because we want to be able to deal with pool + * creation/destruction as well as vdev configuration changes. The bulk of this + * processing is handled by the pool_list_* routines in zpool_iter.c. We rely + * on pool_list_update() to detect the addition of new pools. Configuration + * changes are all handled within libzfs. + */ +int +zpool_do_iostat(int argc, char **argv) +{ + int c; + int ret; + int npools; + unsigned long interval = 0, count = 0; + zpool_list_t *list; + boolean_t verbose = B_FALSE; + iostat_cbdata_t cb; + + /* check options */ + while ((c = getopt(argc, argv, "T:v")) != -1) { + switch (c) { + case 'T': + get_timestamp_arg(*optarg); + break; + case 'v': + verbose = B_TRUE; + break; + case '?': + (void) fprintf(stderr, gettext("invalid option '%c'\n"), + optopt); + usage(B_FALSE); + } + } + + argc -= optind; + argv += optind; + + get_interval_count(&argc, argv, &interval, &count); + /* * Construct the list of all interesting pools. */ @@ -2001,6 +2340,9 @@ zpool_do_iostat(int argc, char **argv) cb.cb_namewidth = 0; (void) pool_list_iter(list, B_FALSE, get_namewidth, &cb); + if (timestamp_fmt != NODATE) + print_timestamp(timestamp_fmt); + /* * If it's the first time, or verbose mode, print the header. */ @@ -2152,12 +2494,13 @@ list_callback(zpool_handle_t *zhp, void *data) } /* - * zpool list [-H] [-o prop[,prop]*] [pool] ... + * zpool list [-H] [-o prop[,prop]*] [-T d|u] [pool] ... [interval [count]] * * -H Scripted mode. Don't display headers, and separate properties * by a single tab. * -o List of properties to display. Defaults to - * "name,size,used,available,capacity,health,altroot" + * "name,size,allocated,free,capacity,health,altroot" + * -T Display a timestamp in date(1) or Unix format * * List all pools in the system, whether or not they're healthy. Output space * statistics for each one, as well as health status summary. @@ -2169,11 +2512,12 @@ zpool_do_list(int argc, char **argv) int ret; list_cbdata_t cb = { 0 }; static char default_props[] = - "name,size,used,available,capacity,health,altroot"; + "name,size,allocated,free,capacity,dedupratio,health,altroot"; char *props = default_props; + unsigned long interval = 0, count = 0; /* check options */ - while ((c = getopt(argc, argv, ":Ho:")) != -1) { + while ((c = getopt(argc, argv, ":Ho:T:")) != -1) { switch (c) { case 'H': cb.cb_scripted = B_TRUE; @@ -2181,6 +2525,9 @@ zpool_do_list(int argc, char **argv) case 'o': props = optarg; break; + case 'T': + get_timestamp_arg(*optarg); + break; case ':': (void) fprintf(stderr, gettext("missing argument for " "'%c' option\n"), optopt); @@ -2196,21 +2543,37 @@ zpool_do_list(int argc, char **argv) argc -= optind; argv += optind; + get_interval_count(&argc, argv, &interval, &count); + if (zprop_get_list(g_zfs, props, &cb.cb_proplist, ZFS_TYPE_POOL) != 0) usage(B_FALSE); cb.cb_first = B_TRUE; - ret = for_each_pool(argc, argv, B_TRUE, &cb.cb_proplist, - list_callback, &cb); + for (;;) { - zprop_free_list(cb.cb_proplist); + if (timestamp_fmt != NODATE) + print_timestamp(timestamp_fmt); - if (argc == 0 && cb.cb_first && !cb.cb_scripted) { - (void) printf(gettext("no pools available\n")); - return (0); + ret = for_each_pool(argc, argv, B_TRUE, &cb.cb_proplist, + list_callback, &cb); + + if (argc == 0 && cb.cb_first && !cb.cb_scripted) { + (void) printf(gettext("no pools available\n")); + zprop_free_list(cb.cb_proplist); + return (0); + } + + if (interval == 0) + break; + + if (count != 0 && --count == 0) + break; + + (void) sleep(interval); } + zprop_free_list(cb.cb_proplist); return (ret); } @@ -2417,6 +2780,146 @@ zpool_do_detach(int argc, char **argv) } /* + * zpool split [-n] [-o prop=val] ... + * [-o mntopt] ... + * [-R altroot] [ ...] + * + * -n Do not split the pool, but display the resulting layout if + * it were to be split. + * -o Set property=value, or set mount options. + * -R Mount the split-off pool under an alternate root. + * + * Splits the named pool and gives it the new pool name. Devices to be split + * off may be listed, provided that no more than one device is specified + * per top-level vdev mirror. The newly split pool is left in an exported + * state unless -R is specified. + * + * Restrictions: the top-level of the pool pool must only be made up of + * mirrors; all devices in the pool must be healthy; no device may be + * undergoing a resilvering operation. + */ +int +zpool_do_split(int argc, char **argv) +{ + char *srcpool, *newpool, *propval; + char *mntopts = NULL; + splitflags_t flags; + int c, ret = 0; + zpool_handle_t *zhp; + nvlist_t *config, *props = NULL; + + flags.dryrun = B_FALSE; + flags.import = B_FALSE; + + /* check options */ + while ((c = getopt(argc, argv, ":R:no:")) != -1) { + switch (c) { + case 'R': + flags.import = B_TRUE; + if (add_prop_list( + zpool_prop_to_name(ZPOOL_PROP_ALTROOT), optarg, + &props, B_TRUE) != 0) { + if (props) + nvlist_free(props); + usage(B_FALSE); + } + break; + case 'n': + flags.dryrun = B_TRUE; + break; + case 'o': + if ((propval = strchr(optarg, '=')) != NULL) { + *propval = '\0'; + propval++; + if (add_prop_list(optarg, propval, + &props, B_TRUE) != 0) { + if (props) + nvlist_free(props); + usage(B_FALSE); + } + } else { + mntopts = optarg; + } + break; + case ':': + (void) fprintf(stderr, gettext("missing argument for " + "'%c' option\n"), optopt); + usage(B_FALSE); + break; + case '?': + (void) fprintf(stderr, gettext("invalid option '%c'\n"), + optopt); + usage(B_FALSE); + break; + } + } + + if (!flags.import && mntopts != NULL) { + (void) fprintf(stderr, gettext("setting mntopts is only " + "valid when importing the pool\n")); + usage(B_FALSE); + } + + argc -= optind; + argv += optind; + + if (argc < 1) { + (void) fprintf(stderr, gettext("Missing pool name\n")); + usage(B_FALSE); + } + if (argc < 2) { + (void) fprintf(stderr, gettext("Missing new pool name\n")); + usage(B_FALSE); + } + + srcpool = argv[0]; + newpool = argv[1]; + + argc -= 2; + argv += 2; + + if ((zhp = zpool_open(g_zfs, srcpool)) == NULL) + return (1); + + config = split_mirror_vdev(zhp, newpool, props, flags, argc, argv); + if (config == NULL) { + ret = 1; + } else { + if (flags.dryrun) { + (void) printf(gettext("would create '%s' with the " + "following layout:\n\n"), newpool); + print_vdev_tree(NULL, newpool, config, 0, B_FALSE); + } + nvlist_free(config); + } + + zpool_close(zhp); + + if (ret != 0 || flags.dryrun || !flags.import) + return (ret); + + /* + * The split was successful. Now we need to open the new + * pool and import it. + */ + if ((zhp = zpool_open_canfail(g_zfs, newpool)) == NULL) + return (1); + if (zpool_get_state(zhp) != POOL_STATE_UNAVAIL && + zpool_enable_datasets(zhp, mntopts, 0) != 0) { + ret = 1; + (void) fprintf(stderr, gettext("Split was succssful, but " + "the datasets could not all be mounted\n")); + (void) fprintf(stderr, gettext("Try doing '%s' with a " + "different altroot\n"), "zpool import"); + } + zpool_close(zhp); + + return (ret); +} + + + +/* * zpool online ... */ int @@ -2427,10 +2930,14 @@ zpool_do_online(int argc, char **argv) zpool_handle_t *zhp; int ret = 0; vdev_state_t newstate; + int flags = 0; /* check options */ - while ((c = getopt(argc, argv, "t")) != -1) { + while ((c = getopt(argc, argv, "et")) != -1) { switch (c) { + case 'e': + flags |= ZFS_ONLINE_EXPAND; + break; case 't': case '?': (void) fprintf(stderr, gettext("invalid option '%c'\n"), @@ -2458,7 +2965,7 @@ zpool_do_online(int argc, char **argv) return (1); for (i = 1; i < argc; i++) { - if (zpool_vdev_online(zhp, argv[i], 0, &newstate) == 0) { + if (zpool_vdev_online(zhp, argv[i], flags, &newstate) == 0) { if (newstate != VDEV_STATE_HEALTHY) { (void) printf(gettext("warning: device '%s' " "onlined, but remains in faulted state\n"), @@ -2552,31 +3059,80 @@ zpool_do_offline(int argc, char **argv) int zpool_do_clear(int argc, char **argv) { + int c; int ret = 0; + boolean_t dryrun = B_FALSE; + boolean_t do_rewind = B_FALSE; + boolean_t xtreme_rewind = B_FALSE; + uint32_t rewind_policy = ZPOOL_NO_REWIND; + nvlist_t *policy = NULL; zpool_handle_t *zhp; char *pool, *device; - if (argc < 2) { + /* check options */ + while ((c = getopt(argc, argv, "FnX")) != -1) { + switch (c) { + case 'F': + do_rewind = B_TRUE; + break; + case 'n': + dryrun = B_TRUE; + break; + case 'X': + xtreme_rewind = B_TRUE; + break; + case '?': + (void) fprintf(stderr, gettext("invalid option '%c'\n"), + optopt); + usage(B_FALSE); + } + } + + argc -= optind; + argv += optind; + + if (argc < 1) { (void) fprintf(stderr, gettext("missing pool name\n")); usage(B_FALSE); } - if (argc > 3) { + if (argc > 2) { (void) fprintf(stderr, gettext("too many arguments\n")); usage(B_FALSE); } - pool = argv[1]; - device = argc == 3 ? argv[2] : NULL; + if ((dryrun || xtreme_rewind) && !do_rewind) { + (void) fprintf(stderr, + gettext("-n or -X only meaningful with -F\n")); + usage(B_FALSE); + } + if (dryrun) + rewind_policy = ZPOOL_TRY_REWIND; + else if (do_rewind) + rewind_policy = ZPOOL_DO_REWIND; + if (xtreme_rewind) + rewind_policy |= ZPOOL_EXTREME_REWIND; - if ((zhp = zpool_open_canfail(g_zfs, pool)) == NULL) + /* In future, further rewind policy choices can be passed along here */ + if (nvlist_alloc(&policy, NV_UNIQUE_NAME, 0) != 0 || + nvlist_add_uint32(policy, ZPOOL_REWIND_REQUEST, rewind_policy) != 0) return (1); - if (zpool_clear(zhp, device) != 0) + pool = argv[0]; + device = argc == 2 ? argv[1] : NULL; + + if ((zhp = zpool_open_canfail(g_zfs, pool)) == NULL) { + nvlist_free(policy); + return (1); + } + + if (zpool_clear(zhp, device, policy) != 0) ret = 1; zpool_close(zhp); + nvlist_free(policy); + return (ret); } @@ -2601,7 +3157,7 @@ scrub_callback(zpool_handle_t *zhp, void *data) return (1); } - err = zpool_scrub(zhp, cb->cb_type); + err = zpool_scan(zhp, cb->cb_type); return (err != 0); } @@ -2617,13 +3173,13 @@ zpool_do_scrub(int argc, char **argv) int c; scrub_cbdata_t cb; - cb.cb_type = POOL_SCRUB_EVERYTHING; + cb.cb_type = POOL_SCAN_SCRUB; /* check options */ while ((c = getopt(argc, argv, "s")) != -1) { switch (c) { case 's': - cb.cb_type = POOL_SCRUB_NONE; + cb.cb_type = POOL_SCAN_NONE; break; case '?': (void) fprintf(stderr, gettext("invalid option '%c'\n"), @@ -2651,242 +3207,109 @@ typedef struct status_cbdata { boolean_t cb_verbose; boolean_t cb_explain; boolean_t cb_first; + boolean_t cb_dedup_stats; } status_cbdata_t; /* * Print out detailed scrub status. */ void -print_scrub_status(nvlist_t *nvroot) +print_scan_status(pool_scan_stat_t *ps) { - vdev_stat_t *vs; - uint_t vsc; - time_t start, end, now; + time_t start, end; + uint64_t elapsed, mins_left; + uint64_t pass_exam, examined, total; + uint_t rate; double fraction_done; - uint64_t examined, total, minutes_left, minutes_taken; - char *scrub_type; + char processed_buf[7], examined_buf[7], total_buf[7], rate_buf[7]; - verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_STATS, - (uint64_t **)&vs, &vsc) == 0); + (void) printf(gettext(" scan: ")); - /* - * If there's never been a scrub, there's not much to say. - */ - if (vs->vs_scrub_end == 0 && vs->vs_scrub_type == POOL_SCRUB_NONE) { + /* If there's never been a scan, there's not much to say. */ + if (ps == NULL || ps->pss_func == POOL_SCAN_NONE || + ps->pss_func >= POOL_SCAN_FUNCS) { (void) printf(gettext("none requested\n")); return; } - scrub_type = (vs->vs_scrub_type == POOL_SCRUB_RESILVER) ? - "resilver" : "scrub"; + start = ps->pss_start_time; + end = ps->pss_end_time; + zfs_nicenum(ps->pss_processed, processed_buf, sizeof (processed_buf)); - start = vs->vs_scrub_start; - end = vs->vs_scrub_end; - now = time(NULL); - examined = vs->vs_scrub_examined; - total = vs->vs_alloc; - - if (end != 0) { - minutes_taken = (uint64_t)((end - start) / 60); - - (void) printf(gettext("%s %s after %lluh%um with %llu errors " - "on %s"), - scrub_type, vs->vs_scrub_complete ? "completed" : "stopped", + assert(ps->pss_func == POOL_SCAN_SCRUB || + ps->pss_func == POOL_SCAN_RESILVER); + /* + * Scan is finished or canceled. + */ + if (ps->pss_state == DSS_FINISHED) { + uint64_t minutes_taken = (end - start) / 60; + char *fmt; + + if (ps->pss_func == POOL_SCAN_SCRUB) { + fmt = gettext("scrub repaired %s in %lluh%um with " + "%llu errors on %s"); + } else if (ps->pss_func == POOL_SCAN_RESILVER) { + fmt = gettext("resilvered %s in %lluh%um with " + "%llu errors on %s"); + } + /* LINTED */ + (void) printf(fmt, processed_buf, (u_longlong_t)(minutes_taken / 60), (uint_t)(minutes_taken % 60), - (u_longlong_t)vs->vs_scrub_errors, ctime(&end)); + (u_longlong_t)ps->pss_errors, + ctime((time_t *)&end)); + return; + } else if (ps->pss_state == DSS_CANCELED) { + if (ps->pss_func == POOL_SCAN_SCRUB) { + (void) printf(gettext("scrub canceled on %s"), + ctime(&end)); + } else if (ps->pss_func == POOL_SCAN_RESILVER) { + (void) printf(gettext("resilver canceled on %s"), + ctime(&end)); + } return; } - if (examined == 0) - examined = 1; - if (examined > total) - total = examined; - - fraction_done = (double)examined / total; - minutes_left = (uint64_t)((now - start) * - (1 - fraction_done) / fraction_done / 60); - minutes_taken = (uint64_t)((now - start) / 60); - - (void) printf(gettext("%s in progress for %lluh%um, %.2f%% done, " - "%lluh%um to go\n"), - scrub_type, (u_longlong_t)(minutes_taken / 60), - (uint_t)(minutes_taken % 60), 100 * fraction_done, - (u_longlong_t)(minutes_left / 60), (uint_t)(minutes_left % 60)); -} - -typedef struct spare_cbdata { - uint64_t cb_guid; - zpool_handle_t *cb_zhp; -} spare_cbdata_t; - -static boolean_t -find_vdev(nvlist_t *nv, uint64_t search) -{ - uint64_t guid; - nvlist_t **child; - uint_t c, children; - - if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &guid) == 0 && - search == guid) - return (B_TRUE); - - if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, - &child, &children) == 0) { - for (c = 0; c < children; c++) - if (find_vdev(child[c], search)) - return (B_TRUE); - } - - return (B_FALSE); -} - -static int -find_spare(zpool_handle_t *zhp, void *data) -{ - spare_cbdata_t *cbp = data; - nvlist_t *config, *nvroot; - - config = zpool_get_config(zhp, NULL); - verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, - &nvroot) == 0); - - if (find_vdev(nvroot, cbp->cb_guid)) { - cbp->cb_zhp = zhp; - return (1); - } - - zpool_close(zhp); - return (0); -} - -/* - * Print out configuration state as requested by status_callback. - */ -void -print_status_config(zpool_handle_t *zhp, const char *name, nvlist_t *nv, - int namewidth, int depth, boolean_t isspare, boolean_t print_logs) -{ - nvlist_t **child; - uint_t c, children; - vdev_stat_t *vs; - char rbuf[6], wbuf[6], cbuf[6], repaired[7]; - char *vname; - uint64_t notpresent; - spare_cbdata_t cb; - char *state; - - verify(nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_STATS, - (uint64_t **)&vs, &c) == 0); - - if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, - &child, &children) != 0) - children = 0; - - state = zpool_state_to_name(vs->vs_state, vs->vs_aux); - if (isspare) { - /* - * For hot spares, we use the terms 'INUSE' and 'AVAILABLE' for - * online drives. - */ - if (vs->vs_aux == VDEV_AUX_SPARED) - state = "INUSE"; - else if (vs->vs_state == VDEV_STATE_HEALTHY) - state = "AVAIL"; - } - - (void) printf("\t%*s%-*s %-8s", depth, "", namewidth - depth, - name, state); - - if (!isspare) { - 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)); - (void) printf(" %5s %5s %5s", rbuf, wbuf, cbuf); - } - - if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NOT_PRESENT, - ¬present) == 0) { - char *path; - verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0); - (void) printf(" was %s", path); - } else if (vs->vs_aux != 0) { - (void) printf(" "); - - switch (vs->vs_aux) { - case VDEV_AUX_OPEN_FAILED: - (void) printf(gettext("cannot open")); - break; + assert(ps->pss_state == DSS_SCANNING); - case VDEV_AUX_BAD_GUID_SUM: - (void) printf(gettext("missing device")); - break; - - case VDEV_AUX_NO_REPLICAS: - (void) printf(gettext("insufficient replicas")); - break; - - case VDEV_AUX_VERSION_NEWER: - (void) printf(gettext("newer version")); - break; - - case VDEV_AUX_SPARED: - verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, - &cb.cb_guid) == 0); - if (zpool_iter(g_zfs, find_spare, &cb) == 1) { - if (strcmp(zpool_get_name(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(cb.cb_zhp)); - zpool_close(cb.cb_zhp); - } else { - (void) printf(gettext("currently in use")); - } - break; - - case VDEV_AUX_ERR_EXCEEDED: - (void) printf(gettext("too many errors")); - break; - - case VDEV_AUX_IO_FAILURE: - (void) printf(gettext("experienced I/O failures")); - break; - - case VDEV_AUX_BAD_LOG: - (void) printf(gettext("bad intent log")); - break; - - default: - (void) printf(gettext("corrupted data")); - break; - } - } else if (vs->vs_scrub_repaired != 0 && children == 0) { - /* - * Report bytes resilvered/repaired on leaf devices. - */ - zfs_nicenum(vs->vs_scrub_repaired, repaired, sizeof (repaired)); - (void) printf(gettext(" %s %s"), repaired, - (vs->vs_scrub_type == POOL_SCRUB_RESILVER) ? - "resilvered" : "repaired"); + /* + * Scan is in progress. + */ + if (ps->pss_func == POOL_SCAN_SCRUB) { + (void) printf(gettext("scrub in progress since %s"), + ctime(&start)); + } else if (ps->pss_func == POOL_SCAN_RESILVER) { + (void) printf(gettext("resilver in progress since %s"), + ctime(&start)); } - (void) printf("\n"); - - for (c = 0; c < children; c++) { - uint64_t is_log = B_FALSE; + examined = ps->pss_examined ? ps->pss_examined : 1; + total = ps->pss_to_examine; + fraction_done = (double)examined / total; - (void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG, - &is_log); - if ((is_log && !print_logs) || (!is_log && print_logs)) - continue; - vname = zpool_vdev_name(g_zfs, zhp, child[c]); - print_status_config(zhp, vname, child[c], - namewidth, depth + 2, isspare, B_FALSE); - free(vname); + /* elapsed time for this pass */ + elapsed = time(NULL) - ps->pss_pass_start; + elapsed = elapsed ? elapsed : 1; + pass_exam = ps->pss_pass_exam ? ps->pss_pass_exam : 1; + rate = pass_exam / elapsed; + rate = rate ? rate : 1; + mins_left = ((total - examined) / rate) / 60; + + zfs_nicenum(examined, examined_buf, sizeof (examined_buf)); + zfs_nicenum(total, total_buf, sizeof (total_buf)); + zfs_nicenum(rate, rate_buf, sizeof (rate_buf)); + + (void) printf(gettext(" %s scanned out of %s at " + "%s/s, %lluh%um to go\n"), examined_buf, total_buf, rate_buf, + (u_longlong_t)(mins_left / 60), + (uint_t)(mins_left % 60)); + + if (ps->pss_func == POOL_SCAN_RESILVER) { + (void) printf(gettext(" %s resilvered, %.2f%% done\n"), + processed_buf, 100 * fraction_done); + } else if (ps->pss_func == POOL_SCAN_SCRUB) { + (void) printf(gettext(" %s repaired, %.2f%% done\n"), + processed_buf, 100 * fraction_done); } } @@ -2938,9 +3361,9 @@ print_spares(zpool_handle_t *zhp, nvlist_t **spares, uint_t nspares, (void) printf(gettext("\tspares\n")); for (i = 0; i < nspares; i++) { - name = zpool_vdev_name(g_zfs, zhp, spares[i]); + name = zpool_vdev_name(g_zfs, zhp, spares[i], B_FALSE); print_status_config(zhp, name, spares[i], - namewidth, 2, B_TRUE, B_FALSE); + namewidth, 2, B_TRUE); free(name); } } @@ -2958,13 +3381,43 @@ print_l2cache(zpool_handle_t *zhp, nvlist_t **l2cache, uint_t nl2cache, (void) printf(gettext("\tcache\n")); for (i = 0; i < nl2cache; i++) { - name = zpool_vdev_name(g_zfs, zhp, l2cache[i]); + name = zpool_vdev_name(g_zfs, zhp, l2cache[i], B_FALSE); print_status_config(zhp, name, l2cache[i], - namewidth, 2, B_FALSE, B_FALSE); + namewidth, 2, B_FALSE); free(name); } } +static void +print_dedup_stats(nvlist_t *config) +{ + ddt_histogram_t *ddh; + ddt_stat_t *dds; + ddt_object_t *ddo; + uint_t c; + + /* + * If the pool was faulted then we may not have been able to + * obtain the config. Otherwise, if have anything in the dedup + * table continue processing the stats. + */ + if (nvlist_lookup_uint64_array(config, ZPOOL_CONFIG_DDT_OBJ_STATS, + (uint64_t **)&ddo, &c) != 0 || ddo->ddo_count == 0) + return; + + (void) printf("\n"); + (void) printf("DDT entries %llu, size %llu on disk, %llu in core\n", + (u_longlong_t)ddo->ddo_count, + (u_longlong_t)ddo->ddo_dspace, + (u_longlong_t)ddo->ddo_mspace); + + verify(nvlist_lookup_uint64_array(config, ZPOOL_CONFIG_DDT_STATS, + (uint64_t **)&dds, &c) == 0); + verify(nvlist_lookup_uint64_array(config, ZPOOL_CONFIG_DDT_HISTOGRAM, + (uint64_t **)&ddh, &c) == 0); + zpool_dump_ddt(dds, ddh); +} + /* * Display a summary of pool status. Displays a summary such as: * @@ -3017,7 +3470,7 @@ status_callback(zpool_handle_t *zhp, void *data) verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0); - verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_STATS, + verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_VDEV_STATS, (uint64_t **)&vs, &c) == 0); health = zpool_state_to_name(vs->vs_state, vs->vs_aux); @@ -3055,8 +3508,8 @@ status_callback(zpool_handle_t *zhp, void *data) "be used because the label is missing \n\tor invalid. " "There are insufficient replicas for the pool to " "continue\n\tfunctioning.\n")); - (void) printf(gettext("action: Destroy and re-create the pool " - "from a backup source.\n")); + zpool_explain_recover(zpool_get_handle(zhp), + zpool_get_name(zhp), reason, config); break; case ZPOOL_STATUS_FAILING_DEV: @@ -3080,6 +3533,16 @@ status_callback(zpool_handle_t *zhp, void *data) "replace'.\n")); break; + case ZPOOL_STATUS_REMOVED_DEV: + (void) printf(gettext("status: One or more devices has " + "been removed by the administrator.\n\tSufficient " + "replicas exist for the pool to continue functioning in " + "a\n\tdegraded state.\n")); + (void) printf(gettext("action: Online the device using " + "'zpool online' or replace the device with\n\t'zpool " + "replace'.\n")); + break; + case ZPOOL_STATUS_RESILVERING: (void) printf(gettext("status: One or more devices is " "currently being resilvered. The pool will\n\tcontinue " @@ -3100,8 +3563,8 @@ status_callback(zpool_handle_t *zhp, void *data) case ZPOOL_STATUS_CORRUPT_POOL: (void) printf(gettext("status: The pool metadata is corrupted " "and the pool cannot be opened.\n")); - (void) printf(gettext("action: Destroy and re-create the pool " - "from a backup source.\n")); + zpool_explain_recover(zpool_get_handle(zhp), + zpool_get_name(zhp), reason, config); break; case ZPOOL_STATUS_VERSION_OLDER: @@ -3177,10 +3640,11 @@ status_callback(zpool_handle_t *zhp, void *data) uint64_t nerr; nvlist_t **spares, **l2cache; uint_t nspares, nl2cache; + pool_scan_stat_t *ps = NULL; - - (void) printf(gettext(" scrub: ")); - print_scrub_status(nvroot); + (void) nvlist_lookup_uint64_array(nvroot, + ZPOOL_CONFIG_SCAN_STATS, (uint64_t **)&ps, &c); + print_scan_status(ps); namewidth = max_width(zhp, nvroot, 0, 0); if (namewidth < 10) @@ -3190,11 +3654,10 @@ status_callback(zpool_handle_t *zhp, void *data) (void) printf(gettext("\t%-*s %-8s %5s %5s %5s\n"), namewidth, "NAME", "STATE", "READ", "WRITE", "CKSUM"); print_status_config(zhp, zpool_get_name(zhp), nvroot, - namewidth, 0, B_FALSE, B_FALSE); - if (num_logs(nvroot) > 0) - print_status_config(zhp, "logs", nvroot, namewidth, 0, - B_FALSE, B_TRUE); + namewidth, 0, B_FALSE); + if (num_logs(nvroot) > 0) + print_logs(zhp, nvroot, namewidth, B_TRUE); if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_L2CACHE, &l2cache, &nl2cache) == 0) print_l2cache(zhp, l2cache, nl2cache, namewidth); @@ -3237,6 +3700,9 @@ status_callback(zpool_handle_t *zhp, void *data) else print_error_log(zhp); } + + if (cbp->cb_dedup_stats) + print_dedup_stats(config); } else { (void) printf(gettext("config: The configuration cannot be " "determined.\n")); @@ -3246,10 +3712,12 @@ status_callback(zpool_handle_t *zhp, void *data) } /* - * zpool status [-vx] [pool] ... + * zpool status [-vx] [-T d|u] [pool] ... [interval [count]] * * -v Display complete error logs * -x Display only pools with potential problems + * -D Display dedup status (undocumented) + * -T Display a timestamp in date(1) or Unix format * * Describes the health status of all pools or some subset. */ @@ -3258,10 +3726,11 @@ zpool_do_status(int argc, char **argv) { int c; int ret; + unsigned long interval = 0, count = 0; status_cbdata_t cb = { 0 }; /* check options */ - while ((c = getopt(argc, argv, "vx")) != -1) { + while ((c = getopt(argc, argv, "vxDT:")) != -1) { switch (c) { case 'v': cb.cb_verbose = B_TRUE; @@ -3269,6 +3738,12 @@ zpool_do_status(int argc, char **argv) case 'x': cb.cb_explain = B_TRUE; break; + case 'D': + cb.cb_dedup_stats = B_TRUE; + break; + case 'T': + get_timestamp_arg(*optarg); + break; case '?': (void) fprintf(stderr, gettext("invalid option '%c'\n"), optopt); @@ -3279,19 +3754,38 @@ zpool_do_status(int argc, char **argv) argc -= optind; argv += optind; - cb.cb_first = B_TRUE; + get_interval_count(&argc, argv, &interval, &count); if (argc == 0) cb.cb_allpools = B_TRUE; - ret = for_each_pool(argc, argv, B_TRUE, NULL, status_callback, &cb); + cb.cb_first = B_TRUE; - if (argc == 0 && cb.cb_count == 0) - (void) printf(gettext("no pools available\n")); - else if (cb.cb_explain && cb.cb_first && cb.cb_allpools) - (void) printf(gettext("all pools are healthy\n")); + for (;;) { + if (timestamp_fmt != NODATE) + print_timestamp(timestamp_fmt); - return (ret); + ret = for_each_pool(argc, argv, B_TRUE, NULL, + status_callback, &cb); + + if (argc == 0 && cb.cb_count == 0) + (void) printf(gettext("no pools available\n")); + else if (cb.cb_explain && cb.cb_first && cb.cb_allpools) + (void) printf(gettext("all pools are healthy\n")); + + if (ret != 0) + return (ret); + + if (interval == 0) + break; + + if (count != 0 && --count == 0) + break; + + (void) sleep(interval); + } + + return (0); } typedef struct upgrade_cbdata { @@ -3418,7 +3912,7 @@ zpool_do_upgrade(int argc, char **argv) /* check options */ - while ((c = getopt(argc, argv, "avV:")) != -1) { + while ((c = getopt(argc, argv, ":avV:")) != -1) { switch (c) { case 'a': cb.cb_all = B_TRUE; @@ -3435,6 +3929,11 @@ zpool_do_upgrade(int argc, char **argv) usage(B_FALSE); } break; + case ':': + (void) fprintf(stderr, gettext("missing argument for " + "'%c' option\n"), optopt); + usage(B_FALSE); + break; case '?': (void) fprintf(stderr, gettext("invalid option '%c'\n"), optopt); @@ -3495,13 +3994,24 @@ zpool_do_upgrade(int argc, char **argv) (void) printf(gettext(" 11 Improved scrub performance\n")); (void) printf(gettext(" 12 Snapshot properties\n")); (void) printf(gettext(" 13 snapused property\n")); - (void) printf(gettext(" 14 passthrough-x aclinherit " - "support\n")); - (void) printf(gettext("For more information on a particular " - "version, including supported releases, see:\n\n")); - (void) printf("http://www.opensolaris.org/os/community/zfs/" - "version/N\n\n"); - (void) printf(gettext("Where 'N' is the version number.\n")); + (void) printf(gettext(" 14 passthrough-x aclinherit\n")); + (void) printf(gettext(" 15 user/group space accounting\n")); + (void) printf(gettext(" 16 stmf property support\n")); + (void) printf(gettext(" 17 Triple-parity RAID-Z\n")); + (void) printf(gettext(" 18 Snapshot user holds\n")); + (void) printf(gettext(" 19 Log device removal\n")); + (void) printf(gettext(" 20 Compression using zle " + "(zero-length encoding)\n")); + (void) printf(gettext(" 21 Deduplication\n")); + (void) printf(gettext(" 22 Received properties\n")); + (void) printf(gettext(" 23 Slim ZIL\n")); + (void) printf(gettext(" 24 System attributes\n")); + (void) printf(gettext(" 25 Improved scrub stats\n")); + (void) printf(gettext(" 26 Improved snapshot deletion " + "performance\n")); + (void) printf(gettext("\nFor more information on a particular " + "version, including supported releases,\n")); + (void) printf(gettext("see the ZFS Administration Guide.\n\n")); } else if (argc == 0) { int notfound; @@ -3543,47 +4053,6 @@ typedef struct hist_cbdata { int internal; } hist_cbdata_t; -char *hist_event_table[LOG_END] = { - "invalid event", - "pool create", - "vdev add", - "pool remove", - "pool destroy", - "pool export", - "pool import", - "vdev attach", - "vdev replace", - "vdev detach", - "vdev online", - "vdev offline", - "vdev upgrade", - "pool clear", - "pool scrub", - "pool property set", - "create", - "clone", - "destroy", - "destroy_begin_sync", - "inherit", - "property set", - "quota set", - "permission update", - "permission remove", - "permission who remove", - "promote", - "receive", - "rename", - "reservation set", - "replay_inc_sync", - "replay_full_sync", - "rollback", - "snapshot", - "filesystem version upgrade", - "refquota set", - "refreservation set", - "pool scrub done", -}; - /* * Print out the command history for a specific pool. */ @@ -3641,7 +4110,7 @@ get_history_one(zpool_handle_t *zhp, void *data) (void) snprintf(internalstr, sizeof (internalstr), "[internal %s txg:%lld] %s", - hist_event_table[ievent], txg, + zfs_history_event_names[ievent], txg, pathstr); cmdstr = internalstr; } @@ -3753,7 +4222,8 @@ get_callback(zpool_handle_t *zhp, void *data) continue; zprop_print_one_property(zpool_get_name(zhp), cbp, - zpool_prop_to_name(pl->pl_prop), value, srctype, NULL); + zpool_prop_to_name(pl->pl_prop), value, srctype, NULL, + NULL); } return (0); }