* Copyright (c) 2017 Datto Inc.
* Copyright (c) 2017 Open-E, Inc. All Rights Reserved.
* Copyright (c) 2017, Intel Corporation.
+ * Copyright (c) 2019, loli10K <ezomori.nozomu@gmail.com>
*/
#include <assert.h>
#include <math.h>
#include <libzfs.h>
+#include <libzutil.h>
#include "zpool_util.h"
#include "zfs_comutil.h"
static int zpool_do_replace(int, char **);
static int zpool_do_split(int, char **);
+static int zpool_do_initialize(int, char **);
static int zpool_do_scrub(int, char **);
static int zpool_do_resilver(int, char **);
+static int zpool_do_trim(int, char **);
static int zpool_do_import(int, char **);
static int zpool_do_export(int, char **);
static int zpool_do_sync(int, char **);
+static int zpool_do_version(int, char **);
+
/*
* These libumem hooks provide a reasonable set of defaults for the allocator's
* debugging facilities.
HELP_ONLINE,
HELP_REPLACE,
HELP_REMOVE,
+ HELP_INITIALIZE,
HELP_SCRUB,
HELP_RESILVER,
+ HELP_TRIM,
HELP_STATUS,
HELP_UPGRADE,
HELP_EVENTS,
HELP_SPLIT,
HELP_SYNC,
HELP_REGUID,
- HELP_REOPEN
+ HELP_REOPEN,
+ HELP_VERSION
} zpool_help_t;
* of all the nvlists a flag requires. Also specifies the order in
* which data gets printed in zpool iostat.
*/
-static const char *vsx_type_to_nvlist[IOS_COUNT][11] = {
+static const char *vsx_type_to_nvlist[IOS_COUNT][13] = {
[IOS_L_HISTO] = {
ZPOOL_CONFIG_VDEV_TOT_R_LAT_HISTO,
ZPOOL_CONFIG_VDEV_TOT_W_LAT_HISTO,
ZPOOL_CONFIG_VDEV_ASYNC_R_LAT_HISTO,
ZPOOL_CONFIG_VDEV_ASYNC_W_LAT_HISTO,
ZPOOL_CONFIG_VDEV_SCRUB_LAT_HISTO,
+ ZPOOL_CONFIG_VDEV_TRIM_LAT_HISTO,
NULL},
[IOS_LATENCY] = {
ZPOOL_CONFIG_VDEV_TOT_R_LAT_HISTO,
ZPOOL_CONFIG_VDEV_TOT_W_LAT_HISTO,
ZPOOL_CONFIG_VDEV_DISK_R_LAT_HISTO,
ZPOOL_CONFIG_VDEV_DISK_W_LAT_HISTO,
+ ZPOOL_CONFIG_VDEV_TRIM_LAT_HISTO,
NULL},
[IOS_QUEUES] = {
ZPOOL_CONFIG_VDEV_SYNC_R_ACTIVE_QUEUE,
ZPOOL_CONFIG_VDEV_ASYNC_R_ACTIVE_QUEUE,
ZPOOL_CONFIG_VDEV_ASYNC_W_ACTIVE_QUEUE,
ZPOOL_CONFIG_VDEV_SCRUB_ACTIVE_QUEUE,
+ ZPOOL_CONFIG_VDEV_TRIM_ACTIVE_QUEUE,
NULL},
[IOS_RQ_HISTO] = {
ZPOOL_CONFIG_VDEV_SYNC_IND_R_HISTO,
ZPOOL_CONFIG_VDEV_ASYNC_AGG_W_HISTO,
ZPOOL_CONFIG_VDEV_IND_SCRUB_HISTO,
ZPOOL_CONFIG_VDEV_AGG_SCRUB_HISTO,
+ ZPOOL_CONFIG_VDEV_IND_TRIM_HISTO,
+ ZPOOL_CONFIG_VDEV_AGG_TRIM_HISTO,
NULL},
};
* the generic usage message.
*/
static zpool_command_t command_table[] = {
+ { "version", zpool_do_version, HELP_VERSION },
+ { NULL },
{ "create", zpool_do_create, HELP_CREATE },
{ "destroy", zpool_do_destroy, HELP_DESTROY },
{ NULL },
{ "replace", zpool_do_replace, HELP_REPLACE },
{ "split", zpool_do_split, HELP_SPLIT },
{ NULL },
- { "scrub", zpool_do_scrub, HELP_SCRUB },
+ { "initialize", zpool_do_initialize, HELP_INITIALIZE },
{ "resilver", zpool_do_resilver, HELP_RESILVER },
+ { "scrub", zpool_do_scrub, HELP_SCRUB },
+ { "trim", zpool_do_trim, HELP_TRIM },
{ NULL },
{ "import", zpool_do_import, HELP_IMPORT },
{ "export", zpool_do_export, HELP_EXPORT },
return (gettext("\tiostat [[[-c [script1,script2,...]"
"[-lq]]|[-rw]] [-T d | u] [-ghHLpPvy]\n"
"\t [[pool ...]|[pool vdev ...]|[vdev ...]]"
- " [interval [count]]\n"));
+ " [[-n] interval [count]]\n"));
case HELP_LABELCLEAR:
return (gettext("\tlabelclear [-f] <vdev>\n"));
case HELP_LIST:
return (gettext("\tremove [-nps] <pool> <device> ...\n"));
case HELP_REOPEN:
return (gettext("\treopen [-n] <pool>\n"));
+ case HELP_INITIALIZE:
+ return (gettext("\tinitialize [-c | -s] <pool> "
+ "[<device> ...]\n"));
case HELP_SCRUB:
return (gettext("\tscrub [-s | -p] <pool> ...\n"));
case HELP_RESILVER:
return (gettext("\tresilver <pool> ...\n"));
+ case HELP_TRIM:
+ return (gettext("\ttrim [-d] [-r <rate>] [-c | -s] <pool> "
+ "[<device> ...]\n"));
case HELP_STATUS:
- return (gettext("\tstatus [-c [script1,script2,...]] [-gLPvxD]"
- "[-T d|u] [pool] ... \n"
+ return (gettext("\tstatus [-c [script1,script2,...]] "
+ "[-igLpPstvxD] [-T d|u] [pool] ... \n"
"\t [interval [count]]\n"));
case HELP_UPGRADE:
return (gettext("\tupgrade\n"
return (gettext("\treguid <pool>\n"));
case HELP_SYNC:
return (gettext("\tsync [pool] ...\n"));
+ case HELP_VERSION:
+ return (gettext("\tversion\n"));
}
abort();
/* NOTREACHED */
}
+static void
+zpool_collect_leaves(zpool_handle_t *zhp, nvlist_t *nvroot, nvlist_t *res)
+{
+ uint_t children = 0;
+ nvlist_t **child;
+ uint_t i;
+
+ (void) nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,
+ &child, &children);
+
+ if (children == 0) {
+ char *path = zpool_vdev_name(g_zfs, zhp, nvroot,
+ VDEV_NAME_PATH);
+
+ if (strcmp(path, VDEV_TYPE_INDIRECT) != 0)
+ fnvlist_add_boolean(res, path);
+
+ free(path);
+ return;
+ }
+
+ for (i = 0; i < children; i++) {
+ zpool_collect_leaves(zhp, child[i], res);
+ }
+}
/*
* Callback routine that will print out a pool property value.
exit(requested ? 0 : 2);
}
+/*
+ * zpool initialize [-c | -s] <pool> [<vdev> ...]
+ * Initialize all unused blocks in the specified vdevs, or all vdevs in the pool
+ * if none specified.
+ *
+ * -c Cancel. Ends active initializing.
+ * -s Suspend. Initializing can then be restarted with no flags.
+ */
+int
+zpool_do_initialize(int argc, char **argv)
+{
+ int c;
+ char *poolname;
+ zpool_handle_t *zhp;
+ nvlist_t *vdevs;
+ int err = 0;
+
+ struct option long_options[] = {
+ {"cancel", no_argument, NULL, 'c'},
+ {"suspend", no_argument, NULL, 's'},
+ {0, 0, 0, 0}
+ };
+
+ pool_initialize_func_t cmd_type = POOL_INITIALIZE_START;
+ while ((c = getopt_long(argc, argv, "cs", long_options, NULL)) != -1) {
+ switch (c) {
+ case 'c':
+ if (cmd_type != POOL_INITIALIZE_START &&
+ cmd_type != POOL_INITIALIZE_CANCEL) {
+ (void) fprintf(stderr, gettext("-c cannot be "
+ "combined with other options\n"));
+ usage(B_FALSE);
+ }
+ cmd_type = POOL_INITIALIZE_CANCEL;
+ break;
+ case 's':
+ if (cmd_type != POOL_INITIALIZE_START &&
+ cmd_type != POOL_INITIALIZE_SUSPEND) {
+ (void) fprintf(stderr, gettext("-s cannot be "
+ "combined with other options\n"));
+ usage(B_FALSE);
+ }
+ cmd_type = POOL_INITIALIZE_SUSPEND;
+ break;
+ case '?':
+ if (optopt != 0) {
+ (void) fprintf(stderr,
+ gettext("invalid option '%c'\n"), optopt);
+ } else {
+ (void) fprintf(stderr,
+ gettext("invalid option '%s'\n"),
+ argv[optind - 1]);
+ }
+ usage(B_FALSE);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ (void) fprintf(stderr, gettext("missing pool name argument\n"));
+ usage(B_FALSE);
+ return (-1);
+ }
+
+ poolname = argv[0];
+ zhp = zpool_open(g_zfs, poolname);
+ if (zhp == NULL)
+ return (-1);
+
+ vdevs = fnvlist_alloc();
+ if (argc == 1) {
+ /* no individual leaf vdevs specified, so add them all */
+ nvlist_t *config = zpool_get_config(zhp, NULL);
+ nvlist_t *nvroot = fnvlist_lookup_nvlist(config,
+ ZPOOL_CONFIG_VDEV_TREE);
+ zpool_collect_leaves(zhp, nvroot, vdevs);
+ } else {
+ for (int i = 1; i < argc; i++) {
+ fnvlist_add_boolean(vdevs, argv[i]);
+ }
+ }
+
+ err = zpool_initialize(zhp, cmd_type, vdevs);
+
+ fnvlist_free(vdevs);
+ zpool_close(zhp);
+
+ return (err);
+}
+
/*
* print a pool vdev config for dry runs
*/
return (1);
}
- if (ioctl(fd, BLKFLSBUF) != 0)
+ /*
+ * Flush all dirty pages for the block device. This should not be
+ * fatal when the device does not support BLKFLSBUF as would be the
+ * case for a file vdev.
+ */
+ if ((ioctl(fd, BLKFLSBUF) != 0) && (errno != ENOTTY))
(void) fprintf(stderr, gettext("failed to invalidate "
"cache for %s: %s\n"), vdev, strerror(errno));
- if (zpool_read_label(fd, &config, NULL) != 0 || config == NULL) {
+ if (zpool_read_label(fd, &config, NULL) != 0) {
(void) fprintf(stderr,
- gettext("failed to check state for %s\n"), vdev);
+ gettext("failed to read label from %s\n"), vdev);
ret = 1;
goto errout;
}
int cb_namewidth;
boolean_t cb_allpools;
boolean_t cb_verbose;
+ boolean_t cb_literal;
boolean_t cb_explain;
boolean_t cb_first;
boolean_t cb_dedup_stats;
boolean_t cb_print_status;
+ boolean_t cb_print_slow_ios;
+ boolean_t cb_print_vdev_init;
+ boolean_t cb_print_vdev_trim;
vdev_cmd_data_list_t *vcdl;
} status_cbdata_t;
}
}
+/*
+ * Print vdev initialization status for leaves
+ */
+static void
+print_status_initialize(vdev_stat_t *vs, boolean_t verbose)
+{
+ if (verbose) {
+ if ((vs->vs_initialize_state == VDEV_INITIALIZE_ACTIVE ||
+ vs->vs_initialize_state == VDEV_INITIALIZE_SUSPENDED ||
+ vs->vs_initialize_state == VDEV_INITIALIZE_COMPLETE) &&
+ !vs->vs_scan_removing) {
+ char zbuf[1024];
+ char tbuf[256];
+ struct tm zaction_ts;
+
+ time_t t = vs->vs_initialize_action_time;
+ int initialize_pct = 100;
+ if (vs->vs_initialize_state !=
+ VDEV_INITIALIZE_COMPLETE) {
+ initialize_pct = (vs->vs_initialize_bytes_done *
+ 100 / (vs->vs_initialize_bytes_est + 1));
+ }
+
+ (void) localtime_r(&t, &zaction_ts);
+ (void) strftime(tbuf, sizeof (tbuf), "%c", &zaction_ts);
+
+ switch (vs->vs_initialize_state) {
+ case VDEV_INITIALIZE_SUSPENDED:
+ (void) snprintf(zbuf, sizeof (zbuf), ", %s %s",
+ gettext("suspended, started at"), tbuf);
+ break;
+ case VDEV_INITIALIZE_ACTIVE:
+ (void) snprintf(zbuf, sizeof (zbuf), ", %s %s",
+ gettext("started at"), tbuf);
+ break;
+ case VDEV_INITIALIZE_COMPLETE:
+ (void) snprintf(zbuf, sizeof (zbuf), ", %s %s",
+ gettext("completed at"), tbuf);
+ break;
+ }
+
+ (void) printf(gettext(" (%d%% initialized%s)"),
+ initialize_pct, zbuf);
+ } else {
+ (void) printf(gettext(" (uninitialized)"));
+ }
+ } else if (vs->vs_initialize_state == VDEV_INITIALIZE_ACTIVE) {
+ (void) printf(gettext(" (initializing)"));
+ }
+}
+
+/*
+ * Print vdev TRIM status for leaves
+ */
+static void
+print_status_trim(vdev_stat_t *vs, boolean_t verbose)
+{
+ if (verbose) {
+ if ((vs->vs_trim_state == VDEV_TRIM_ACTIVE ||
+ vs->vs_trim_state == VDEV_TRIM_SUSPENDED ||
+ vs->vs_trim_state == VDEV_TRIM_COMPLETE) &&
+ !vs->vs_scan_removing) {
+ char zbuf[1024];
+ char tbuf[256];
+ struct tm zaction_ts;
+
+ time_t t = vs->vs_trim_action_time;
+ int trim_pct = 100;
+ if (vs->vs_trim_state != VDEV_TRIM_COMPLETE) {
+ trim_pct = (vs->vs_trim_bytes_done *
+ 100 / (vs->vs_trim_bytes_est + 1));
+ }
+
+ (void) localtime_r(&t, &zaction_ts);
+ (void) strftime(tbuf, sizeof (tbuf), "%c", &zaction_ts);
+
+ switch (vs->vs_trim_state) {
+ case VDEV_TRIM_SUSPENDED:
+ (void) snprintf(zbuf, sizeof (zbuf), ", %s %s",
+ gettext("suspended, started at"), tbuf);
+ break;
+ case VDEV_TRIM_ACTIVE:
+ (void) snprintf(zbuf, sizeof (zbuf), ", %s %s",
+ gettext("started at"), tbuf);
+ break;
+ case VDEV_TRIM_COMPLETE:
+ (void) snprintf(zbuf, sizeof (zbuf), ", %s %s",
+ gettext("completed at"), tbuf);
+ break;
+ }
+
+ (void) printf(gettext(" (%d%% trimmed%s)"),
+ trim_pct, zbuf);
+ } else if (vs->vs_trim_notsup) {
+ (void) printf(gettext(" (trim unsupported)"));
+ } else {
+ (void) printf(gettext(" (untrimmed)"));
+ }
+ } else if (vs->vs_trim_state == VDEV_TRIM_ACTIVE) {
+ (void) printf(gettext(" (trimming)"));
+ }
+}
+
/*
* Print out configuration state as requested by status_callback.
*/
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 (cb->cb_literal) {
+ printf(" %5llu %5llu %5llu",
+ (u_longlong_t)vs->vs_read_errors,
+ (u_longlong_t)vs->vs_write_errors,
+ (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));
+ printf(" %5s %5s %5s", rbuf, wbuf, 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 (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NOT_PRESENT,
}
}
+ /* 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");
for (c = 0; c < children; c++) {
"old ones.\n"));
break;
+ case ZPOOL_ERRATA_ZOL_8308_ENCRYPTION:
+ (void) printf(gettext(" action: Existing "
+ "encrypted snapshots and bookmarks contain "
+ "an on-disk\n\tincompatibility. This may "
+ "cause on-disk corruption if they are used"
+ "\n\twith 'zfs recv'. To correct the "
+ "issue, enable the bookmark_v2 feature.\n\t"
+ "No additional action is needed if there "
+ "are no encrypted snapshots or\n\t"
+ "bookmarks. If preserving the encrypted "
+ "snapshots and bookmarks is\n\trequired, "
+ "use a non-raw send to backup and restore "
+ "them. Alternately,\n\tthey may be removed"
+ " to resolve the incompatibility.\n"));
+ break;
default:
/*
* All errata must contain an action message.
return (ret);
}
+typedef struct target_exists_args {
+ const char *poolname;
+ uint64_t poolguid;
+} target_exists_args_t;
+
+static int
+name_or_guid_exists(zpool_handle_t *zhp, void *data)
+{
+ target_exists_args_t *args = data;
+ nvlist_t *config = zpool_get_config(zhp, NULL);
+ int found = 0;
+
+ if (config == NULL)
+ return (0);
+
+ if (args->poolname != NULL) {
+ char *pool_name;
+
+ verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
+ &pool_name) == 0);
+ if (strcmp(pool_name, args->poolname) == 0)
+ found = 1;
+ } else {
+ uint64_t pool_guid;
+
+ verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
+ &pool_guid) == 0);
+ if (pool_guid == args->poolguid)
+ found = 1;
+ }
+ zpool_close(zhp);
+
+ return (found);
+}
/*
* zpool checkpoint <pool>
* checkpoint --discard <pool>
boolean_t do_rewind = B_FALSE;
boolean_t xtreme_rewind = B_FALSE;
boolean_t do_scan = B_FALSE;
+ boolean_t pool_exists = B_FALSE;
uint64_t pool_state, txg = -1ULL;
char *cachefile = NULL;
importargs_t idata = { 0 };
/*
* User specified a name or guid. Ensure it's unique.
*/
- idata.unique = B_TRUE;
+ target_exists_args_t search = {searchname, searchguid};
+ pool_exists = zpool_iter(g_zfs, name_or_guid_exists, &search);
}
/*
idata.scan = do_scan;
idata.policy = policy;
- pools = zpool_search_import(g_zfs, &idata);
+ pools = zpool_search_import(g_zfs, &idata, &libzfs_config_ops);
- if (pools != NULL && idata.exists &&
+ if (pools != NULL && pool_exists &&
(argc == 1 || strcmp(argv[0], argv[1]) == 0)) {
(void) fprintf(stderr, gettext("cannot import '%s': "
"a pool with that name already exists\n"),
"<pool | id> <newpool>' to give it a new name\n"),
"zpool import");
err = 1;
- } else if (pools == NULL && idata.exists) {
+ } else if (pools == NULL && pool_exists) {
(void) fprintf(stderr, gettext("cannot import '%s': "
"a pool with that name is already created/imported,\n"),
argv[0]);
unsigned int columns; /* Center name to this number of columns */
} name_and_columns_t;
-#define IOSTAT_MAX_LABELS 11 /* Max number of labels on one line */
+#define IOSTAT_MAX_LABELS 13 /* Max number of labels on one line */
static const name_and_columns_t iostat_top_labels[][IOSTAT_MAX_LABELS] =
{
[IOS_DEFAULT] = {{"capacity", 2}, {"operations", 2}, {"bandwidth", 2},
{NULL}},
[IOS_LATENCY] = {{"total_wait", 2}, {"disk_wait", 2}, {"syncq_wait", 2},
- {"asyncq_wait", 2}, {"scrub"}},
+ {"asyncq_wait", 2}, {"scrub", 1}, {"trim", 1}, {NULL}},
[IOS_QUEUES] = {{"syncq_read", 2}, {"syncq_write", 2},
{"asyncq_read", 2}, {"asyncq_write", 2}, {"scrubq_read", 2},
- {NULL}},
- [IOS_L_HISTO] = {{"total_wait", 2}, {"disk_wait", 2},
- {"sync_queue", 2}, {"async_queue", 2}, {NULL}},
+ {"trimq_write", 2}, {NULL}},
+ [IOS_L_HISTO] = {{"total_wait", 2}, {"disk_wait", 2}, {"syncq_wait", 2},
+ {"asyncq_wait", 2}, {NULL}},
[IOS_RQ_HISTO] = {{"sync_read", 2}, {"sync_write", 2},
- {"async_read", 2}, {"async_write", 2}, {"scrub", 2}, {NULL}},
-
+ {"async_read", 2}, {"async_write", 2}, {"scrub", 2},
+ {"trim", 2}, {NULL}},
};
/* Shorthand - if "columns" field not set, default to 1 column */
[IOS_DEFAULT] = {{"alloc"}, {"free"}, {"read"}, {"write"}, {"read"},
{"write"}, {NULL}},
[IOS_LATENCY] = {{"read"}, {"write"}, {"read"}, {"write"}, {"read"},
- {"write"}, {"read"}, {"write"}, {"wait"}, {NULL}},
+ {"write"}, {"read"}, {"write"}, {"wait"}, {"wait"}, {NULL}},
[IOS_QUEUES] = {{"pend"}, {"activ"}, {"pend"}, {"activ"}, {"pend"},
- {"activ"}, {"pend"}, {"activ"}, {"pend"}, {"activ"}, {NULL}},
+ {"activ"}, {"pend"}, {"activ"}, {"pend"}, {"activ"},
+ {"pend"}, {"activ"}, {NULL}},
[IOS_L_HISTO] = {{"read"}, {"write"}, {"read"}, {"write"}, {"read"},
- {"write"}, {"read"}, {"write"}, {"scrub"}, {NULL}},
+ {"write"}, {"read"}, {"write"}, {"scrub"}, {"trim"}, {NULL}},
[IOS_RQ_HISTO] = {{"ind"}, {"agg"}, {"ind"}, {"agg"}, {"ind"}, {"agg"},
- {"ind"}, {"agg"}, {"ind"}, {"agg"}, {NULL}},
+ {"ind"}, {"agg"}, {"ind"}, {"agg"}, {"ind"}, {"agg"}, {NULL}},
};
static const char *histo_to_title[] = {
[IOS_DEFAULT] = 15, /* 1PB capacity */
[IOS_LATENCY] = 10, /* 1B ns = 10sec */
[IOS_QUEUES] = 6, /* 1M queue entries */
+ [IOS_L_HISTO] = 10, /* 1B ns = 10sec */
+ [IOS_RQ_HISTO] = 6, /* 1M queue entries */
};
if (cb->cb_literal)
const name_and_columns_t labels[][IOSTAT_MAX_LABELS])
{
int i, idx, s;
- unsigned int text_start, rw_column_width, spaces_to_end;
+ int text_start, rw_column_width, spaces_to_end;
uint64_t flags = cb->cb_flags;
uint64_t f;
unsigned int column_width = force_column_width;
rw_column_width = (column_width * columns) +
(2 * (columns - 1));
- text_start = (int)((rw_column_width)/columns -
- slen/columns);
+ text_start = (int)((rw_column_width) / columns -
+ slen / columns);
+ if (text_start < 0)
+ text_start = 0;
printf(" "); /* Two spaces between columns */
/* Print space after label to end of column */
spaces_to_end = rw_column_width - text_start - slen;
+ if (spaces_to_end < 0)
+ spaces_to_end = 0;
+
for (s = 0; s < spaces_to_end; s++)
printf(" ");
-
}
}
}
ZPOOL_CONFIG_VDEV_ASYNC_W_ACTIVE_QUEUE,
ZPOOL_CONFIG_VDEV_SCRUB_PEND_QUEUE,
ZPOOL_CONFIG_VDEV_SCRUB_ACTIVE_QUEUE,
+ ZPOOL_CONFIG_VDEV_TRIM_PEND_QUEUE,
+ ZPOOL_CONFIG_VDEV_TRIM_ACTIVE_QUEUE,
};
struct stat_array *nva;
ZPOOL_CONFIG_VDEV_ASYNC_R_LAT_HISTO,
ZPOOL_CONFIG_VDEV_ASYNC_W_LAT_HISTO,
ZPOOL_CONFIG_VDEV_SCRUB_LAT_HISTO,
+ ZPOOL_CONFIG_VDEV_TRIM_LAT_HISTO,
};
struct stat_array *nva;
return (columns);
}
-int
-get_namewidth(zpool_handle_t *zhp, void *data)
+/*
+ * Return the required length of the pool/vdev name column. The minimum
+ * allowed width and output formatting flags must be provided.
+ */
+static int
+get_namewidth(zpool_handle_t *zhp, int min_width, int flags, boolean_t verbose)
{
- iostat_cbdata_t *cb = data;
nvlist_t *config, *nvroot;
- int columns;
+ int width = min_width;
if ((config = zpool_get_config(zhp, NULL)) != NULL) {
verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
&nvroot) == 0);
unsigned int poolname_len = strlen(zpool_get_name(zhp));
- if (!cb->cb_verbose)
- cb->cb_namewidth = MAX(poolname_len, cb->cb_namewidth);
- else
- cb->cb_namewidth = MAX(poolname_len,
- max_width(zhp, nvroot, 0, cb->cb_namewidth,
- cb->cb_name_flags));
+ if (verbose == B_FALSE) {
+ width = MAX(poolname_len, min_width);
+ } else {
+ width = MAX(poolname_len,
+ max_width(zhp, nvroot, 0, min_width, flags));
+ }
}
- /*
- * The width must be at least 10, but may be as large as the
- * column width - 42 so that we can still fit in one line.
- */
- columns = get_columns();
-
- if (cb->cb_namewidth < 10)
- cb->cb_namewidth = 10;
- if (cb->cb_namewidth > columns - 42)
- cb->cb_namewidth = columns - 42;
- return (0);
+ return (width);
}
/*
free(sp);
}
+/*
+ * Set the minimum pool/vdev name column width. The width must be at least 10,
+ * but may be as large as the column width - 42 so it still fits on one line.
+ */
+static int
+get_namewidth_iostat(zpool_handle_t *zhp, void *data)
+{
+ iostat_cbdata_t *cb = data;
+ int width, columns;
+
+ width = get_namewidth(zhp, cb->cb_namewidth, cb->cb_name_flags,
+ cb->cb_verbose);
+ columns = get_columns();
+
+ if (width < 10)
+ width = 10;
+ if (width > columns - 42)
+ width = columns - 42;
+
+ cb->cb_namewidth = width;
+
+ return (0);
+}
+
/*
* zpool iostat [[-c [script1,script2,...]] [-lq]|[-rw]] [-ghHLpPvy] [-n name]
* [-T d|u] [[ pool ...]|[pool vdev ...]|[vdev ...]]
* -w Display latency histograms
* -r Display request size histogram
* -T Display a timestamp in date(1) or Unix format
+ * -n Only print headers once
*
* 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
int npools;
float interval = 0;
unsigned long count = 0;
+ struct winsize win;
+ int winheight = 24;
zpool_list_t *list;
boolean_t verbose = B_FALSE;
boolean_t latency = B_FALSE, l_histo = B_FALSE, rq_histo = B_FALSE;
boolean_t guid = B_FALSE;
boolean_t follow_links = B_FALSE;
boolean_t full_name = B_FALSE;
+ boolean_t headers_once = B_FALSE;
iostat_cbdata_t cb = { 0 };
char *cmd = NULL;
uint64_t unsupported_flags;
/* check options */
- while ((c = getopt(argc, argv, "c:gLPT:vyhplqrwH")) != -1) {
+ while ((c = getopt(argc, argv, "c:gLPT:vyhplqrwnH")) != -1) {
switch (c) {
case 'c':
if (cmd != NULL) {
case 'y':
omit_since_boot = B_TRUE;
break;
+ case 'n':
+ headers_once = B_TRUE;
+ break;
case 'h':
usage(B_FALSE);
break;
* for the pool / device name column across all pools.
*/
cb.cb_namewidth = 0;
- (void) pool_list_iter(list, B_FALSE, get_namewidth,
- &cb);
+ (void) pool_list_iter(list, B_FALSE,
+ get_namewidth_iostat, &cb);
if (timestamp_fmt != NODATE)
print_timestamp(timestamp_fmt);
cb.vcdl = NULL;
}
+ /*
+ * Are we connected to TTY? If not, headers_once
+ * should be true, to avoid breaking scripts.
+ */
+ if (isatty(fileno(stdout)) == 0)
+ headers_once = B_TRUE;
+
+ /*
+ * Check terminal size so we can print headers
+ * even when terminal window has its height
+ * changed.
+ */
+ if (headers_once == B_FALSE) {
+ if (ioctl(1, TIOCGWINSZ, &win) != -1 &&
+ win.ws_row > 0)
+ winheight = win.ws_row;
+ else
+ headers_once = B_TRUE;
+ }
+
/*
* If it's the first time and we're not skipping it,
* or either skip or verbose mode, print the header.
* every vdev, so skip this for histograms.
*/
if (((++cb.cb_iteration == 1 && !skip) ||
- (skip != verbose)) &&
+ (skip != verbose) ||
+ (!headers_once &&
+ (cb.cb_iteration % winheight) == 0)) &&
(!(cb.cb_flags & IOS_ANYHISTO_M)) &&
!cb.cb_scripted)
print_iostat_header(&cb);
continue;
}
-
pool_list_iter(list, B_FALSE, print_iostat, &cb);
/*
}
static void
-print_one_column(zpool_prop_t prop, uint64_t value, boolean_t scripted,
- boolean_t valid, enum zfs_nicenum_format format)
+print_one_column(zpool_prop_t prop, uint64_t value, const char *str,
+ boolean_t scripted, boolean_t valid, enum zfs_nicenum_format format)
{
char propval[64];
boolean_t fixed;
switch (prop) {
case ZPOOL_PROP_EXPANDSZ:
case ZPOOL_PROP_CHECKPOINT:
+ case ZPOOL_PROP_DEDUPRATIO:
if (value == 0)
(void) strlcpy(propval, "-", sizeof (propval));
else
value < 1000 ? "%1.2f%%" : value < 10000 ?
"%2.1f%%" : "%3.0f%%", value / 100.0);
break;
+ case ZPOOL_PROP_HEALTH:
+ width = 8;
+ snprintf(propval, sizeof (propval), "%-*s", (int)width, str);
+ break;
default:
zfs_nicenum_format(value, propval, sizeof (propval), format);
}
*/
void
print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
- list_cbdata_t *cb, int depth)
+ list_cbdata_t *cb, int depth, boolean_t isspare)
{
nvlist_t **child;
vdev_stat_t *vs;
char *vname;
boolean_t scripted = cb->cb_scripted;
uint64_t islog = B_FALSE;
- char *dashes = "%-*s - - - - - -\n";
+ char *dashes = "%-*s - - - - "
+ "- - - - -\n";
verify(nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_VDEV_STATS,
(uint64_t **)&vs, &c) == 0);
boolean_t toplevel = (vs->vs_space != 0);
uint64_t cap;
enum zfs_nicenum_format format;
+ const char *state;
if (cb->cb_literal)
format = ZFS_NICENUM_RAW;
* 'toplevel' boolean value is passed to the print_one_column()
* to indicate that the value is valid.
*/
- print_one_column(ZPOOL_PROP_SIZE, vs->vs_space, scripted,
- toplevel, format);
- print_one_column(ZPOOL_PROP_ALLOCATED, vs->vs_alloc, scripted,
+ print_one_column(ZPOOL_PROP_SIZE, vs->vs_space, NULL, scripted,
toplevel, format);
- print_one_column(ZPOOL_PROP_FREE, vs->vs_space - vs->vs_alloc,
+ print_one_column(ZPOOL_PROP_ALLOCATED, vs->vs_alloc, NULL,
scripted, toplevel, format);
+ print_one_column(ZPOOL_PROP_FREE, vs->vs_space - vs->vs_alloc,
+ NULL, scripted, toplevel, format);
print_one_column(ZPOOL_PROP_CHECKPOINT,
- vs->vs_checkpoint_space, scripted, toplevel, format);
- print_one_column(ZPOOL_PROP_EXPANDSZ, vs->vs_esize, scripted,
- B_TRUE, format);
+ vs->vs_checkpoint_space, NULL, scripted, toplevel, format);
+ print_one_column(ZPOOL_PROP_EXPANDSZ, vs->vs_esize, NULL,
+ scripted, B_TRUE, format);
print_one_column(ZPOOL_PROP_FRAGMENTATION,
- vs->vs_fragmentation, scripted,
+ vs->vs_fragmentation, NULL, scripted,
(vs->vs_fragmentation != ZFS_FRAG_INVALID && toplevel),
format);
cap = (vs->vs_space == 0) ? 0 :
(vs->vs_alloc * 10000 / vs->vs_space);
- print_one_column(ZPOOL_PROP_CAPACITY, cap, scripted, toplevel,
- format);
+ print_one_column(ZPOOL_PROP_CAPACITY, cap, NULL,
+ scripted, toplevel, format);
+ print_one_column(ZPOOL_PROP_DEDUPRATIO, 0, NULL,
+ scripted, toplevel, format);
+ state = zpool_state_to_name(vs->vs_state, vs->vs_aux);
+ if (isspare) {
+ if (vs->vs_aux == VDEV_AUX_SPARED)
+ state = "INUSE";
+ else if (vs->vs_state == VDEV_STATE_HEALTHY)
+ state = "AVAIL";
+ }
+ print_one_column(ZPOOL_PROP_HEALTH, 0, state, scripted,
+ B_TRUE, format);
(void) printf("\n");
}
vname = zpool_vdev_name(g_zfs, zhp, child[c],
cb->cb_name_flags);
- print_list_stats(zhp, vname, child[c], cb, depth + 2);
+ print_list_stats(zhp, vname, child[c], cb, depth + 2, B_FALSE);
free(vname);
}
}
vname = zpool_vdev_name(g_zfs, zhp, child[c],
cb->cb_name_flags);
- print_list_stats(zhp, vname, child[c], cb, depth + 2);
+ print_list_stats(zhp, vname, child[c], cb, depth + 2,
+ B_FALSE);
free(vname);
}
}
for (c = 0; c < children; c++) {
vname = zpool_vdev_name(g_zfs, zhp, child[c],
cb->cb_name_flags);
- print_list_stats(zhp, vname, child[c], cb, depth + 2);
+ print_list_stats(zhp, vname, child[c], cb, depth + 2,
+ B_FALSE);
free(vname);
}
}
for (c = 0; c < children; c++) {
vname = zpool_vdev_name(g_zfs, zhp, child[c],
cb->cb_name_flags);
- print_list_stats(zhp, vname, child[c], cb, depth + 2);
+ print_list_stats(zhp, vname, child[c], cb, depth + 2,
+ B_TRUE);
free(vname);
}
}
list_callback(zpool_handle_t *zhp, void *data)
{
list_cbdata_t *cbp = data;
- nvlist_t *config;
- nvlist_t *nvroot;
- config = zpool_get_config(zhp, NULL);
+ print_pool(zhp, cbp);
if (cbp->cb_verbose) {
- config = zpool_get_config(zhp, NULL);
+ nvlist_t *config, *nvroot;
+ config = zpool_get_config(zhp, NULL);
verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
&nvroot) == 0);
+ print_list_stats(zhp, NULL, nvroot, cbp, 0, B_FALSE);
}
- if (cbp->cb_verbose)
- cbp->cb_namewidth = max_width(zhp, nvroot, 0, 0,
- cbp->cb_name_flags);
+ return (0);
+}
- print_pool(zhp, cbp);
+/*
+ * Set the minimum pool/vdev name column width. The width must be at least 9,
+ * but may be as large as needed.
+ */
+static int
+get_namewidth_list(zpool_handle_t *zhp, void *data)
+{
+ list_cbdata_t *cb = data;
+ int width;
+
+ width = get_namewidth(zhp, cb->cb_namewidth, cb->cb_name_flags,
+ cb->cb_verbose);
+
+ if (width < 9)
+ width = 9;
- if (cbp->cb_verbose)
- print_list_stats(zhp, NULL, nvroot, cbp, 0);
+ cb->cb_namewidth = width;
return (0);
}
* -o List of properties to display. Defaults to
* "name,size,allocated,free,expandsize,fragmentation,capacity,"
* "dedupratio,health,altroot"
- * -p Display values in parsable (exact) format.
+ * -p Display values in parsable (exact) format.
* -P Display full path for vdev name.
* -T Display a timestamp in date(1) or Unix format
*
if (pool_list_count(list) == 0)
break;
+ cb.cb_namewidth = 0;
+ (void) pool_list_iter(list, B_FALSE, get_namewidth_list, &cb);
+
if (timestamp_fmt != NODATE)
print_timestamp(timestamp_fmt);
return (for_each_pool(argc, argv, B_TRUE, NULL, scrub_callback, &cb));
}
+/*
+ * zpool trim [-d] [-r <rate>] [-c | -s] <pool> [<device> ...]
+ *
+ * -c Cancel. Ends any in-progress trim.
+ * -d Secure trim. Requires kernel and device support.
+ * -r <rate> Sets the TRIM rate in bytes (per second). Supports
+ * adding a multiplier suffix such as 'k' or 'm'.
+ * -s Suspend. TRIM can then be restarted with no flags.
+ */
+int
+zpool_do_trim(int argc, char **argv)
+{
+ struct option long_options[] = {
+ {"cancel", no_argument, NULL, 'c'},
+ {"secure", no_argument, NULL, 'd'},
+ {"rate", required_argument, NULL, 'r'},
+ {"suspend", no_argument, NULL, 's'},
+ {0, 0, 0, 0}
+ };
+
+ pool_trim_func_t cmd_type = POOL_TRIM_START;
+ uint64_t rate = 0;
+ boolean_t secure = B_FALSE;
+
+ int c;
+ while ((c = getopt_long(argc, argv, "cdr:s", long_options, NULL))
+ != -1) {
+ switch (c) {
+ case 'c':
+ if (cmd_type != POOL_TRIM_START &&
+ cmd_type != POOL_TRIM_CANCEL) {
+ (void) fprintf(stderr, gettext("-c cannot be "
+ "combined with other options\n"));
+ usage(B_FALSE);
+ }
+ cmd_type = POOL_TRIM_CANCEL;
+ break;
+ case 'd':
+ if (cmd_type != POOL_TRIM_START) {
+ (void) fprintf(stderr, gettext("-d cannot be "
+ "combined with the -c or -s options\n"));
+ usage(B_FALSE);
+ }
+ secure = B_TRUE;
+ break;
+ case 'r':
+ if (cmd_type != POOL_TRIM_START) {
+ (void) fprintf(stderr, gettext("-r cannot be "
+ "combined with the -c or -s options\n"));
+ usage(B_FALSE);
+ }
+ if (zfs_nicestrtonum(NULL, optarg, &rate) == -1) {
+ (void) fprintf(stderr,
+ gettext("invalid value for rate\n"));
+ usage(B_FALSE);
+ }
+ break;
+ case 's':
+ if (cmd_type != POOL_TRIM_START &&
+ cmd_type != POOL_TRIM_SUSPEND) {
+ (void) fprintf(stderr, gettext("-s cannot be "
+ "combined with other options\n"));
+ usage(B_FALSE);
+ }
+ cmd_type = POOL_TRIM_SUSPEND;
+ break;
+ case '?':
+ if (optopt != 0) {
+ (void) fprintf(stderr,
+ gettext("invalid option '%c'\n"), optopt);
+ } else {
+ (void) fprintf(stderr,
+ gettext("invalid option '%s'\n"),
+ argv[optind - 1]);
+ }
+ usage(B_FALSE);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ (void) fprintf(stderr, gettext("missing pool name argument\n"));
+ usage(B_FALSE);
+ return (-1);
+ }
+
+ char *poolname = argv[0];
+ zpool_handle_t *zhp = zpool_open(g_zfs, poolname);
+ if (zhp == NULL)
+ return (-1);
+
+ trimflags_t trim_flags = {
+ .secure = secure,
+ .rate = rate,
+ };
+
+ nvlist_t *vdevs = fnvlist_alloc();
+ if (argc == 1) {
+ /* no individual leaf vdevs specified, so add them all */
+ nvlist_t *config = zpool_get_config(zhp, NULL);
+ nvlist_t *nvroot = fnvlist_lookup_nvlist(config,
+ ZPOOL_CONFIG_VDEV_TREE);
+ zpool_collect_leaves(zhp, nvroot, vdevs);
+ trim_flags.fullpool = B_TRUE;
+ } else {
+ trim_flags.fullpool = B_FALSE;
+ for (int i = 1; i < argc; i++) {
+ fnvlist_add_boolean(vdevs, argv[i]);
+ }
+ }
+
+ int error = zpool_trim(zhp, cmd_type, vdevs, &trim_flags);
+
+ fnvlist_free(vdevs);
+ zpool_close(zhp);
+
+ return (error);
+}
/*
* Print out detailed scrub status.
scan_rate = pass_scanned / elapsed;
issue_rate = pass_issued / elapsed;
- total_secs_left = (issue_rate != 0) ?
+ total_secs_left = (issue_rate != 0 && total >= issued) ?
((total - issued) / issue_rate) : UINT64_MAX;
days_left = total_secs_left / 60 / 60 / 24;
}
if (pause == 0) {
- if (issue_rate >= 10 * 1024 * 1024) {
+ if (total_secs_left != UINT64_MAX &&
+ issue_rate >= 10 * 1024 * 1024) {
(void) printf(gettext(", %llu days "
"%02llu:%02llu:%02llu to go\n"),
(u_longlong_t)days_left, (u_longlong_t)hours_left,
"mount existing encrypted datasets readonly.\n"));
break;
+ case ZPOOL_ERRATA_ZOL_8308_ENCRYPTION:
+ (void) printf(gettext("\tExisting encrypted snapshots "
+ "and bookmarks contain an on-disk\n\tincompat"
+ "ibility. This may cause on-disk corruption if "
+ "they are used\n\twith 'zfs recv'.\n"));
+ (void) printf(gettext("action: To correct the issue, "
+ "enable the bookmark_v2 feature. No additional\n\t"
+ "action is needed if there are no encrypted "
+ "snapshots or bookmarks.\n\tIf preserving the "
+ "encrypted snapshots and bookmarks is required, "
+ "use\n\ta non-raw send to backup and restore them. "
+ "Alternately, they may be\n\tremoved to resolve "
+ "the incompatibility.\n"));
+ break;
+
default:
/*
* All errata which allow the pool to be imported
cbp->cb_namewidth, "NAME", "STATE", "READ", "WRITE",
"CKSUM");
+ if (cbp->cb_print_slow_ios)
+ (void) printf(" %5s", gettext("SLOW"));
+
if (cbp->vcdl != NULL)
print_cmd_columns(cbp->vcdl, 0);
}
/*
- * zpool status [-c [script1,script2,...]] [-gLPvx] [-T d|u] [pool] ...
+ * zpool status [-c [script1,script2,...]] [-igLpPstvx] [-T d|u] [pool] ...
* [interval [count]]
*
* -c CMD For each vdev, run command CMD
+ * -i Display vdev initialization status.
* -g Display guid for individual vdev name.
* -L Follow links when resolving vdev path name.
+ * -p Display values in parsable (exact) format.
* -P Display full path for vdev name.
+ * -s Display slow IOs column.
* -v Display complete error logs
* -x Display only pools with potential problems
* -D Display dedup status (undocumented)
+ * -t Display vdev TRIM status.
* -T Display a timestamp in date(1) or Unix format
*
* Describes the health status of all pools or some subset.
char *cmd = NULL;
/* check options */
- while ((c = getopt(argc, argv, "c:gLPvxDT:")) != -1) {
+ while ((c = getopt(argc, argv, "c:igLpPsvxDtT:")) != -1) {
switch (c) {
case 'c':
if (cmd != NULL) {
}
cmd = optarg;
break;
+ case 'i':
+ cb.cb_print_vdev_init = B_TRUE;
+ break;
case 'g':
cb.cb_name_flags |= VDEV_NAME_GUID;
break;
case 'L':
cb.cb_name_flags |= VDEV_NAME_FOLLOW_LINKS;
break;
+ case 'p':
+ cb.cb_literal = B_TRUE;
+ break;
case 'P':
cb.cb_name_flags |= VDEV_NAME_PATH;
break;
+ case 's':
+ cb.cb_print_slow_ios = B_TRUE;
+ break;
case 'v':
cb.cb_verbose = B_TRUE;
break;
case 'D':
cb.cb_dedup_stats = B_TRUE;
break;
+ case 't':
+ cb.cb_print_vdev_trim = B_TRUE;
+ break;
case 'T':
get_timestamp_arg(*optarg);
break;
return (1);
}
+/*
+ * Display version message
+ */
+static int
+zpool_do_version(int argc, char **argv)
+{
+ if (zfs_version_print() == -1)
+ return (1);
+
+ return (0);
+}
+
int
main(int argc, char **argv)
{
if ((strcmp(cmdname, "-?") == 0) || strcmp(cmdname, "--help") == 0)
usage(B_TRUE);
+ /*
+ * Special case '-V|--version'
+ */
+ if ((strcmp(cmdname, "-V") == 0) || (strcmp(cmdname, "--version") == 0))
+ return (zpool_do_version(argc, argv));
+
if ((g_zfs = libzfs_init()) == NULL) {
(void) fprintf(stderr, "%s", libzfs_error_init(errno));
return (1);