Fix 'zpool list -H' error code
[zfs.git] / cmd / zpool / zpool_main.c
index 54bba86..b96fbe4 100644 (file)
  */
 
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright (c) 2012 by Frederik Wessels. All rights reserved.
+ * Copyright (c) 2012 by Cyril Plisko. All rights reserved.
  */
 
 #include <assert.h>
 #include <priv.h>
 #include <pwd.h>
 #include <zone.h>
+#include <zfs_prop.h>
 #include <sys/fs/zfs.h>
-
 #include <sys/stat.h>
+#include <sys/fm/util.h>
+#include <sys/fm/protocol.h>
 
 #include <libzfs.h>
 
 #include "zpool_util.h"
 #include "zfs_comutil.h"
+#include "zfeature_common.h"
+
+#include "statcommon.h"
 
 static int zpool_do_create(int, char **);
 static int zpool_do_destroy(int, char **);
 
 static int zpool_do_add(int, char **);
 static int zpool_do_remove(int, char **);
+static int zpool_do_labelclear(int, char **);
 
 static int zpool_do_list(int, char **);
 static int zpool_do_iostat(int, char **);
@@ -63,10 +72,14 @@ static int zpool_do_status(int, char **);
 static int zpool_do_online(int, char **);
 static int zpool_do_offline(int, char **);
 static int zpool_do_clear(int, char **);
+static int zpool_do_reopen(int, char **);
+
+static int zpool_do_reguid(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 **);
 
@@ -76,6 +89,7 @@ static int zpool_do_export(int, char **);
 static int zpool_do_upgrade(int, char **);
 
 static int zpool_do_history(int, char **);
+static int zpool_do_events(int, char **);
 
 static int zpool_do_get(int, char **);
 static int zpool_do_set(int, char **);
@@ -110,6 +124,7 @@ typedef enum {
        HELP_HISTORY,
        HELP_IMPORT,
        HELP_IOSTAT,
+       HELP_LABELCLEAR,
        HELP_LIST,
        HELP_OFFLINE,
        HELP_ONLINE,
@@ -118,8 +133,12 @@ typedef enum {
        HELP_SCRUB,
        HELP_STATUS,
        HELP_UPGRADE,
+       HELP_EVENTS,
        HELP_GET,
-       HELP_SET
+       HELP_SET,
+       HELP_SPLIT,
+       HELP_REGUID,
+       HELP_REOPEN
 } zpool_help_t;
 
 
@@ -145,6 +164,8 @@ static zpool_command_t command_table[] = {
        { "add",        zpool_do_add,           HELP_ADD                },
        { "remove",     zpool_do_remove,        HELP_REMOVE             },
        { NULL },
+       { "labelclear", zpool_do_labelclear,    HELP_LABELCLEAR         },
+       { NULL },
        { "list",       zpool_do_list,          HELP_LIST               },
        { "iostat",     zpool_do_iostat,        HELP_IOSTAT             },
        { "status",     zpool_do_status,        HELP_STATUS             },
@@ -152,18 +173,23 @@ static zpool_command_t command_table[] = {
        { "online",     zpool_do_online,        HELP_ONLINE             },
        { "offline",    zpool_do_offline,       HELP_OFFLINE            },
        { "clear",      zpool_do_clear,         HELP_CLEAR              },
+       { "reopen",     zpool_do_reopen,        HELP_REOPEN             },
        { NULL },
        { "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 },
        { "import",     zpool_do_import,        HELP_IMPORT             },
        { "export",     zpool_do_export,        HELP_EXPORT             },
        { "upgrade",    zpool_do_upgrade,       HELP_UPGRADE            },
+       { "reguid",     zpool_do_reguid,        HELP_REGUID             },
        { NULL },
        { "history",    zpool_do_history,       HELP_HISTORY            },
+       { "events",     zpool_do_events,        HELP_EVENTS             },
+       { NULL },
        { "get",        zpool_do_get,           HELP_GET                },
        { "set",        zpool_do_set,           HELP_SET                },
 };
@@ -173,18 +199,21 @@ 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) {
        case HELP_ADD:
-               return (gettext("\tadd [-fn] <pool> <vdev> ...\n"));
+               return (gettext("\tadd [-fn] [-o property=value] "
+                   "<pool> <vdev> ...\n"));
        case HELP_ATTACH:
-               return (gettext("\tattach [-f] <pool> <device> "
-                   "<new-device>\n"));
+               return (gettext("\tattach [-f] [-o property=value] "
+                   "<pool> <device> <new-device>\n"));
        case HELP_CLEAR:
-               return (gettext("\tclear <pool> [device]\n"));
+               return (gettext("\tclear [-nF] <pool> [device]\n"));
        case HELP_CREATE:
-               return (gettext("\tcreate [-fn] [-o property=value] ... \n"
+               return (gettext("\tcreate [-fnd] [-o property=value] ... \n"
                    "\t    [-O file-system-property=value] ... \n"
                    "\t    [-m mountpoint] [-R root] <pool> <vdev> ...\n"));
        case HELP_DESTROY:
@@ -197,17 +226,22 @@ get_usage(zpool_help_t idx) {
                return (gettext("\thistory [-il] [<pool>] ...\n"));
        case HELP_IMPORT:
                return (gettext("\timport [-d dir] [-D]\n"
+                   "\timport [-d dir | -c cachefile] [-F [-n]] <pool | id>\n"
                    "\timport [-o mntopts] [-o property=value] ... \n"
-                   "\t    [-d dir | -c cachefile] [-D] [-f] [-R root] -a\n"
+                   "\t    [-d dir | -c cachefile] [-D] [-f] [-m] [-N] "
+                   "[-R root] [-F [-n]] -a\n"
                    "\timport [-o mntopts] [-o property=value] ... \n"
-                   "\t    [-d dir | -c cachefile] [-D] [-f] [-R root] "
-                   "<pool | id> [newpool]\n"));
+                   "\t    [-d dir | -c cachefile] [-D] [-f] [-m] [-N] "
+                   "[-R root] [-F [-n]]\n"
+                   "\t    <pool | id> [newpool]\n"));
        case HELP_IOSTAT:
-               return (gettext("\tiostat [-v] [pool] ... [interval "
+               return (gettext("\tiostat [-v] [-T d|u] [pool] ... [interval "
                    "[count]]\n"));
+       case HELP_LABELCLEAR:
+               return (gettext("\tlabelclear [-f] <vdev>\n"));
        case HELP_LIST:
-               return (gettext("\tlist [-H] [-o property[,...]] "
-                   "[pool] ...\n"));
+               return (gettext("\tlist [-Hv] [-o property[,...]] "
+                   "[-T d|u] [pool] ... [interval [count]]\n"));
        case HELP_OFFLINE:
                return (gettext("\toffline [-t] <pool> <device> ...\n"));
        case HELP_ONLINE:
@@ -217,19 +251,30 @@ get_usage(zpool_help_t idx) {
                    "[new-device]\n"));
        case HELP_REMOVE:
                return (gettext("\tremove <pool> <device> ...\n"));
+       case HELP_REOPEN:
+               return (gettext("\treopen <pool>\n"));
        case HELP_SCRUB:
                return (gettext("\tscrub [-s] <pool> ...\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"
                    "\tupgrade [-V version] <-a | pool ...>\n"));
+       case HELP_EVENTS:
+               return (gettext("\tevents [-vHfc]\n"));
        case HELP_GET:
                return (gettext("\tget <\"all\" | property[,...]> "
                    "<pool> ...\n"));
        case HELP_SET:
                return (gettext("\tset <property=value> <pool> \n"));
+       case HELP_SPLIT:
+               return (gettext("\tsplit [-n] [-R altroot] [-o mntopts]\n"
+                   "\t    [-o property=value] <pool> <newpool> "
+                   "[<device> ...]\n"));
+       case HELP_REGUID:
+               return (gettext("\treguid <pool>\n"));
        }
 
        abort();
@@ -245,12 +290,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,12 +342,18 @@ 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 */
                (void) zprop_iter(print_prop_cb, fp, B_FALSE, B_TRUE,
                    ZFS_TYPE_POOL);
+
+               (void) fprintf(fp, "\t%-15s   ", "feature@...");
+               (void) fprintf(fp, "YES   disabled | enabled | active\n");
+
+               (void) fprintf(fp, gettext("\nThe feature@ properties must be "
+                   "appended with a feature name.\nSee zpool-features(5).\n"));
        }
 
        /*
@@ -339,13 +390,25 @@ 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);
        }
 }
 
+static boolean_t
+prop_list_contains_feature(nvlist_t *proplist)
+{
+       nvpair_t *nvp;
+       for (nvp = nvlist_next_nvpair(proplist, NULL); NULL != nvp;
+           nvp = nvlist_next_nvpair(proplist, nvp)) {
+               if (zpool_prop_feature(nvpair_name(nvp)))
+                       return (B_TRUE);
+       }
+       return (B_FALSE);
+}
+
 /*
  * Add a property pair (name, string-value) into a property nvlist.
  */
@@ -369,19 +432,40 @@ add_prop_list(const char *propname, char *propval, nvlist_t **props,
        proplist = *props;
 
        if (poolprop) {
-               if ((prop = zpool_name_to_prop(propname)) == ZPROP_INVAL) {
+               const char *vname = zpool_prop_to_name(ZPOOL_PROP_VERSION);
+
+               if ((prop = zpool_name_to_prop(propname)) == ZPROP_INVAL &&
+                   !zpool_prop_feature(propname)) {
                        (void) fprintf(stderr, gettext("property '%s' is "
                            "not a valid pool property\n"), propname);
                        return (2);
                }
-               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);
+
+               /*
+                * feature@ properties and version should not be specified
+                * at the same time.
+                */
+               if ((prop == ZPROP_INVAL && zpool_prop_feature(propname) &&
+                   nvlist_exists(proplist, vname)) ||
+                   (prop == ZPOOL_PROP_VERSION &&
+                   prop_list_contains_feature(proplist))) {
+                       (void) fprintf(stderr, gettext("'feature@' and "
+                           "'version' properties cannot be specified "
+                           "together\n"));
                        return (2);
                }
-               normnm = zfs_prop_to_name(fprop);
+
+
+               if (zpool_prop_feature(propname))
+                       normnm = propname;
+               else
+                       normnm = zpool_prop_to_name(prop);
+       } else {
+               if ((fprop = zfs_name_to_prop(propname)) != ZPROP_INVAL) {
+                       normnm = zfs_prop_to_name(fprop);
+               } else {
+                       normnm = propname;
+               }
        }
 
        if (nvlist_lookup_string(proplist, normnm, &strval) == 0 &&
@@ -401,11 +485,12 @@ add_prop_list(const char *propname, char *propval, nvlist_t **props,
 }
 
 /*
- * zpool add [-fn] <pool> <vdev> ...
+ * zpool add [-fn] [-o property=value] <pool> <vdev> ...
  *
  *     -f      Force addition of devices, even if they appear in use
  *     -n      Do not add the devices, but display the resulting layout if
  *             they were to be added.
+ *     -o      Set property=value.
  *
  * Adds the given vdevs to 'pool'.  As with create, the bulk of this work is
  * handled by get_vdev_spec(), which constructs the nvlist needed to pass to
@@ -422,9 +507,11 @@ zpool_do_add(int argc, char **argv)
        int ret;
        zpool_handle_t *zhp;
        nvlist_t *config;
+       nvlist_t *props = NULL;
+       char *propval;
 
        /* check options */
-       while ((c = getopt(argc, argv, "fn")) != -1) {
+       while ((c = getopt(argc, argv, "fno:")) != -1) {
                switch (c) {
                case 'f':
                        force = B_TRUE;
@@ -432,6 +519,19 @@ zpool_do_add(int argc, char **argv)
                case 'n':
                        dryrun = B_TRUE;
                        break;
+               case 'o':
+                       if ((propval = strchr(optarg, '=')) == NULL) {
+                               (void) fprintf(stderr, gettext("missing "
+                                   "'=' for -o option\n"));
+                               usage(B_FALSE);
+                       }
+                       *propval = '\0';
+                       propval++;
+
+                       if ((strcmp(optarg, ZPOOL_CONFIG_ASHIFT) != 0) ||
+                           (add_prop_list(optarg, propval, &props, B_TRUE)))
+                               usage(B_FALSE);
+                       break;
                case '?':
                        (void) fprintf(stderr, gettext("invalid option '%c'\n"),
                            optopt);
@@ -468,7 +568,7 @@ zpool_do_add(int argc, char **argv)
        }
 
        /* pass off to get_vdev_spec for processing */
-       nvroot = make_root_vdev(zhp, force, !force, B_FALSE, dryrun,
+       nvroot = make_root_vdev(zhp, props, force, !force, B_FALSE, dryrun,
            argc, argv);
        if (nvroot == NULL) {
                zpool_close(zhp);
@@ -501,6 +601,7 @@ zpool_do_add(int argc, char **argv)
                ret = (zpool_add(zhp, nvroot) != 0);
        }
 
+       nvlist_free(props);
        nvlist_free(nvroot);
        zpool_close(zhp);
 
@@ -508,11 +609,10 @@ zpool_do_add(int argc, char **argv)
 }
 
 /*
- * zpool remove <pool> <vdev> ...
+ * zpool remove  <pool> <vdev> ...
  *
- * 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)
@@ -548,7 +648,128 @@ zpool_do_remove(int argc, char **argv)
 }
 
 /*
- * zpool create [-fn] [-o property=value] ...
+ * zpool labelclear <vdev>
+ *
+ * Verifies that the vdev is not active and zeros out the label information
+ * on the device.
+ */
+int
+zpool_do_labelclear(int argc, char **argv)
+{
+       char *vdev, *name;
+       int c, fd = -1, ret = 0;
+       pool_state_t state;
+       boolean_t inuse = B_FALSE;
+       boolean_t force = B_FALSE;
+
+       /* check options */
+       while ((c = getopt(argc, argv, "f")) != -1) {
+               switch (c) {
+               case 'f':
+                       force = B_TRUE;
+                       break;
+               default:
+                       (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+                           optopt);
+                       usage(B_FALSE);
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+
+       /* get vdev name */
+       if (argc < 1) {
+               (void) fprintf(stderr, gettext("missing vdev device name\n"));
+               usage(B_FALSE);
+       }
+
+       vdev = argv[0];
+       if ((fd = open(vdev, O_RDWR)) < 0) {
+               (void) fprintf(stderr, gettext("Unable to open %s\n"), vdev);
+               return (B_FALSE);
+       }
+
+       name = NULL;
+       if (zpool_in_use(g_zfs, fd, &state, &name, &inuse) != 0) {
+               if (force)
+                       goto wipe_label;
+
+               (void) fprintf(stderr,
+                   gettext("Unable to determine pool state for %s\n"
+                   "Use -f to force the clearing any label data\n"), vdev);
+
+               return (1);
+       }
+
+       if (inuse) {
+               switch (state) {
+               default:
+               case POOL_STATE_ACTIVE:
+               case POOL_STATE_SPARE:
+               case POOL_STATE_L2CACHE:
+                       (void) fprintf(stderr,
+                           gettext("labelclear operation failed.\n"
+                           "\tVdev %s is a member (%s), of pool \"%s\".\n"
+                           "\tTo remove label information from this device, "
+                           "export or destroy\n\tthe pool, or remove %s from "
+                           "the configuration of this pool\n\tand retry the "
+                           "labelclear operation.\n"),
+                           vdev, zpool_pool_state_to_name(state), name, vdev);
+                       ret = 1;
+                       goto errout;
+
+               case POOL_STATE_EXPORTED:
+                       if (force)
+                               break;
+
+                       (void) fprintf(stderr,
+                           gettext("labelclear operation failed.\n\tVdev "
+                           "%s is a member of the exported pool \"%s\".\n"
+                           "\tUse \"zpool labelclear -f %s\" to force the "
+                           "removal of label\n\tinformation.\n"),
+                           vdev, name, vdev);
+                       ret = 1;
+                       goto errout;
+
+               case POOL_STATE_POTENTIALLY_ACTIVE:
+                       if (force)
+                               break;
+
+                       (void) fprintf(stderr,
+                           gettext("labelclear operation failed.\n"
+                           "\tVdev %s is a member of the pool \"%s\".\n"
+                           "\tThis pool is unknown to this system, but may "
+                           "be active on\n\tanother system. Use "
+                           "\'zpool labelclear -f %s\' to force the\n"
+                           "\tremoval of label information.\n"),
+                           vdev, name, vdev);
+                       ret = 1;
+                       goto errout;
+
+               case POOL_STATE_DESTROYED:
+                       /* inuse should never be set for a destroyed pool... */
+                       break;
+               }
+       }
+
+wipe_label:
+       if (zpool_clear_label(fd) != 0) {
+               (void) fprintf(stderr,
+                   gettext("Label clear failed on vdev %s\n"), vdev);
+               ret = 1;
+       }
+
+errout:
+       close(fd);
+       if (name != NULL)
+               free(name);
+
+       return (ret);
+}
+
+/*
+ * zpool create [-fnd] [-o property=value] ...
  *             [-O file-system-property=value] ...
  *             [-R root] [-m mountpoint] <pool> <dev> ...
  *
@@ -557,8 +778,10 @@ zpool_do_remove(int argc, char **argv)
  *             were to be created.
  *      -R     Create a pool under an alternate root
  *      -m     Set default mountpoint for the root dataset.  By default it's
- *             '/<pool>'
+ *             '/<pool>'
  *     -o      Set property=value.
+ *     -d      Don't automatically enable all supported pool features
+ *             (individual features can be enabled with -o).
  *     -O      Set fsproperty=value in the pool's root file system
  *
  * Creates the named pool according to the given vdev specification.  The
@@ -571,6 +794,7 @@ zpool_do_create(int argc, char **argv)
 {
        boolean_t force = B_FALSE;
        boolean_t dryrun = B_FALSE;
+       boolean_t enable_all_pool_feat = B_TRUE;
        int c;
        nvlist_t *nvroot = NULL;
        char *poolname;
@@ -582,7 +806,7 @@ zpool_do_create(int argc, char **argv)
        char *propval;
 
        /* check options */
-       while ((c = getopt(argc, argv, ":fnR:m:o:O:")) != -1) {
+       while ((c = getopt(argc, argv, ":fndR:m:o:O:")) != -1) {
                switch (c) {
                case 'f':
                        force = B_TRUE;
@@ -590,6 +814,9 @@ zpool_do_create(int argc, char **argv)
                case 'n':
                        dryrun = B_TRUE;
                        break;
+               case 'd':
+                       enable_all_pool_feat = B_FALSE;
+                       break;
                case 'R':
                        altroot = optarg;
                        if (add_prop_list(zpool_prop_to_name(
@@ -617,6 +844,21 @@ zpool_do_create(int argc, char **argv)
 
                        if (add_prop_list(optarg, propval, &props, B_TRUE))
                                goto errout;
+
+                       /*
+                        * If the user is creating a pool that doesn't support
+                        * feature flags, don't enable any features.
+                        */
+                       if (zpool_name_to_prop(optarg) == ZPOOL_PROP_VERSION) {
+                               char *end;
+                               u_longlong_t ver;
+
+                               ver = strtoull(propval, &end, 10);
+                               if (*end == '\0' &&
+                                   ver < SPA_VERSION_FEATURES) {
+                                       enable_all_pool_feat = B_FALSE;
+                               }
+                       }
                        break;
                case 'O':
                        if ((propval = strchr(optarg, '=')) == NULL) {
@@ -669,7 +911,7 @@ zpool_do_create(int argc, char **argv)
        }
 
        /* pass off to get_vdev_spec for bulk processing */
-       nvroot = make_root_vdev(NULL, force, !force, B_FALSE, dryrun,
+       nvroot = make_root_vdev(NULL, props, force, !force, B_FALSE, dryrun,
            argc - 1, argv + 1);
        if (nvroot == NULL)
                goto errout;
@@ -682,7 +924,6 @@ zpool_do_create(int argc, char **argv)
                goto errout;
        }
 
-
        if (altroot != NULL && altroot[0] != '/') {
                (void) fprintf(stderr, gettext("invalid alternate root '%s': "
                    "must be an absolute path\n"), altroot);
@@ -764,6 +1005,27 @@ zpool_do_create(int argc, char **argv)
                /*
                 * Hand off to libzfs.
                 */
+               if (enable_all_pool_feat) {
+                       int i;
+                       for (i = 0; i < SPA_FEATURES; i++) {
+                               char propname[MAXPATHLEN];
+                               zfeature_info_t *feat = &spa_feature_table[i];
+
+                               (void) snprintf(propname, sizeof (propname),
+                                   "feature@%s", feat->fi_uname);
+
+                               /*
+                                * Skip feature if user specified it manually
+                                * on the command line.
+                                */
+                               if (nvlist_exists(props, propname))
+                                       continue;
+
+                               if (add_prop_list(propname, ZFS_FEATURE_ENABLED,
+                                   &props, B_TRUE) != 0)
+                                       goto errout;
+                       }
+               }
                if (zpool_create(g_zfs, poolname,
                    nvroot, props, fsprops) == 0) {
                        zfs_handle_t *pool = zfs_open(g_zfs, poolname,
@@ -877,17 +1139,21 @@ int
 zpool_do_export(int argc, char **argv)
 {
        boolean_t force = B_FALSE;
+       boolean_t hardforce = B_FALSE;
        int c;
        zpool_handle_t *zhp;
        int ret;
        int i;
 
        /* check options */
-       while ((c = getopt(argc, argv, "f")) != -1) {
+       while ((c = getopt(argc, argv, "fF")) != -1) {
                switch (c) {
                case 'f':
                        force = B_TRUE;
                        break;
+               case 'F':
+                       hardforce = B_TRUE;
+                       break;
                case '?':
                        (void) fprintf(stderr, gettext("invalid option '%c'\n"),
                            optopt);
@@ -917,8 +1183,12 @@ zpool_do_export(int argc, char **argv)
                        continue;
                }
 
-               if (zpool_export(zhp, force) != 0)
+               if (hardforce) {
+                       if (zpool_export_force(zhp) != 0)
+                               ret = 1;
+               } else if (zpool_export(zhp, force) != 0) {
                        ret = 1;
+               }
 
                zpool_close(zhp);
        }
@@ -933,7 +1203,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;
@@ -971,31 +1241,103 @@ 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 the configuration of an exported pool.  Iterate over all vdevs in the
- * pool, printing out the name and status for each one.
+ * Print out configuration state as requested by status_callback.
  */
 void
-print_import_config(const char *name, nvlist_t *nv, int namewidth, int depth,
-    boolean_t print_logs)
+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 *type, *vname;
+       char rbuf[6], wbuf[6], cbuf[6];
+       char *vname;
+       uint64_t notpresent;
+       spare_cbdata_t cb;
+       char *state;
 
-       verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) == 0);
-       if (strcmp(type, VDEV_TYPE_MISSING) == 0)
-               return;
+       if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
+           &child, &children) != 0)
+               children = 0;
 
-       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);
-       (void) printf("  %s", zpool_state_to_name(vs->vs_state, vs->vs_aux));
+       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";
+       }
 
-       if (vs->vs_aux != 0) {
+       (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,
+           &notpresent) == 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) {
@@ -1015,32 +1357,156 @@ print_import_config(const char *name, nvlist_t *nv, int namewidth, int depth,
                        (void) printf(gettext("newer version"));
                        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,
+                           &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) printf("\n");
 
-       if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
-           &child, &children) != 0)
-               return;
+       (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 is_log = B_FALSE;
+               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,
-                   &is_log);
-               if ((is_log && !print_logs) || (!is_log && print_logs))
+                   &islog);
+               (void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_HOLE,
+                   &ishole);
+               if (islog || ishole)
                        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, 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)
+{
+       nvlist_t **child;
+       uint_t c, children;
+       vdev_stat_t *vs;
+       char *type, *vname;
+
+       verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) == 0);
+       if (strcmp(type, VDEV_TYPE_MISSING) == 0 ||
+           strcmp(type, VDEV_TYPE_HOLE) == 0)
+               return;
+
+       verify(nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_VDEV_STATS,
+           (uint64_t **)&vs, &c) == 0);
+
+       (void) printf("\t%*s%-*s", depth, "", namewidth - depth, name);
+       (void) printf("  %s", zpool_state_to_name(vs->vs_state, vs->vs_aux));
+
+       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_UNSUP_FEAT:
+                       (void) printf(gettext("unsupported feature(s)"));
+                       break;
+
+               case VDEV_AUX_ERR_EXCEEDED:
+                       (void) printf(gettext("too many errors"));
+                       break;
+
+               default:
+                       (void) printf(gettext("corrupted data"));
+                       break;
+               }
+       }
+       (void) printf("\n");
+
+       if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
+           &child, &children) != 0)
+               return;
+
+       for (c = 0; c < children; c++) {
+               uint64_t is_log = B_FALSE;
+
+               (void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG,
+                   &is_log);
+               if (is_log)
+                       continue;
+
+               vname = zpool_vdev_name(g_zfs, NULL, child[c], B_TRUE);
+               print_import_config(vname, child[c], namewidth, depth + 2);
                free(vname);
        }
 
@@ -1048,7 +1514,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);
                }
@@ -1058,7 +1524,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);
                }
@@ -1066,6 +1532,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
@@ -1081,6 +1585,7 @@ show_import(nvlist_t *config)
        const char *health;
        uint_t vsc;
        int namewidth;
+       char *comment;
 
        verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
            &name) == 0);
@@ -1091,15 +1596,15 @@ 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);
 
        reason = zpool_import_status(config, &msgid);
 
-       (void) printf(gettext("  pool: %s\n"), name);
-       (void) printf(gettext("    id: %llu\n"), (u_longlong_t)guid);
-       (void) printf(gettext(" state: %s"), health);
+       (void) printf(gettext("   pool: %s\n"), name);
+       (void) printf(gettext("     id: %llu\n"), (u_longlong_t)guid);
+       (void) printf(gettext("  state: %s"), health);
        if (pool_state == POOL_STATE_DESTROYED)
                (void) printf(gettext(" (DESTROYED)"));
        (void) printf("\n");
@@ -1108,56 +1613,81 @@ show_import(nvlist_t *config)
        case ZPOOL_STATUS_MISSING_DEV_R:
        case ZPOOL_STATUS_MISSING_DEV_NR:
        case ZPOOL_STATUS_BAD_GUID_SUM:
-               (void) printf(gettext("status: One or more devices are missing "
-                   "from the system.\n"));
+               (void) printf(gettext(" status: One or more devices are "
+                   "missing from the system.\n"));
                break;
 
        case ZPOOL_STATUS_CORRUPT_LABEL_R:
        case ZPOOL_STATUS_CORRUPT_LABEL_NR:
-               (void) printf(gettext("status: One or more devices contains "
+               (void) printf(gettext(" status: One or more devices contains "
                    "corrupted data.\n"));
                break;
 
        case ZPOOL_STATUS_CORRUPT_DATA:
-               (void) printf(gettext("status: The pool data is corrupted.\n"));
+               (void) printf(
+                   gettext(" status: The pool data is corrupted.\n"));
                break;
 
        case ZPOOL_STATUS_OFFLINE_DEV:
-               (void) printf(gettext("status: One or more devices "
+               (void) printf(gettext(" status: One or more devices "
                    "are offlined.\n"));
                break;
 
        case ZPOOL_STATUS_CORRUPT_POOL:
-               (void) printf(gettext("status: The pool metadata is "
+               (void) printf(gettext(" status: The pool metadata is "
                    "corrupted.\n"));
                break;
 
        case ZPOOL_STATUS_VERSION_OLDER:
-               (void) printf(gettext("status: The pool is formatted using an "
-                   "older on-disk version.\n"));
+               (void) printf(gettext(" status: The pool is formatted using a "
+                   "legacy on-disk version.\n"));
                break;
 
        case ZPOOL_STATUS_VERSION_NEWER:
-               (void) printf(gettext("status: The pool is formatted using an "
+               (void) printf(gettext(" status: The pool is formatted using an "
                    "incompatible version.\n"));
                break;
 
+       case ZPOOL_STATUS_FEAT_DISABLED:
+               (void) printf(gettext(" status: Some supported features are "
+                   "not enabled on the pool.\n"));
+               break;
+
+       case ZPOOL_STATUS_UNSUP_FEAT_READ:
+               (void) printf(gettext("status: The pool uses the following "
+                   "feature(s) not supported on this sytem:\n"));
+               zpool_print_unsup_feat(config);
+               break;
+
+       case ZPOOL_STATUS_UNSUP_FEAT_WRITE:
+               (void) printf(gettext("status: The pool can only be accessed "
+                   "in read-only mode on this system. It\n\tcannot be "
+                   "accessed in read-write mode because it uses the "
+                   "following\n\tfeature(s) not supported on this system:\n"));
+               zpool_print_unsup_feat(config);
+               break;
+
        case ZPOOL_STATUS_HOSTID_MISMATCH:
-               (void) printf(gettext("status: The pool was last accessed by "
+               (void) printf(gettext(" status: The pool was last accessed by "
                    "another system.\n"));
                break;
 
        case ZPOOL_STATUS_FAULTED_DEV_R:
        case ZPOOL_STATUS_FAULTED_DEV_NR:
-               (void) printf(gettext("status: One or more devices are "
+               (void) printf(gettext(" status: One or more devices are "
                    "faulted.\n"));
                break;
 
        case ZPOOL_STATUS_BAD_LOG:
-               (void) printf(gettext("status: An intent log record cannot be "
+               (void) printf(gettext(" status: An intent log record cannot be "
                    "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.
@@ -1169,44 +1699,64 @@ show_import(nvlist_t *config)
         * Print out an action according to the overall state of the pool.
         */
        if (vs->vs_state == VDEV_STATE_HEALTHY) {
-               if (reason == ZPOOL_STATUS_VERSION_OLDER)
-                       (void) printf(gettext("action: The pool can be "
+               if (reason == ZPOOL_STATUS_VERSION_OLDER ||
+                   reason == ZPOOL_STATUS_FEAT_DISABLED) {
+                       (void) printf(gettext(" action: The pool can be "
                            "imported using its name or numeric identifier, "
                            "though\n\tsome features will not be available "
                            "without an explicit 'zpool upgrade'.\n"));
-               else if (reason == ZPOOL_STATUS_HOSTID_MISMATCH)
-                       (void) printf(gettext("action: The pool can be "
+               } else if (reason == ZPOOL_STATUS_HOSTID_MISMATCH) {
+                       (void) printf(gettext(" action: The pool can be "
                            "imported using its name or numeric "
                            "identifier and\n\tthe '-f' flag.\n"));
-               else
-                       (void) printf(gettext("action: The pool can be "
+               } else {
+                       (void) printf(gettext(" action: The pool can be "
                            "imported using its name or numeric "
                            "identifier.\n"));
+               }
        } else if (vs->vs_state == VDEV_STATE_DEGRADED) {
-               (void) printf(gettext("action: The pool can be imported "
+               (void) printf(gettext(" action: The pool can be imported "
                    "despite missing or damaged devices.  The\n\tfault "
                    "tolerance of the pool may be compromised if imported.\n"));
        } else {
                switch (reason) {
                case ZPOOL_STATUS_VERSION_NEWER:
-                       (void) printf(gettext("action: The pool cannot be "
+                       (void) printf(gettext(" action: The pool cannot be "
                            "imported.  Access the pool on a system running "
                            "newer\n\tsoftware, or recreate the pool from "
                            "backup.\n"));
                        break;
+               case ZPOOL_STATUS_UNSUP_FEAT_READ:
+                       (void) printf(gettext("action: The pool cannot be "
+                           "imported. Access the pool on a system that "
+                           "supports\n\tthe required feature(s), or recreate "
+                           "the pool from backup.\n"));
+                       break;
+               case ZPOOL_STATUS_UNSUP_FEAT_WRITE:
+                       (void) printf(gettext("action: The pool cannot be "
+                           "imported in read-write mode. Import the pool "
+                           "with\n"
+                           "\t\"-o readonly=on\", access the pool on a system "
+                           "that supports the\n\trequired feature(s), or "
+                           "recreate the pool from backup.\n"));
+                       break;
                case ZPOOL_STATUS_MISSING_DEV_R:
                case ZPOOL_STATUS_MISSING_DEV_NR:
                case ZPOOL_STATUS_BAD_GUID_SUM:
-                       (void) printf(gettext("action: The pool cannot be "
+                       (void) printf(gettext(" action: The pool cannot be "
                            "imported. Attach the missing\n\tdevices and try "
                            "again.\n"));
                        break;
                default:
-                       (void) printf(gettext("action: The pool cannot be "
+                       (void) printf(gettext(" action: The pool cannot be "
                            "imported due to damaged devices or data.\n"));
                }
        }
 
+       /* Print the comment attached to the pool. */
+       if (nvlist_lookup_string(config, ZPOOL_CONFIG_COMMENT, &comment) == 0)
+               (void) printf(gettext("comment: %s\n"), comment);
+
        /*
         * If the state is "closed" or "can't open", and the aux state
         * is "corrupt data":
@@ -1224,20 +1774,18 @@ show_import(nvlist_t *config)
        }
 
        if (msgid != NULL)
-               (void) printf(gettext("   see: http://www.sun.com/msg/%s\n"),
+               (void) printf(gettext("   see: http://zfsonlinux.org/msg/%s\n"),
                    msgid);
 
-       (void) printf(gettext("config:\n\n"));
+       (void) printf(gettext(" config:\n\n"));
 
        namewidth = max_width(NULL, nvroot, 0, 0);
        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 "
@@ -1253,13 +1801,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)
+    nvlist_t *props, int flags)
 {
        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);
@@ -1268,16 +1815,19 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts,
            ZPOOL_CONFIG_POOL_STATE, &state) == 0);
        verify(nvlist_lookup_uint64(config,
            ZPOOL_CONFIG_VERSION, &version) == 0);
-       if (version > SPA_VERSION) {
+       if (!SPA_VERSION_IS_SUPPORTED(version)) {
                (void) fprintf(stderr, gettext("cannot import '%s': pool "
-                   "is formatted using a newer ZFS version\n"), name);
+                   "is formatted using an unsupported ZFS version\n"), name);
                return (1);
-       } else if (state != POOL_STATE_EXPORTED && !force) {
+       } else if (state != POOL_STATE_EXPORTED &&
+           !(flags & ZFS_IMPORT_ANY_HOST)) {
                uint64_t hostid;
 
                if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_HOSTID,
                    &hostid) == 0) {
-                       if ((unsigned long)hostid != gethostid()) {
+                       unsigned long system_hostid = gethostid() & 0xffffffff;
+
+                       if ((unsigned long)hostid != system_hostid) {
                                char *hostname;
                                uint64_t timestamp;
                                time_t t;
@@ -1306,22 +1856,24 @@ 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, flags) != 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_enable_datasets(zhp, mntopts, 0) != 0) {
+       if (zpool_get_state(zhp) != POOL_STATE_UNAVAIL &&
+           !(flags & ZFS_IMPORT_ONLY) &&
+           zpool_enable_datasets(zhp, mntopts, 0) != 0) {
                zpool_close(zhp);
                return (1);
        }
 
        zpool_close(zhp);
-       return (error);
+       return (0);
 }
 
 /*
@@ -1329,12 +1881,12 @@ 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] <pool | id> [newpool]
+ *              [-d dir | -c cachefile] [-f] [-n] [-F] <pool | id> [newpool]
  *
  *      -c     Read pool information from a cachefile instead of searching
  *             devices.
  *
- *       -d    Scan in a specific directory, other than /dev/dsk.  More than
+ *       -d    Scan in a specific directory, other than /dev/.  More than
  *             one directory can be specified using multiple '-d' options.
  *
  *       -D     Scan for previously destroyed pools or import all or only
@@ -1344,12 +1896,22 @@ 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.
+ *
+ *       -N     Import the pool but don't mount datasets.
+ *
+ *       -T     Specify a starting txg to use for import. This option is
+ *             intentionally undocumented option for testing purposes.
  *
  *       -a    Import all pools found.
  *
@@ -1362,28 +1924,35 @@ int
 zpool_do_import(int argc, char **argv)
 {
        char **searchdirs = NULL;
+       char *env, *envdup = 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;
        char *mntopts = NULL;
-       boolean_t do_force = B_FALSE;
        nvpair_t *elem;
        nvlist_t *config;
        uint64_t searchguid = 0;
        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;
-       uint64_t pool_state;
+       int flags = ZFS_IMPORT_NORMAL;
+       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, txg = -1ULL;
        char *cachefile = NULL;
+       importargs_t idata = { 0 };
+       char *endptr;
 
        /* check options */
-       while ((c = getopt(argc, argv, ":ac:d:DfFo:p:R:")) != -1) {
+       while ((c = getopt(argc, argv, ":aCc:d:DEfFmnNo:rR:T:VX")) != -1) {
                switch (c) {
                case 'a':
                        do_all = B_TRUE;
@@ -1408,10 +1977,19 @@ zpool_do_import(int argc, char **argv)
                        do_destroyed = B_TRUE;
                        break;
                case 'f':
-                       do_force = B_TRUE;
+                       flags |= ZFS_IMPORT_ANY_HOST;
                        break;
                case 'F':
-                       allow_faulted = B_TRUE;
+                       do_rewind = B_TRUE;
+                       break;
+               case 'm':
+                       flags |= ZFS_IMPORT_MISSING_LOG;
+                       break;
+               case 'n':
+                       dryrun = B_TRUE;
+                       break;
+               case 'N':
+                       flags |= ZFS_IMPORT_ONLY;
                        break;
                case 'o':
                        if ((propval = strchr(optarg, '=')) != NULL) {
@@ -1436,6 +2014,22 @@ zpool_do_import(int argc, char **argv)
                            ZPOOL_PROP_CACHEFILE), "none", &props, B_TRUE))
                                goto error;
                        break;
+               case 'T':
+                       errno = 0;
+                       txg = strtoull(optarg, &endptr, 10);
+                       if (errno != 0 || *endptr != '\0') {
+                               (void) fprintf(stderr,
+                                   gettext("invalid txg value\n"));
+                               usage(B_FALSE);
+                       }
+                       rewind_policy = ZPOOL_DO_REWIND | ZPOOL_EXTREME_REWIND;
+                       break;
+               case 'V':
+                       flags |= ZFS_IMPORT_VERBATIM;
+                       break;
+               case 'X':
+                       xtreme_rewind = B_TRUE;
+                       break;
                case ':':
                        (void) fprintf(stderr, gettext("missing argument for "
                            "'%c' option\n"), optopt);
@@ -1456,11 +2050,23 @@ zpool_do_import(int argc, char **argv)
                usage(B_FALSE);
        }
 
-       if (searchdirs == NULL) {
-               searchdirs = safe_malloc(sizeof (char *));
-               searchdirs[0] = "/dev/dsk";
-               nsearch = 1;
+       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_uint64(policy, ZPOOL_REWIND_REQUEST_TXG, txg) != 0 ||
+           nvlist_add_uint32(policy, ZPOOL_REWIND_REQUEST, rewind_policy) != 0)
+               goto error;
 
        /* check argument count */
        if (do_all) {
@@ -1482,7 +2088,10 @@ zpool_do_import(int argc, char **argv)
                if (argc == 0 && !priv_ineffect(PRIV_SYS_CONFIG)) {
                        (void) fprintf(stderr, gettext("cannot "
                            "discover pools: permission denied\n"));
-                       free(searchdirs);
+                       if (searchdirs != NULL)
+                               free(searchdirs);
+
+                       nvlist_free(policy);
                        return (1);
                }
        }
@@ -1508,28 +2117,76 @@ 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);
+               idata.unique = B_TRUE;
+       }
+
+       /*
+        * Check the environment for the preferred search path.
+        */
+       if ((searchdirs == NULL) && (env = getenv("ZPOOL_IMPORT_PATH"))) {
+               char *dir;
+
+               envdup = strdup(env);
+
+               dir = strtok(envdup, ":");
+               while (dir != NULL) {
+                       if (searchdirs == NULL) {
+                               searchdirs = safe_malloc(sizeof (char *));
+                       } else {
+                               char **tmp = safe_malloc((nsearch + 1) *
+                                   sizeof (char *));
+                               bcopy(searchdirs, tmp, nsearch *
+                                   sizeof (char *));
+                               free(searchdirs);
+                               searchdirs = tmp;
+                       }
+                       searchdirs[nsearch++] = dir;
+                       dir = strtok(NULL, ":");
+               }
        }
 
-       if (pools == NULL) {
+       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 "
+                   "<pool | id> <newpool>' 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]);
                }
-               free(searchdirs);
+               err = 1;
+       }
+
+       if (err == 1) {
+               if (searchdirs != NULL)
+                       free(searchdirs);
+               if (envdup != NULL)
+                       free(envdup);
+               nvlist_free(policy);
                return (1);
        }
 
@@ -1553,17 +2210,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
+                                   props, flags);
+                       } else {
                                show_import(config);
+                       }
                } else if (searchname != NULL) {
                        char *name;
 
@@ -1609,7 +2270,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, props, flags);
                }
        }
 
@@ -1624,16 +2285,20 @@ zpool_do_import(int argc, char **argv)
 error:
        nvlist_free(props);
        nvlist_free(pools);
-       free(searchdirs);
+       nvlist_free(policy);
+       if (searchdirs != NULL)
+               free(searchdirs);
+       if (envdup != NULL)
+               free(envdup);
 
        return (err ? 1 : 0);
 }
 
 typedef struct iostat_cbdata {
-       zpool_list_t *cb_list;
-       int cb_verbose;
-       int cb_iteration;
+       boolean_t cb_verbose;
        int cb_namewidth;
+       int cb_iteration;
+       zpool_list_t *cb_list;
 } iostat_cbdata_t;
 
 static void
@@ -1651,7 +2316,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);
 }
@@ -1686,13 +2351,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)
@@ -1742,13 +2407,49 @@ 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, islog = B_FALSE;
+
+               (void) nvlist_lookup_uint64(newchild[c], ZPOOL_CONFIG_IS_HOLE,
+                   &ishole);
+
+               (void) nvlist_lookup_uint64(newchild[c], ZPOOL_CONFIG_IS_LOG,
+                   &islog);
+
+               if (ishole || islog)
+                       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);
        }
 
        /*
+        * Log device section
+        */
+
+       if (num_logs(newnv) > 0) {
+               (void) printf("%-*s      -      -      -      -      -      "
+                   "-\n", cb->cb_namewidth, "logs");
+
+               for (c = 0; c < children; c++) {
+                       uint64_t islog = B_FALSE;
+                       (void) nvlist_lookup_uint64(newchild[c],
+                           ZPOOL_CONFIG_IS_LOG, &islog);
+
+                       if (islog) {
+                               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);
+                       }
+               }
+
+       }
+
+       /*
         * Include level 2 ARC devices in iostat output
         */
        if (nvlist_lookup_nvlist_array(newnv, ZPOOL_CONFIG_L2CACHE,
@@ -1763,7 +2464,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);
@@ -1824,11 +2526,30 @@ print_iostat(zpool_handle_t *zhp, void *data)
        return (0);
 }
 
+static int
+get_columns(void)
+{
+       struct winsize ws;
+       int columns = 80;
+       int error;
+
+       if (isatty(STDOUT_FILENO)) {
+               error = ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws);
+               if (error == 0)
+                       columns = ws.ws_col;
+       } else {
+               columns = 999;
+       }
+
+       return columns;
+}
+
 int
 get_namewidth(zpool_handle_t *zhp, void *data)
 {
        iostat_cbdata_t *cb = data;
        nvlist_t *config, *nvroot;
+       int columns;
 
        if ((config = zpool_get_config(zhp, NULL)) != NULL) {
                verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
@@ -1836,58 +2557,33 @@ get_namewidth(zpool_handle_t *zhp, void *data)
                if (!cb->cb_verbose)
                        cb->cb_namewidth = strlen(zpool_get_name(zhp));
                else
-                       cb->cb_namewidth = max_width(zhp, nvroot, 0, 0);
+                       cb->cb_namewidth = max_width(zhp, nvroot, 0,
+                           cb->cb_namewidth);
        }
 
        /*
-        * The width must fall into the range [10,38].  The upper limit is the
-        * maximum we can have and still fit in 80 columns.
+        * 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 > 38)
-               cb->cb_namewidth = 38;
+       if (cb->cb_namewidth > columns - 42)
+               cb->cb_namewidth = columns - 42;
 
        return (0);
 }
 
 /*
- * 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;
 
        /*
         * Determine if the last argument is an integer or a pool name
@@ -1904,7 +2600,6 @@ zpool_do_iostat(int argc, char **argv)
                                    "cannot be zero\n"));
                                usage(B_FALSE);
                        }
-
                        /*
                         * Ignore the last parameter
                         */
@@ -1921,7 +2616,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;
@@ -1946,20 +2641,80 @@ zpool_do_iostat(int argc, char **argv)
                }
        }
 
-       /*
-        * Construct the list of all interesting pools.
-        */
-       ret = 0;
-       if ((list = pool_list_get(argc, argv, NULL, &ret)) == NULL)
-               return (1);
+       *iv = interval;
+       *cnt = count;
+       *argcp = argc;
+}
 
-       if (pool_list_count(list) == 0 && argc != 0) {
-               pool_list_free(list);
-               return (1);
-       }
+static void
+get_timestamp_arg(char c)
+{
+       if (c == 'u')
+               timestamp_fmt = UDATE;
+       else if (c == 'd')
+               timestamp_fmt = DDATE;
+       else
+               usage(B_FALSE);
+}
 
-       if (pool_list_count(list) == 0 && interval == 0) {
-               pool_list_free(list);
+/*
+ * 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.
+        */
+       ret = 0;
+       if ((list = pool_list_get(argc, argv, NULL, &ret)) == NULL)
+               return (1);
+
+       if (pool_list_count(list) == 0 && argc != 0) {
+               pool_list_free(list);
+               return (1);
+       }
+
+       if (pool_list_count(list) == 0 && interval == 0) {
+               pool_list_free(list);
                (void) fprintf(stderr, gettext("no pools available\n"));
                return (1);
        }
@@ -1976,39 +2731,48 @@ zpool_do_iostat(int argc, char **argv)
                pool_list_update(list);
 
                if ((npools = pool_list_count(list)) == 0)
-                       break;
+                       (void) fprintf(stderr, gettext("no pools available\n"));
+               else {
+                       /*
+                        * Refresh all statistics.  This is done as an
+                        * explicit step before calculating the maximum name
+                        * width, so that any * configuration changes are
+                        * properly accounted for.
+                        */
+                       (void) pool_list_iter(list, B_FALSE, refresh_iostat,
+                               &cb);
 
-               /*
-                * Refresh all statistics.  This is done as an explicit step
-                * before calculating the maximum name width, so that any
-                * configuration changes are properly accounted for.
-                */
-               (void) pool_list_iter(list, B_FALSE, refresh_iostat, &cb);
+                       /*
+                        * Iterate over all pools to determine the maximum width
+                        * for the pool / device name column across all pools.
+                        */
+                       cb.cb_namewidth = 0;
+                       (void) pool_list_iter(list, B_FALSE, get_namewidth,
+                               &cb);
 
-               /*
-                * Iterate over all pools to determine the maximum width
-                * for the pool / device name column across all pools.
-                */
-               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.
-                */
-               if (++cb.cb_iteration == 1 || verbose)
-                       print_iostat_header(&cb);
+                       /*
+                        * If it's the first time, or verbose mode, print the
+                        * header.
+                        */
+                       if (++cb.cb_iteration == 1 || verbose)
+                               print_iostat_header(&cb);
 
-               (void) pool_list_iter(list, B_FALSE, print_iostat, &cb);
+                       (void) pool_list_iter(list, B_FALSE, print_iostat, &cb);
 
-               /*
-                * If there's more than one pool, and we're not in verbose mode
-                * (which prints a separator for us), then print a separator.
-                */
-               if (npools > 1 && !verbose)
-                       print_iostat_separator(&cb);
+                       /*
+                        * If there's more than one pool, and we're not in
+                        * verbose mode (which prints a separator for us),
+                        * then print a separator.
+                        */
+                       if (npools > 1 && !verbose)
+                               print_iostat_separator(&cb);
 
-               if (verbose)
-                       (void) printf("\n");
+                       if (verbose)
+                               (void) printf("\n");
+               }
 
                /*
                 * Flush the output so that redirection to a file isn't buffered
@@ -2031,8 +2795,9 @@ zpool_do_iostat(int argc, char **argv)
 }
 
 typedef struct list_cbdata {
+       boolean_t       cb_verbose;
+       int             cb_namewidth;
        boolean_t       cb_scripted;
-       boolean_t       cb_first;
        zprop_list_t    *cb_proplist;
 } list_cbdata_t;
 
@@ -2040,30 +2805,49 @@ typedef struct list_cbdata {
  * Given a list of columns to display, output appropriate headers for each one.
  */
 static void
-print_header(zprop_list_t *pl)
+print_header(list_cbdata_t *cb)
 {
+       zprop_list_t *pl = cb->cb_proplist;
+       char headerbuf[ZPOOL_MAXPROPLEN];
        const char *header;
        boolean_t first = B_TRUE;
        boolean_t right_justify;
+       size_t width = 0;
 
        for (; pl != NULL; pl = pl->pl_next) {
-               if (pl->pl_prop == ZPROP_INVAL)
-                       continue;
+               width = pl->pl_width;
+               if (first && cb->cb_verbose) {
+                       /*
+                        * Reset the width to accommodate the verbose listing
+                        * of devices.
+                        */
+                       width = cb->cb_namewidth;
+               }
 
                if (!first)
                        (void) printf("  ");
                else
                        first = B_FALSE;
 
-               header = zpool_prop_column_name(pl->pl_prop);
-               right_justify = zpool_prop_align_right(pl->pl_prop);
+               right_justify = B_FALSE;
+               if (pl->pl_prop != ZPROP_INVAL) {
+                       header = zpool_prop_column_name(pl->pl_prop);
+                       right_justify = zpool_prop_align_right(pl->pl_prop);
+               } else {
+                       int i;
+
+                       for (i = 0; pl->pl_user_prop[i] != '\0'; i++)
+                               headerbuf[i] = toupper(pl->pl_user_prop[i]);
+                       headerbuf[i] = '\0';
+                       header = headerbuf;
+               }
 
                if (pl->pl_next == NULL && !right_justify)
                        (void) printf("%s", header);
                else if (right_justify)
-                       (void) printf("%*s", pl->pl_width, header);
+                       (void) printf("%*s", (int)width, header);
                else
-                       (void) printf("%-*s", pl->pl_width, header);
+                       (void) printf("%-*s", (int)width, header);
        }
 
        (void) printf("\n");
@@ -2074,17 +2858,28 @@ print_header(zprop_list_t *pl)
  * to the described layout.
  */
 static void
-print_pool(zpool_handle_t *zhp, zprop_list_t *pl, int scripted)
+print_pool(zpool_handle_t *zhp, list_cbdata_t *cb)
 {
+       zprop_list_t *pl = cb->cb_proplist;
        boolean_t first = B_TRUE;
        char property[ZPOOL_MAXPROPLEN];
        char *propstr;
        boolean_t right_justify;
-       int width;
+       size_t width;
 
        for (; pl != NULL; pl = pl->pl_next) {
+
+               width = pl->pl_width;
+               if (first && cb->cb_verbose) {
+                       /*
+                        * Reset the width to accommodate the verbose listing
+                        * of devices.
+                        */
+                       width = cb->cb_namewidth;
+               }
+
                if (!first) {
-                       if (scripted)
+                       if (cb->cb_scripted)
                                (void) printf("\t");
                        else
                                (void) printf("  ");
@@ -2094,35 +2889,137 @@ print_pool(zpool_handle_t *zhp, zprop_list_t *pl, int scripted)
 
                right_justify = B_FALSE;
                if (pl->pl_prop != ZPROP_INVAL) {
-                       if (zpool_get_prop(zhp, pl->pl_prop, property,
+                       if (pl->pl_prop == ZPOOL_PROP_EXPANDSZ &&
+                           zpool_get_prop_int(zhp, pl->pl_prop, NULL) == 0)
+                               propstr = "-";
+                       else if (zpool_get_prop(zhp, pl->pl_prop, property,
                            sizeof (property), NULL) != 0)
                                propstr = "-";
                        else
                                propstr = property;
 
                        right_justify = zpool_prop_align_right(pl->pl_prop);
+               } else if ((zpool_prop_feature(pl->pl_user_prop) ||
+                   zpool_prop_unsupported(pl->pl_user_prop)) &&
+                   zpool_prop_get_feature(zhp, pl->pl_user_prop, property,
+                   sizeof (property)) == 0) {
+                       propstr = property;
                } else {
                        propstr = "-";
                }
 
-               width = pl->pl_width;
 
                /*
                 * If this is being called in scripted mode, or if this is the
                 * last column and it is left-justified, don't include a width
                 * format specifier.
                 */
-               if (scripted || (pl->pl_next == NULL && !right_justify))
+               if (cb->cb_scripted || (pl->pl_next == NULL && !right_justify))
                        (void) printf("%s", propstr);
                else if (right_justify)
-                       (void) printf("%*s", width, propstr);
+                       (void) printf("%*s", (int)width, propstr);
                else
-                       (void) printf("%-*s", width, propstr);
+                       (void) printf("%-*s", (int)width, propstr);
        }
 
        (void) printf("\n");
 }
 
+static void
+print_one_column(zpool_prop_t prop, uint64_t value, boolean_t scripted)
+{
+       char propval[64];
+       boolean_t fixed;
+       size_t width = zprop_width(prop, &fixed, ZFS_TYPE_POOL);
+
+       zfs_nicenum(value, propval, sizeof (propval));
+
+       if (prop == ZPOOL_PROP_EXPANDSZ && value == 0)
+               (void) strlcpy(propval, "-", sizeof (propval));
+
+       if (scripted)
+               (void) printf("\t%s", propval);
+       else
+               (void) printf("  %*s", (int)width, propval);
+}
+
+void
+print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
+    list_cbdata_t *cb, int depth)
+{
+       nvlist_t **child;
+       vdev_stat_t *vs;
+       uint_t c, children;
+       char *vname;
+       boolean_t scripted = cb->cb_scripted;
+
+       verify(nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_VDEV_STATS,
+           (uint64_t **)&vs, &c) == 0);
+
+       if (name != NULL) {
+               if (scripted)
+                       (void) printf("\t%s", name);
+               else if (strlen(name) + depth > cb->cb_namewidth)
+                       (void) printf("%*s%s", depth, "", name);
+               else
+                       (void) printf("%*s%s%*s", depth, "", name,
+                           (int)(cb->cb_namewidth - strlen(name) - depth), "");
+
+               /* only toplevel vdevs have capacity stats */
+               if (vs->vs_space == 0) {
+                       if (scripted)
+                               (void) printf("\t-\t-\t-");
+                       else
+                               (void) printf("      -      -      -");
+               } else {
+                       print_one_column(ZPOOL_PROP_SIZE, vs->vs_space,
+                           scripted);
+                       print_one_column(ZPOOL_PROP_CAPACITY, vs->vs_alloc,
+                           scripted);
+                       print_one_column(ZPOOL_PROP_FREE,
+                           vs->vs_space - vs->vs_alloc, scripted);
+               }
+               print_one_column(ZPOOL_PROP_EXPANDSZ, vs->vs_esize,
+                   scripted);
+               (void) printf("\n");
+       }
+
+       if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
+           &child, &children) != 0)
+               return;
+
+       for (c = 0; c < children; c++) {
+               uint64_t ishole = B_FALSE;
+
+               if (nvlist_lookup_uint64(child[c],
+                   ZPOOL_CONFIG_IS_HOLE, &ishole) == 0 && ishole)
+                       continue;
+
+               vname = zpool_vdev_name(g_zfs, zhp, child[c], B_FALSE);
+               print_list_stats(zhp, vname, child[c], cb, depth + 2);
+               free(vname);
+       }
+
+       /*
+        * Include level 2 ARC devices in iostat output
+        */
+       if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE,
+           &child, &children) != 0)
+               return;
+
+       if (children > 0) {
+               (void) printf("%-*s      -      -      -      -      -      "
+                   "-\n", cb->cb_namewidth, "cache");
+               for (c = 0; c < children; c++) {
+                       vname = zpool_vdev_name(g_zfs, zhp, child[c],
+                           B_FALSE);
+                       print_list_stats(zhp, vname, child[c], cb, depth + 2);
+                       free(vname);
+               }
+       }
+}
+
+
 /*
  * Generic callback function to list a pool.
  */
@@ -2130,25 +3027,30 @@ int
 list_callback(zpool_handle_t *zhp, void *data)
 {
        list_cbdata_t *cbp = data;
+       nvlist_t *config;
+       nvlist_t *nvroot;
 
-       if (cbp->cb_first) {
-               if (!cbp->cb_scripted)
-                       print_header(cbp->cb_proplist);
-               cbp->cb_first = B_FALSE;
-       }
+       config = zpool_get_config(zhp, NULL);
+
+       print_pool(zhp, cbp);
+       if (!cbp->cb_verbose)
+               return (0);
 
-       print_pool(zhp, cbp->cb_proplist, cbp->cb_scripted);
+       verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
+           &nvroot) == 0);
+       print_list_stats(zhp, NULL, nvroot, cbp, 0);
 
        return (0);
 }
 
 /*
- * 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.
@@ -2157,14 +3059,18 @@ int
 zpool_do_list(int argc, char **argv)
 {
        int c;
-       int ret;
+       int ret = 0;
        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;
+       zpool_list_t *list;
+       boolean_t first = B_TRUE;
 
        /* check options */
-       while ((c = getopt(argc, argv, ":Ho:")) != -1) {
+       while ((c = getopt(argc, argv, ":Ho:T:v")) != -1) {
                switch (c) {
                case 'H':
                        cb.cb_scripted = B_TRUE;
@@ -2172,6 +3078,12 @@ zpool_do_list(int argc, char **argv)
                case 'o':
                        props = optarg;
                        break;
+               case 'T':
+                       get_timestamp_arg(*optarg);
+                       break;
+               case 'v':
+                       cb.cb_verbose = B_TRUE;
+                       break;
                case ':':
                        (void) fprintf(stderr, gettext("missing argument for "
                            "'%c' option\n"), optopt);
@@ -2187,49 +3099,46 @@ 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);
-
-       zprop_free_list(cb.cb_proplist);
+       if ((list = pool_list_get(argc, argv, &cb.cb_proplist, &ret)) == NULL)
+               return (1);
 
-       if (argc == 0 && cb.cb_first && !cb.cb_scripted) {
+       if (argc == 0 && !cb.cb_scripted && pool_list_count(list) == 0) {
                (void) printf(gettext("no pools available\n"));
+               zprop_free_list(cb.cb_proplist);
                return (0);
        }
 
-       return (ret);
-}
+       for (;;) {
+               pool_list_update(list);
 
-static nvlist_t *
-zpool_get_vdev_by_name(nvlist_t *nv, char *name)
-{
-       nvlist_t **child;
-       uint_t c, children;
-       nvlist_t *match;
-       char *path;
+               if (pool_list_count(list) == 0)
+                       break;
 
-       if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
-           &child, &children) != 0) {
-               verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0);
-               if (strncmp(name, "/dev/dsk/", 9) == 0)
-                       name += 9;
-               if (strncmp(path, "/dev/dsk/", 9) == 0)
-                       path += 9;
-               if (strcmp(name, path) == 0)
-                       return (nv);
-               return (NULL);
-       }
+               if (timestamp_fmt != NODATE)
+                       print_timestamp(timestamp_fmt);
+
+               if (!cb.cb_scripted && (first || cb.cb_verbose)) {
+                       print_header(&cb);
+                       first = B_FALSE;
+               }
+               ret = pool_list_iter(list, B_TRUE, list_callback, &cb);
+
+               if (interval == 0)
+                       break;
 
-       for (c = 0; c < children; c++)
-               if ((match = zpool_get_vdev_by_name(child[c], name)) != NULL)
-                       return (match);
+               if (count != 0 && --count == 0)
+                       break;
+
+               (void) sleep(interval);
+       }
 
-       return (NULL);
+       zprop_free_list(cb.cb_proplist);
+       return (ret);
 }
 
 static int
@@ -2240,14 +3149,29 @@ zpool_do_attach_or_replace(int argc, char **argv, int replacing)
        nvlist_t *nvroot;
        char *poolname, *old_disk, *new_disk;
        zpool_handle_t *zhp;
+       nvlist_t *props = NULL;
+       char *propval;
        int ret;
 
        /* check options */
-       while ((c = getopt(argc, argv, "f")) != -1) {
+       while ((c = getopt(argc, argv, "fo:")) != -1) {
                switch (c) {
                case 'f':
                        force = B_TRUE;
                        break;
+               case 'o':
+                       if ((propval = strchr(optarg, '=')) == NULL) {
+                               (void) fprintf(stderr, gettext("missing "
+                                   "'=' for -o option\n"));
+                               usage(B_FALSE);
+                       }
+                       *propval = '\0';
+                       propval++;
+
+                       if ((strcmp(optarg, ZPOOL_CONFIG_ASHIFT) != 0) ||
+                           (add_prop_list(optarg, propval, &props, B_TRUE)))
+                               usage(B_FALSE);
+                       break;
                case '?':
                        (void) fprintf(stderr, gettext("invalid option '%c'\n"),
                            optopt);
@@ -2304,7 +3228,7 @@ zpool_do_attach_or_replace(int argc, char **argv, int replacing)
                return (1);
        }
 
-       nvroot = make_root_vdev(zhp, force, B_FALSE, replacing, B_FALSE,
+       nvroot = make_root_vdev(zhp, props, force, B_FALSE, replacing, B_FALSE,
            argc, argv);
        if (nvroot == NULL) {
                zpool_close(zhp);
@@ -2334,9 +3258,10 @@ zpool_do_replace(int argc, char **argv)
 }
 
 /*
- * zpool attach [-f] <pool> <device> <new_device>
+ * zpool attach [-f] [-o property=value] <pool> <device> <new_device>
  *
  *     -f      Force attach, even if <new_device> appears to be in use.
+ *     -o      Set property=value.
  *
  * Attach <new_device> to the mirror containing <device>.  If <device> is not
  * part of a mirror, then <device> will be transformed into a mirror of
@@ -2408,6 +3333,146 @@ zpool_do_detach(int argc, char **argv)
 }
 
 /*
+ * zpool split [-n] [-o prop=val] ...
+ *             [-o mntopt] ...
+ *             [-R altroot] <pool> <newpool> [<device> ...]
+ *
+ *     -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 successful, 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 <pool> <device> ...
  */
 int
@@ -2418,10 +3483,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"),
@@ -2449,7 +3518,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"),
@@ -2543,31 +3612,171 @@ 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;
+
+       /* 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 ((zhp = zpool_open_canfail(g_zfs, pool)) == NULL)
+       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) != 0)
+       if (zpool_clear(zhp, device, policy) != 0)
                ret = 1;
 
        zpool_close(zhp);
 
+       nvlist_free(policy);
+
+       return (ret);
+}
+
+/*
+ * zpool reguid <pool>
+ */
+int
+zpool_do_reguid(int argc, char **argv)
+{
+       int c;
+       char *poolname;
+       zpool_handle_t *zhp;
+       int ret = 0;
+
+       /* check options */
+       while ((c = getopt(argc, argv, "")) != -1) {
+               switch (c) {
+               case '?':
+                       (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+                           optopt);
+                       usage(B_FALSE);
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+
+       /* get pool name and check number of arguments */
+       if (argc < 1) {
+               (void) fprintf(stderr, gettext("missing pool name\n"));
+               usage(B_FALSE);
+       }
+
+       if (argc > 1) {
+               (void) fprintf(stderr, gettext("too many arguments\n"));
+               usage(B_FALSE);
+       }
+
+       poolname = argv[0];
+       if ((zhp = zpool_open(g_zfs, poolname)) == NULL)
+               return (1);
+
+       ret = zpool_reguid(zhp);
+
+       zpool_close(zhp);
+       return (ret);
+}
+
+
+/*
+ * zpool reopen <pool>
+ *
+ * Reopen the pool so that the kernel can update the sizes of all vdevs.
+ */
+int
+zpool_do_reopen(int argc, char **argv)
+{
+       int c;
+       int ret = 0;
+       zpool_handle_t *zhp;
+       char *pool;
+
+       /* check options */
+       while ((c = getopt(argc, argv, "")) != -1) {
+               switch (c) {
+               case '?':
+                       (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+                           optopt);
+                       usage(B_FALSE);
+               }
+       }
+
+       argc--;
+       argv++;
+
+       if (argc < 1) {
+               (void) fprintf(stderr, gettext("missing pool name\n"));
+               usage(B_FALSE);
+       }
+
+       if (argc > 1) {
+               (void) fprintf(stderr, gettext("too many arguments\n"));
+               usage(B_FALSE);
+       }
+
+       pool = argv[0];
+       if ((zhp = zpool_open_canfail(g_zfs, pool)) == NULL)
+               return (1);
+
+       ret = zpool_reopen(zhp);
+       zpool_close(zhp);
        return (ret);
 }
 
@@ -2592,7 +3801,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);
 }
@@ -2608,13 +3817,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"),
@@ -2626,258 +3835,134 @@ zpool_do_scrub(int argc, char **argv)
        cb.cb_argc = argc;
        cb.cb_argv = argv;
        argc -= optind;
-       argv += optind;
-
-       if (argc < 1) {
-               (void) fprintf(stderr, gettext("missing pool name argument\n"));
-               usage(B_FALSE);
-       }
-
-       return (for_each_pool(argc, argv, B_TRUE, NULL, scrub_callback, &cb));
-}
-
-typedef struct status_cbdata {
-       int             cb_count;
-       boolean_t       cb_allpools;
-       boolean_t       cb_verbose;
-       boolean_t       cb_explain;
-       boolean_t       cb_first;
-} status_cbdata_t;
-
-/*
- * Print out detailed scrub status.
- */
-void
-print_scrub_status(nvlist_t *nvroot)
-{
-       vdev_stat_t *vs;
-       uint_t vsc;
-       time_t start, end, now;
-       double fraction_done;
-       uint64_t examined, total, minutes_left, minutes_taken;
-       char *scrub_type;
-
-       verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_STATS,
-           (uint64_t **)&vs, &vsc) == 0);
-
-       /*
-        * 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) {
-               (void) printf(gettext("none requested\n"));
-               return;
-       }
-
-       scrub_type = (vs->vs_scrub_type == POOL_SCRUB_RESILVER) ?
-           "resilver" : "scrub";
-
-       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",
-                   (u_longlong_t)(minutes_taken / 60),
-                   (uint_t)(minutes_taken % 60),
-                   (u_longlong_t)vs->vs_scrub_errors, 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);
+       argv += optind;
+
+       if (argc < 1) {
+               (void) fprintf(stderr, gettext("missing pool name argument\n"));
+               usage(B_FALSE);
        }
 
-       zpool_close(zhp);
-       return (0);
+       return (for_each_pool(argc, argv, B_TRUE, NULL, scrub_callback, &cb));
 }
 
+typedef struct status_cbdata {
+       int             cb_count;
+       boolean_t       cb_allpools;
+       boolean_t       cb_verbose;
+       boolean_t       cb_explain;
+       boolean_t       cb_first;
+       boolean_t       cb_dedup_stats;
+} status_cbdata_t;
+
 /*
- * Print out configuration state as requested by status_callback.
+ * Print out detailed scrub status.
  */
 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)
+print_scan_status(pool_scan_stat_t *ps)
 {
-       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);
+       time_t start, end;
+       uint64_t elapsed, mins_left, hours_left;
+       uint64_t pass_exam, examined, total;
+       uint_t rate;
+       double fraction_done;
+       char processed_buf[7], examined_buf[7], total_buf[7], rate_buf[7];
 
-       if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
-           &child, &children) != 0)
-               children = 0;
+       (void) printf(gettext("  scan: "));
 
-       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";
+       /* 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;
        }
 
-       (void) printf("\t%*s%-*s  %-8s", depth, "", namewidth - depth,
-           name, state);
+       start = ps->pss_start_time;
+       end = ps->pss_end_time;
+       zfs_nicenum(ps->pss_processed, processed_buf, sizeof (processed_buf));
 
-       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);
+       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 = NULL;
+
+               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)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 (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NOT_PRESENT,
-           &notpresent) == 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;
+       assert(ps->pss_state == DSS_SCANNING);
 
-               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;
+       /*
+        * 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));
+       }
 
-               case VDEV_AUX_ERR_EXCEEDED:
-                       (void) printf(gettext("too many errors"));
-                       break;
+       examined = ps->pss_examined ? ps->pss_examined : 1;
+       total = ps->pss_to_examine;
+       fraction_done = (double)examined / total;
 
-               case VDEV_AUX_IO_FAILURE:
-                       (void) printf(gettext("experienced I/O failures"));
-                       break;
+       /* 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;
+       hours_left = mins_left / 60;
 
-               case VDEV_AUX_BAD_LOG:
-                       (void) printf(gettext("bad intent log"));
-                       break;
+       zfs_nicenum(examined, examined_buf, sizeof (examined_buf));
+       zfs_nicenum(total, total_buf, sizeof (total_buf));
+       zfs_nicenum(rate, rate_buf, sizeof (rate_buf));
 
-               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");
+       /*
+        * do not print estimated time if hours_left is more than 30 days
+        */
+       (void) printf(gettext("    %s scanned out of %s at %s/s"),
+           examined_buf, total_buf, rate_buf);
+       if (hours_left < (30 * 24)) {
+               (void) printf(gettext(", %lluh%um to go\n"),
+                   (u_longlong_t)hours_left, (uint_t)(mins_left % 60));
+       } else {
+               (void) printf(gettext(
+                   ", (scan is slow, no estimated time)\n"));
        }
 
-       (void) printf("\n");
-
-       for (c = 0; c < children; c++) {
-               uint64_t is_log = B_FALSE;
-
-               (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);
+       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);
        }
 }
 
@@ -2929,9 +4014,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);
        }
 }
@@ -2949,20 +4034,56 @@ 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 we 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)
+               return;
+
+       (void) printf("\n");
+       (void) printf(gettext(" dedup: "));
+       if (ddo->ddo_count == 0) {
+               (void) printf(gettext("no DDT entries\n"));
+               return;
+       }
+
+       (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:
  *
  *        pool: tank
  *     status: DEGRADED
  *     reason: One or more devices ...
- *         see: http://www.sun.com/msg/ZFS-xxxx-01
+ *         see: http://zfsonlinux.org/msg/ZFS-xxxx-01
  *     config:
  *             mirror          DEGRADED
  *                c1t0d0       OK
@@ -2991,7 +4112,10 @@ status_callback(zpool_handle_t *zhp, void *data)
         * If we were given 'zpool status -x', only report those pools with
         * problems.
         */
-       if (reason == ZPOOL_STATUS_OK && cbp->cb_explain) {
+       if (cbp->cb_explain &&
+           (reason == ZPOOL_STATUS_OK ||
+           reason == ZPOOL_STATUS_VERSION_OLDER ||
+           reason == ZPOOL_STATUS_FEAT_DISABLED)) {
                if (!cbp->cb_allpools) {
                        (void) printf(gettext("pool '%s' is healthy\n"),
                            zpool_get_name(zhp));
@@ -3008,7 +4132,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);
 
@@ -3046,8 +4170,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:
@@ -3071,6 +4195,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 "
@@ -3091,17 +4225,18 @@ 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:
-               (void) printf(gettext("status: The pool is formatted using an "
-                   "older on-disk format.  The pool can\n\tstill be used, but "
-                   "some features are unavailable.\n"));
+               (void) printf(gettext("status: The pool is formatted using a "
+                   "legacy on-disk format.  The pool can\n\tstill be used, "
+                   "but some features are unavailable.\n"));
                (void) printf(gettext("action: Upgrade the pool using 'zpool "
                    "upgrade'.  Once this is done, the\n\tpool will no longer "
-                   "be accessible on older software versions.\n"));
+                   "be accessible on software that does not support\n\t"
+                   "feature flags.\n"));
                break;
 
        case ZPOOL_STATUS_VERSION_NEWER:
@@ -3113,6 +4248,41 @@ status_callback(zpool_handle_t *zhp, void *data)
                    "backup.\n"));
                break;
 
+       case ZPOOL_STATUS_FEAT_DISABLED:
+               (void) printf(gettext("status: Some supported features are not "
+                   "enabled on the pool. The pool can\n\tstill be used, but "
+                   "some features are unavailable.\n"));
+               (void) printf(gettext("action: Enable all features using "
+                   "'zpool upgrade'. Once this is done,\n\tthe pool may no "
+                   "longer be accessible by software that does not support\n\t"
+                   "the features. See zpool-features(5) for details.\n"));
+               break;
+
+       case ZPOOL_STATUS_UNSUP_FEAT_READ:
+               (void) printf(gettext("status: The pool cannot be accessed on "
+                   "this system because it uses the\n\tfollowing feature(s) "
+                   "not supported on this system:\n"));
+               zpool_print_unsup_feat(config);
+               (void) printf("\n");
+               (void) printf(gettext("action: Access the pool from a system "
+                   "that supports the required feature(s),\n\tor restore the "
+                   "pool from backup.\n"));
+               break;
+
+       case ZPOOL_STATUS_UNSUP_FEAT_WRITE:
+               (void) printf(gettext("status: The pool can only be accessed "
+                   "in read-only mode on this system. It\n\tcannot be "
+                   "accessed in read-write mode because it uses the "
+                   "following\n\tfeature(s) not supported on this system:\n"));
+               zpool_print_unsup_feat(config);
+               (void) printf("\n");
+               (void) printf(gettext("action: The pool cannot be accessed in "
+                   "read-write mode. Import the pool with\n"
+                   "\t\"-o readonly=on\", access the pool from a system that "
+                   "supports the\n\trequired feature(s), or restore the "
+                   "pool from backup.\n"));
+               break;
+
        case ZPOOL_STATUS_FAULTED_DEV_R:
                (void) printf(gettext("status: One or more devices are "
                    "faulted in response to persistent errors.\n\tSufficient "
@@ -3160,7 +4330,7 @@ status_callback(zpool_handle_t *zhp, void *data)
        }
 
        if (msgid != NULL)
-               (void) printf(gettext("   see: http://www.sun.com/msg/%s\n"),
+               (void) printf(gettext("   see: http://zfsonlinux.org/msg/%s\n"),
                    msgid);
 
        if (config != NULL) {
@@ -3168,10 +4338,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)
@@ -3181,11 +4352,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);
@@ -3228,6 +4398,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"));
@@ -3237,10 +4410,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.
  */
@@ -3249,10 +4424,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;
@@ -3260,6 +4436,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);
@@ -3270,72 +4452,197 @@ zpool_do_status(int argc, char **argv)
        argc -= optind;
        argv += optind;
 
+       get_interval_count(&argc, argv, &interval, &count);
+
+       if (argc == 0)
+               cb.cb_allpools = B_TRUE;
+
        cb.cb_first = B_TRUE;
 
-       if (argc == 0)
-               cb.cb_allpools = B_TRUE;
+       for (;;) {
+               if (timestamp_fmt != NODATE)
+                       print_timestamp(timestamp_fmt);
+
+               ret = for_each_pool(argc, argv, B_TRUE, NULL,
+                   status_callback, &cb);
+
+               if (argc == 0 && cb.cb_count == 0)
+                       (void) fprintf(stderr, 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 {
+       int     cb_first;
+       int     cb_argc;
+       uint64_t cb_version;
+       char    **cb_argv;
+} upgrade_cbdata_t;
+
+static int
+upgrade_version(zpool_handle_t *zhp, uint64_t version)
+{
+       int ret;
+       nvlist_t *config;
+       uint64_t oldversion;
+
+       config = zpool_get_config(zhp, NULL);
+       verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION,
+           &oldversion) == 0);
+
+       assert(SPA_VERSION_IS_SUPPORTED(oldversion));
+       assert(oldversion < version);
+
+       ret = zpool_upgrade(zhp, version);
+       if (ret != 0)
+               return (ret);
+
+       if (version >= SPA_VERSION_FEATURES) {
+               (void) printf(gettext("Successfully upgraded "
+                   "'%s' from version %llu to feature flags.\n"),
+                   zpool_get_name(zhp), (u_longlong_t) oldversion);
+       } else {
+               (void) printf(gettext("Successfully upgraded "
+                   "'%s' from version %llu to version %llu.\n"),
+                   zpool_get_name(zhp), (u_longlong_t) oldversion,
+                   (u_longlong_t) version);
+       }
+
+       return (0);
+}
+
+static int
+upgrade_enable_all(zpool_handle_t *zhp, int *countp)
+{
+       int i, ret, count;
+       boolean_t firstff = B_TRUE;
+       nvlist_t *enabled = zpool_get_features(zhp);
+
+       count = 0;
+       for (i = 0; i < SPA_FEATURES; i++) {
+               const char *fname = spa_feature_table[i].fi_uname;
+               const char *fguid = spa_feature_table[i].fi_guid;
+               if (!nvlist_exists(enabled, fguid)) {
+                       char *propname;
+                       verify(-1 != asprintf(&propname, "feature@%s", fname));
+                       ret = zpool_set_prop(zhp, propname,
+                           ZFS_FEATURE_ENABLED);
+                       if (ret != 0) {
+                               free(propname);
+                               return (ret);
+                       }
+                       count++;
+
+                       if (firstff) {
+                               (void) printf(gettext("Enabled the "
+                                   "following features on '%s':\n"),
+                                   zpool_get_name(zhp));
+                               firstff = B_FALSE;
+                       }
+                       (void) printf(gettext("  %s\n"), fname);
+                       free(propname);
+               }
+       }
+
+       if (countp != NULL)
+               *countp = count;
+       return (0);
+}
+
+static int
+upgrade_cb(zpool_handle_t *zhp, void *arg)
+{
+       upgrade_cbdata_t *cbp = arg;
+       nvlist_t *config;
+       uint64_t version;
+       boolean_t printnl = B_FALSE;
+       int ret;
+
+       config = zpool_get_config(zhp, NULL);
+       verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION,
+           &version) == 0);
+
+       assert(SPA_VERSION_IS_SUPPORTED(version));
+
+       if (version < cbp->cb_version) {
+               cbp->cb_first = B_FALSE;
+               ret = upgrade_version(zhp, cbp->cb_version);
+               if (ret != 0)
+                       return (ret);
+               printnl = B_TRUE;
+
+#if 0
+               /*
+                * XXX: This code can be enabled when Illumos commit
+                * 4445fffbbb1ea25fd0e9ea68b9380dd7a6709025 is merged.
+                * It reworks the history logging among other things.
+                */
+
+               /*
+                * If they did "zpool upgrade -a", then we could
+                * be doing ioctls to different pools.  We need
+                * to log this history once to each pool, and bypass
+                * the normal history logging that happens in main().
+                */
+               (void) zpool_log_history(g_zfs, history_str);
+               log_history = B_FALSE;
+#endif
+       }
+
+       if (cbp->cb_version >= SPA_VERSION_FEATURES) {
+               int count;
+               ret = upgrade_enable_all(zhp, &count);
+               if (ret != 0)
+                       return (ret);
 
-       ret = for_each_pool(argc, argv, B_TRUE, NULL, status_callback, &cb);
+               if (count > 0) {
+                       cbp->cb_first = B_FALSE;
+                       printnl = 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"));
+       if (printnl) {
+               (void) printf(gettext("\n"));
+       }
 
-       return (ret);
+       return (0);
 }
 
-typedef struct upgrade_cbdata {
-       int     cb_all;
-       int     cb_first;
-       int     cb_newer;
-       int     cb_argc;
-       uint64_t cb_version;
-       char    **cb_argv;
-} upgrade_cbdata_t;
-
 static int
-upgrade_cb(zpool_handle_t *zhp, void *arg)
+upgrade_list_older_cb(zpool_handle_t *zhp, void *arg)
 {
        upgrade_cbdata_t *cbp = arg;
        nvlist_t *config;
        uint64_t version;
-       int ret = 0;
 
        config = zpool_get_config(zhp, NULL);
        verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION,
            &version) == 0);
 
-       if (!cbp->cb_newer && version < SPA_VERSION) {
-               if (!cbp->cb_all) {
-                       if (cbp->cb_first) {
-                               (void) printf(gettext("The following pools are "
-                                   "out of date, and can be upgraded.  After "
-                                   "being\nupgraded, these pools will no "
-                                   "longer be accessible by older software "
-                                   "versions.\n\n"));
-                               (void) printf(gettext("VER  POOL\n"));
-                               (void) printf(gettext("---  ------------\n"));
-                               cbp->cb_first = B_FALSE;
-                       }
-
-                       (void) printf("%2llu   %s\n", (u_longlong_t)version,
-                           zpool_get_name(zhp));
-               } else {
-                       cbp->cb_first = B_FALSE;
-                       ret = zpool_upgrade(zhp, cbp->cb_version);
-                       if (!ret) {
-                               (void) printf(gettext("Successfully upgraded "
-                                   "'%s'\n\n"), zpool_get_name(zhp));
-                       }
-               }
-       } else if (cbp->cb_newer && version > SPA_VERSION) {
-               assert(!cbp->cb_all);
+       assert(SPA_VERSION_IS_SUPPORTED(version));
 
+       if (version < SPA_VERSION_FEATURES) {
                if (cbp->cb_first) {
                        (void) printf(gettext("The following pools are "
-                           "formatted using a newer software version and\n"
-                           "cannot be accessed on the current system.\n\n"));
+                           "formatted with legacy version numbers and can\n"
+                           "be upgraded to use feature flags.  After "
+                           "being upgraded, these pools\nwill no "
+                           "longer be accessible by software that does not "
+                           "support feature\nflags.\n\n"));
                        (void) printf(gettext("VER  POOL\n"));
                        (void) printf(gettext("---  ------------\n"));
                        cbp->cb_first = B_FALSE;
@@ -3345,14 +4652,65 @@ upgrade_cb(zpool_handle_t *zhp, void *arg)
                    zpool_get_name(zhp));
        }
 
-       zpool_close(zhp);
-       return (ret);
+       return (0);
+}
+
+static int
+upgrade_list_disabled_cb(zpool_handle_t *zhp, void *arg)
+{
+       upgrade_cbdata_t *cbp = arg;
+       nvlist_t *config;
+       uint64_t version;
+
+       config = zpool_get_config(zhp, NULL);
+       verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION,
+           &version) == 0);
+
+       if (version >= SPA_VERSION_FEATURES) {
+               int i;
+               boolean_t poolfirst = B_TRUE;
+               nvlist_t *enabled = zpool_get_features(zhp);
+
+               for (i = 0; i < SPA_FEATURES; i++) {
+                       const char *fguid = spa_feature_table[i].fi_guid;
+                       const char *fname = spa_feature_table[i].fi_uname;
+                       if (!nvlist_exists(enabled, fguid)) {
+                               if (cbp->cb_first) {
+                                       (void) printf(gettext("\nSome "
+                                           "supported features are not "
+                                           "enabled on the following pools. "
+                                           "Once a\nfeature is enabled the "
+                                           "pool may become incompatible with "
+                                           "software\nthat does not support "
+                                           "the feature. See "
+                                           "zpool-features(5) for "
+                                           "details.\n\n"));
+                                       (void) printf(gettext("POOL  "
+                                           "FEATURE\n"));
+                                       (void) printf(gettext("------"
+                                           "---------\n"));
+                                       cbp->cb_first = B_FALSE;
+                               }
+
+                               if (poolfirst) {
+                                       (void) printf(gettext("%s\n"),
+                                           zpool_get_name(zhp));
+                                       poolfirst = B_FALSE;
+                               }
+
+                               (void) printf(gettext("      %s\n"), fname);
+                       }
+               }
+       }
+
+       return (0);
 }
 
 /* ARGSUSED */
 static int
 upgrade_one(zpool_handle_t *zhp, void *data)
 {
+       boolean_t printnl = B_FALSE;
        upgrade_cbdata_t *cbp = data;
        uint64_t cur_version;
        int ret;
@@ -3367,26 +4725,45 @@ upgrade_one(zpool_handle_t *zhp, void *data)
        cur_version = zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL);
        if (cur_version > cbp->cb_version) {
                (void) printf(gettext("Pool '%s' is already formatted "
-                   "using more current version '%llu'.\n"),
-                   zpool_get_name(zhp), cur_version);
+                   "using more current version '%llu'.\n\n"),
+                   zpool_get_name(zhp), (u_longlong_t) cur_version);
                return (0);
        }
-       if (cur_version == cbp->cb_version) {
+
+       if (cbp->cb_version != SPA_VERSION && cur_version == cbp->cb_version) {
                (void) printf(gettext("Pool '%s' is already formatted "
-                   "using the current version.\n"), zpool_get_name(zhp));
+                   "using version %llu.\n\n"), zpool_get_name(zhp),
+                   (u_longlong_t) cbp->cb_version);
                return (0);
        }
 
-       ret = zpool_upgrade(zhp, cbp->cb_version);
+       if (cur_version != cbp->cb_version) {
+               printnl = B_TRUE;
+               ret = upgrade_version(zhp, cbp->cb_version);
+               if (ret != 0)
+                       return (ret);
+       }
+
+       if (cbp->cb_version >= SPA_VERSION_FEATURES) {
+               int count = 0;
+               ret = upgrade_enable_all(zhp, &count);
+               if (ret != 0)
+                       return (ret);
+
+               if (count != 0) {
+                       printnl = B_TRUE;
+               } else if (cur_version == SPA_VERSION) {
+                       (void) printf(gettext("Pool '%s' already has all "
+                           "supported features enabled.\n"),
+                           zpool_get_name(zhp));
+               }
+       }
 
-       if (!ret) {
-               (void) printf(gettext("Successfully upgraded '%s' "
-                   "from version %llu to version %llu\n\n"),
-                   zpool_get_name(zhp), (u_longlong_t)cur_version,
-                   (u_longlong_t)cbp->cb_version);
+       if (printnl) {
+               (void) printf(gettext("\n"));
        }
 
-       return (ret != 0);
+       return (0);
 }
 
 /*
@@ -3405,27 +4782,33 @@ zpool_do_upgrade(int argc, char **argv)
        upgrade_cbdata_t cb = { 0 };
        int ret = 0;
        boolean_t showversions = B_FALSE;
+       boolean_t upgradeall = B_FALSE;
        char *end;
 
 
        /* 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;
+                       upgradeall = B_TRUE;
                        break;
                case 'v':
                        showversions = B_TRUE;
                        break;
                case 'V':
                        cb.cb_version = strtoll(optarg, &end, 10);
-                       if (*end != '\0' || cb.cb_version > SPA_VERSION ||
-                           cb.cb_version < SPA_VERSION_1) {
+                       if (*end != '\0' ||
+                           !SPA_VERSION_IS_SUPPORTED(cb.cb_version)) {
                                (void) fprintf(stderr,
                                    gettext("invalid version '%s'\n"), optarg);
                                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);
@@ -3440,19 +4823,19 @@ zpool_do_upgrade(int argc, char **argv)
 
        if (cb.cb_version == 0) {
                cb.cb_version = SPA_VERSION;
-       } else if (!cb.cb_all && argc == 0) {
+       } else if (!upgradeall && argc == 0) {
                (void) fprintf(stderr, gettext("-V option is "
                    "incompatible with other arguments\n"));
                usage(B_FALSE);
        }
 
        if (showversions) {
-               if (cb.cb_all || argc != 0) {
+               if (upgradeall || argc != 0) {
                        (void) fprintf(stderr, gettext("-v option is "
                            "incompatible with other arguments\n"));
                        usage(B_FALSE);
                }
-       } else if (cb.cb_all) {
+       } else if (upgradeall) {
                if (argc != 0) {
                        (void) fprintf(stderr, gettext("-a option should not "
                            "be used along with a pool name\n"));
@@ -3460,11 +4843,27 @@ zpool_do_upgrade(int argc, char **argv)
                }
        }
 
-       (void) printf(gettext("This system is currently running "
-           "ZFS pool version %llu.\n\n"), SPA_VERSION);
-       cb.cb_first = B_TRUE;
+       (void) printf(gettext("This system supports ZFS pool feature "
+           "flags.\n\n"));
        if (showversions) {
-               (void) printf(gettext("The following versions are "
+               int i;
+
+               (void) printf(gettext("The following features are "
+                   "supported:\n\n"));
+               (void) printf(gettext("FEAT DESCRIPTION\n"));
+               (void) printf("----------------------------------------------"
+                   "---------------\n");
+               for (i = 0; i < SPA_FEATURES; i++) {
+                       zfeature_info_t *fi = &spa_feature_table[i];
+                       const char *ro = fi->fi_can_readonly ?
+                           " (read-only compatible)" : "";
+
+                       (void) printf("%-37s%s\n", fi->fi_uname, ro);
+                       (void) printf("     %s\n", fi->fi_desc);
+               }
+               (void) printf("\n");
+
+               (void) printf(gettext("The following legacy versions are also "
                    "supported:\n\n"));
                (void) printf(gettext("VER  DESCRIPTION\n"));
                (void) printf("---  -----------------------------------------"
@@ -3486,39 +4885,65 @@ 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"));
-       } else if (argc == 0) {
-               int notfound;
-
+               (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(" 27  Improved snapshot creation "
+                   "performance\n"));
+               (void) printf(gettext(" 28  Multiple vdev replacements\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 && upgradeall) {
+               cb.cb_first = B_TRUE;
                ret = zpool_iter(g_zfs, upgrade_cb, &cb);
-               notfound = cb.cb_first;
-
-               if (!cb.cb_all && ret == 0) {
-                       if (!cb.cb_first)
-                               (void) printf("\n");
-                       cb.cb_first = B_TRUE;
-                       cb.cb_newer = B_TRUE;
-                       ret = zpool_iter(g_zfs, upgrade_cb, &cb);
-                       if (!cb.cb_first) {
-                               notfound = B_FALSE;
-                               (void) printf("\n");
+               if (ret == 0 && cb.cb_first) {
+                       if (cb.cb_version == SPA_VERSION) {
+                               (void) printf(gettext("All pools are already "
+                                   "formatted using feature flags.\n\n"));
+                               (void) printf(gettext("Every feature flags "
+                                   "pool already has all supported features "
+                                   "enabled.\n"));
+                       } else {
+                               (void) printf(gettext("All pools are already "
+                                   "formatted with version %llu or higher.\n"),
+                                   (u_longlong_t) cb.cb_version);
                        }
                }
+       } else if (argc == 0) {
+               cb.cb_first = B_TRUE;
+               ret = zpool_iter(g_zfs, upgrade_list_older_cb, &cb);
+               assert(ret == 0);
+
+               if (cb.cb_first) {
+                       (void) printf(gettext("All pools are formatted "
+                           "using feature flags.\n\n"));
+               } else {
+                       (void) printf(gettext("\nUse 'zpool upgrade -v' "
+                           "for a list of available legacy versions.\n"));
+               }
+
+               cb.cb_first = B_TRUE;
+               ret = zpool_iter(g_zfs, upgrade_list_disabled_cb, &cb);
+               assert(ret == 0);
 
-               if (ret == 0) {
-                       if (notfound)
-                               (void) printf(gettext("All pools are formatted "
-                                   "using this version.\n"));
-                       else if (!cb.cb_all)
-                               (void) printf(gettext("Use 'zpool upgrade -v' "
-                                   "for a list of available versions and "
-                                   "their associated\nfeatures.\n"));
+               if (cb.cb_first) {
+                       (void) printf(gettext("Every feature flags pool has "
+                           "all supported features enabled.\n"));
+               } else {
+                       (void) printf(gettext("\n"));
                }
        } else {
                ret = for_each_pool(argc, argv, B_FALSE, NULL,
@@ -3534,47 +4959,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.
  */
@@ -3631,8 +5015,8 @@ get_history_one(zpool_handle_t *zhp, void *data)
                                continue;
                        (void) snprintf(internalstr,
                            sizeof (internalstr),
-                           "[internal %s txg:%lld] %s",
-                           hist_event_table[ievent], txg,
+                           "[internal %s txg:%llu] %s",
+                           zfs_history_event_names[ievent], (u_longlong_t)txg,
                            pathstr);
                        cmdstr = internalstr;
                }
@@ -3714,13 +5098,344 @@ zpool_do_history(int argc, char **argv)
            &cbdata);
 
        if (argc == 0 && cbdata.first == B_TRUE) {
-               (void) printf(gettext("no pools available\n"));
+               (void) fprintf(stderr, gettext("no pools available\n"));
                return (0);
        }
 
        return (ret);
 }
 
+typedef struct ev_opts {
+       int verbose;
+       int scripted;
+       int follow;
+       int clear;
+} ev_opts_t;
+
+static void
+zpool_do_events_short(nvlist_t *nvl)
+{
+       char ctime_str[26], str[32], *ptr;
+       int64_t *tv;
+       uint_t n;
+
+       verify(nvlist_lookup_int64_array(nvl, FM_EREPORT_TIME, &tv, &n) == 0);
+       memset(str, ' ', 32);
+       (void) ctime_r((const time_t *)&tv[0], ctime_str);
+       (void) strncpy(str,    ctime_str+4,  6);             /* 'Jun 30'     */
+       (void) strncpy(str+7,  ctime_str+20, 4);             /* '1993'       */
+       (void) strncpy(str+12, ctime_str+11, 8);             /* '21:49:08'   */
+       (void) sprintf(str+20, ".%09lld", (longlong_t)tv[1]);/* '.123456789' */
+       (void) printf(gettext("%s "), str);
+
+       verify(nvlist_lookup_string(nvl, FM_CLASS, &ptr) == 0);
+       (void) printf(gettext("%s\n"), ptr);
+}
+
+static void
+zpool_do_events_nvprint(nvlist_t *nvl, int depth)
+{
+       nvpair_t *nvp;
+
+       for (nvp = nvlist_next_nvpair(nvl, NULL);
+           nvp != NULL; nvp = nvlist_next_nvpair(nvl, nvp)) {
+
+               data_type_t type = nvpair_type(nvp);
+               const char *name = nvpair_name(nvp);
+
+               boolean_t b;
+               uint8_t i8;
+               uint16_t i16;
+               uint32_t i32;
+               uint64_t i64;
+               char *str;
+               nvlist_t *cnv;
+
+               printf(gettext("%*s%s = "), depth, "", name);
+
+               switch (type) {
+               case DATA_TYPE_BOOLEAN:
+                       printf(gettext("%s"), "1");
+                       break;
+
+               case DATA_TYPE_BOOLEAN_VALUE:
+                       (void) nvpair_value_boolean_value(nvp, &b);
+                       printf(gettext("%s"), b ? "1" : "0");
+                       break;
+
+               case DATA_TYPE_BYTE:
+                       (void) nvpair_value_byte(nvp, &i8);
+                       printf(gettext("0x%x"), i8);
+                       break;
+
+               case DATA_TYPE_INT8:
+                       (void) nvpair_value_int8(nvp, (void *)&i8);
+                       printf(gettext("0x%x"), i8);
+                       break;
+
+               case DATA_TYPE_UINT8:
+                       (void) nvpair_value_uint8(nvp, &i8);
+                       printf(gettext("0x%x"), i8);
+                       break;
+
+               case DATA_TYPE_INT16:
+                       (void) nvpair_value_int16(nvp, (void *)&i16);
+                       printf(gettext("0x%x"), i16);
+                       break;
+
+               case DATA_TYPE_UINT16:
+                       (void) nvpair_value_uint16(nvp, &i16);
+                       printf(gettext("0x%x"), i16);
+                       break;
+
+               case DATA_TYPE_INT32:
+                       (void) nvpair_value_int32(nvp, (void *)&i32);
+                       printf(gettext("0x%x"), i32);
+                       break;
+
+               case DATA_TYPE_UINT32:
+                       (void) nvpair_value_uint32(nvp, &i32);
+                       printf(gettext("0x%x"), i32);
+                       break;
+
+               case DATA_TYPE_INT64:
+                       (void) nvpair_value_int64(nvp, (void *)&i64);
+                       printf(gettext("0x%llx"), (u_longlong_t)i64);
+                       break;
+
+               case DATA_TYPE_UINT64:
+                       (void) nvpair_value_uint64(nvp, &i64);
+                       printf(gettext("0x%llx"), (u_longlong_t)i64);
+                       break;
+
+               case DATA_TYPE_HRTIME:
+                       (void) nvpair_value_hrtime(nvp, (void *)&i64);
+                       printf(gettext("0x%llx"), (u_longlong_t)i64);
+                       break;
+
+               case DATA_TYPE_STRING:
+                       (void) nvpair_value_string(nvp, &str);
+                       printf(gettext("\"%s\""), str ? str : "<NULL>");
+                       break;
+
+               case DATA_TYPE_NVLIST:
+                       printf(gettext("(embedded nvlist)\n"));
+                       (void) nvpair_value_nvlist(nvp, &cnv);
+                       zpool_do_events_nvprint(cnv, depth + 8);
+                       printf(gettext("%*s(end %s)"), depth, "", name);
+                       break;
+
+               case DATA_TYPE_NVLIST_ARRAY: {
+                       nvlist_t **val;
+                       uint_t i, nelem;
+
+                       (void) nvpair_value_nvlist_array(nvp, &val, &nelem);
+                       printf(gettext("(%d embedded nvlists)\n"), nelem);
+                       for (i = 0; i < nelem; i++) {
+                               printf(gettext("%*s%s[%d] = %s\n"),
+                                      depth, "", name, i, "(embedded nvlist)");
+                               zpool_do_events_nvprint(val[i], depth + 8);
+                               printf(gettext("%*s(end %s[%i])\n"),
+                                      depth, "", name, i);
+                       }
+                       printf(gettext("%*s(end %s)\n"), depth, "", name);
+                       }
+                       break;
+
+               case DATA_TYPE_INT8_ARRAY: {
+                       int8_t *val;
+                       uint_t i, nelem;
+
+                       (void) nvpair_value_int8_array(nvp, &val, &nelem);
+                       for (i = 0; i < nelem; i++)
+                               printf(gettext("0x%x "), val[i]);
+
+                       break;
+                       }
+
+               case DATA_TYPE_UINT8_ARRAY: {
+                       uint8_t *val;
+                       uint_t i, nelem;
+
+                       (void) nvpair_value_uint8_array(nvp, &val, &nelem);
+                       for (i = 0; i < nelem; i++)
+                               printf(gettext("0x%x "), val[i]);
+
+                       break;
+                       }
+
+               case DATA_TYPE_INT16_ARRAY: {
+                       int16_t *val;
+                       uint_t i, nelem;
+
+                       (void) nvpair_value_int16_array(nvp, &val, &nelem);
+                       for (i = 0; i < nelem; i++)
+                               printf(gettext("0x%x "), val[i]);
+
+                       break;
+                       }
+
+               case DATA_TYPE_UINT16_ARRAY: {
+                       uint16_t *val;
+                       uint_t i, nelem;
+
+                       (void) nvpair_value_uint16_array(nvp, &val, &nelem);
+                       for (i = 0; i < nelem; i++)
+                               printf(gettext("0x%x "), val[i]);
+
+                       break;
+                       }
+
+               case DATA_TYPE_INT32_ARRAY: {
+                       int32_t *val;
+                       uint_t i, nelem;
+
+                       (void) nvpair_value_int32_array(nvp, &val, &nelem);
+                       for (i = 0; i < nelem; i++)
+                               printf(gettext("0x%x "), val[i]);
+
+                       break;
+                       }
+
+               case DATA_TYPE_UINT32_ARRAY: {
+                       uint32_t *val;
+                       uint_t i, nelem;
+
+                       (void) nvpair_value_uint32_array(nvp, &val, &nelem);
+                       for (i = 0; i < nelem; i++)
+                               printf(gettext("0x%x "), val[i]);
+
+                       break;
+                       }
+
+               case DATA_TYPE_INT64_ARRAY: {
+                       int64_t *val;
+                       uint_t i, nelem;
+
+                       (void) nvpair_value_int64_array(nvp, &val, &nelem);
+                       for (i = 0; i < nelem; i++)
+                               printf(gettext("0x%llx "), (u_longlong_t)val[i]);
+
+                       break;
+                       }
+
+               case DATA_TYPE_UINT64_ARRAY: {
+                       uint64_t *val;
+                       uint_t i, nelem;
+
+                       (void) nvpair_value_uint64_array(nvp, &val, &nelem);
+                       for (i = 0; i < nelem; i++)
+                               printf(gettext("0x%llx "), (u_longlong_t)val[i]);
+
+                       break;
+                       }
+
+               case DATA_TYPE_STRING_ARRAY:
+               case DATA_TYPE_BOOLEAN_ARRAY:
+               case DATA_TYPE_BYTE_ARRAY:
+               case DATA_TYPE_DOUBLE:
+               case DATA_TYPE_UNKNOWN:
+                       printf(gettext("<unknown>"));
+                       break;
+               }
+
+               printf(gettext("\n"));
+       }
+}
+
+static int
+zpool_do_events_next(ev_opts_t *opts)
+{
+       nvlist_t *nvl;
+       int cleanup_fd, ret, dropped;
+
+        cleanup_fd = open(ZFS_DEV, O_RDWR);
+        VERIFY(cleanup_fd >= 0);
+
+       if (!opts->scripted)
+               (void) printf(gettext("%-30s %s\n"), "TIME", "CLASS");
+
+       while (1) {
+               ret = zpool_events_next(g_zfs, &nvl, &dropped,
+                   !!opts->follow, cleanup_fd);
+               if (ret || nvl == NULL)
+                       break;
+
+               if (dropped > 0)
+                       (void) printf(gettext("dropped %d events\n"), dropped);
+
+               zpool_do_events_short(nvl);
+
+               if (opts->verbose) {
+                       zpool_do_events_nvprint(nvl, 8);
+                       printf(gettext("\n"));
+               }
+               (void) fflush(stdout);
+
+               nvlist_free(nvl);
+       }
+
+        VERIFY(0 == close(cleanup_fd));
+
+       return (ret);
+}
+
+static int
+zpool_do_events_clear(ev_opts_t *opts)
+{
+       int count, ret;
+
+       ret = zpool_events_clear(g_zfs, &count);
+       if (!ret)
+               (void) printf(gettext("cleared %d events\n"), count);
+
+       return (ret);
+}
+
+/*
+ * zpool events [-vfc]
+ *
+ * Displays events logs by ZFS.
+ */
+int
+zpool_do_events(int argc, char **argv)
+{
+       ev_opts_t opts = { 0 };
+       int ret;
+       int c;
+
+       /* check options */
+       while ((c = getopt(argc, argv, "vHfc")) != -1) {
+               switch (c) {
+               case 'v':
+                       opts.verbose = 1;
+                       break;
+               case 'H':
+                       opts.scripted = 1;
+                       break;
+               case 'f':
+                       opts.follow = 1;
+                       break;
+               case 'c':
+                       opts.clear = 1;
+                       break;
+               case '?':
+                       (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+                           optopt);
+                       usage(B_FALSE);
+               }
+       }
+       argc -= optind;
+       argv += optind;
+
+       if (opts.clear)
+               ret = zpool_do_events_clear(&opts);
+       else
+               ret = zpool_do_events_next(&opts);
+
+       return ret;
+}
+
 static int
 get_callback(zpool_handle_t *zhp, void *data)
 {
@@ -3739,12 +5454,26 @@ get_callback(zpool_handle_t *zhp, void *data)
                    pl == cbp->cb_proplist)
                        continue;
 
-               if (zpool_get_prop(zhp, pl->pl_prop,
-                   value, sizeof (value), &srctype) != 0)
-                       continue;
+               if (pl->pl_prop == ZPROP_INVAL &&
+                   (zpool_prop_feature(pl->pl_user_prop) ||
+                   zpool_prop_unsupported(pl->pl_user_prop))) {
+                       srctype = ZPROP_SRC_LOCAL;
+
+                       if (zpool_prop_get_feature(zhp, pl->pl_user_prop,
+                           value, sizeof (value)) == 0) {
+                               zprop_print_one_property(zpool_get_name(zhp),
+                                   cbp, pl->pl_user_prop, value, srctype,
+                                   NULL, NULL);
+                       }
+               } else {
+                       if (zpool_get_prop(zhp, pl->pl_prop, value,
+                           sizeof (value), &srctype) != 0)
+                               continue;
 
-               zprop_print_one_property(zpool_get_name(zhp), cbp,
-                   zpool_prop_to_name(pl->pl_prop), value, srctype, NULL);
+                       zprop_print_one_property(zpool_get_name(zhp), cbp,
+                           zpool_prop_to_name(pl->pl_prop), value, srctype,
+                           NULL, NULL);
+               }
        }
        return (0);
 }
@@ -3756,8 +5485,11 @@ zpool_do_get(int argc, char **argv)
        zprop_list_t fake_name = { 0 };
        int ret;
 
-       if (argc < 3)
+       if (argc < 2) {
+               (void) fprintf(stderr, gettext("missing property "
+                   "argument\n"));
                usage(B_FALSE);
+       }
 
        cb.cb_first = B_TRUE;
        cb.cb_sources = ZPROP_SRC_ALL;
@@ -3767,7 +5499,7 @@ zpool_do_get(int argc, char **argv)
        cb.cb_columns[3] = GET_COL_SOURCE;
        cb.cb_type = ZFS_TYPE_POOL;
 
-       if (zprop_get_list(g_zfs, argv[1],  &cb.cb_proplist,
+       if (zprop_get_list(g_zfs, argv[1], &cb.cb_proplist,
            ZFS_TYPE_POOL) != 0)
                usage(B_FALSE);
 
@@ -3875,20 +5607,12 @@ int
 main(int argc, char **argv)
 {
        int ret;
-       int i;
+       int i = 0;
        char *cmdname;
 
        (void) setlocale(LC_ALL, "");
        (void) textdomain(TEXT_DOMAIN);
 
-       if ((g_zfs = libzfs_init()) == NULL) {
-               (void) fprintf(stderr, gettext("internal error: failed to "
-                   "initialize ZFS library\n"));
-               return (1);
-       }
-
-       libzfs_print_on_error(g_zfs, B_TRUE);
-
        opterr = 0;
 
        /*
@@ -3904,9 +5628,15 @@ main(int argc, char **argv)
        /*
         * Special case '-?'
         */
-       if (strcmp(cmdname, "-?") == 0)
+       if ((strcmp(cmdname, "-?") == 0) ||
+            strcmp(cmdname, "--help") == 0)
                usage(B_TRUE);
 
+       if ((g_zfs = libzfs_init()) == NULL)
+               return (1);
+
+       libzfs_print_on_error(g_zfs, B_TRUE);
+
        zpool_set_history_str("zpool", argc, argv, history_str);
        verify(zpool_stage_history(g_zfs, history_str) == 0);
 
@@ -3933,6 +5663,7 @@ main(int argc, char **argv)
                (void) fprintf(stderr, gettext("unrecognized "
                    "command '%s'\n"), cmdname);
                usage(B_FALSE);
+               ret = 1;
        }
 
        libzfs_fini(g_zfs);