Print this page
2619 asynchronous destruction of ZFS file systems
2747 SPA versioning with zfs feature flags
Reviewed by: Matt Ahrens <mahrens@delphix.com>
Reviewed by: George Wilson <gwilson@delphix.com>
Reviewed by: Richard Lowe <richlowe@richlowe.net>
Reviewed by: Dan Kruchinin <dan.kruchinin@gmail.com>
Approved by: Dan McDonald <danmcd@nexenta.com>

@@ -49,10 +49,11 @@
 
 #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 **);

@@ -198,11 +199,11 @@
                 return (gettext("\tattach [-f] <pool> <device> "
                     "<new-device>\n"));
         case HELP_CLEAR:
                 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:
                 return (gettext("\tdestroy [-f] <pool>\n"));
         case HELP_DETACH:

@@ -329,10 +330,16 @@
                     "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"));
         }
 
         /*
          * See comments at end of main().
          */

@@ -395,15 +402,19 @@
         }
 
         proplist = *props;
 
         if (poolprop) {
-                if ((prop = zpool_name_to_prop(propname)) == ZPROP_INVAL) {
+                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);
                 }
+                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 {

@@ -572,11 +583,11 @@
 
         return (ret);
 }
 
 /*
- * zpool create [-fn] [-o property=value] ...
+ * zpool create [-fnd] [-o property=value] ...
  *              [-O file-system-property=value] ...
  *              [-R root] [-m mountpoint] <pool> <dev> ...
  *
  *      -f      Force creation, even if devices appear in use
  *      -n      Do not create the pool, but display the resulting layout if it

@@ -583,10 +594,12 @@
  *              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>'
  *      -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
  * bulk of the vdev processing is done in get_vdev_spec() in zpool_vdev.c.  Once
  * we get the nvlist back from get_vdev_spec(), we either print out the contents

@@ -595,10 +608,11 @@
 int
 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;
         int ret = 1;
         char *altroot = NULL;

@@ -606,18 +620,21 @@
         nvlist_t *fsprops = NULL;
         nvlist_t *props = NULL;
         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;
                         break;
                 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(
                             ZPOOL_PROP_ALTROOT), optarg, &props, B_TRUE))
                                 goto errout;

@@ -641,10 +658,25 @@
                         *propval = '\0';
                         propval++;
 
                         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) {
                                 (void) fprintf(stderr, gettext("missing "
                                     "'=' for -O option\n"));

@@ -706,11 +738,10 @@
                     "specification: at least one toplevel vdev must be "
                     "specified\n"));
                 goto errout;
         }
 
-
         if (altroot != NULL && altroot[0] != '/') {
                 (void) fprintf(stderr, gettext("invalid alternate root '%s': "
                     "must be an absolute path\n"), altroot);
                 goto errout;
         }

@@ -788,10 +819,31 @@
                 ret = 0;
         } else {
                 /*
                  * 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,
                             ZFS_TYPE_FILESYSTEM);
                         if (pool != NULL) {

@@ -1119,10 +1171,14 @@
 
                 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_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),

@@ -1236,10 +1292,14 @@
 
                 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:

@@ -1402,10 +1462,24 @@
         case ZPOOL_STATUS_VERSION_NEWER:
                 (void) printf(gettext(" status: The pool is formatted using an "
                     "incompatible version.\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 "
                     "another system.\n"));
                 break;
 

@@ -1459,10 +1533,24 @@
                         (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 "
                             "imported. Attach the missing\n\tdevices and try "

@@ -1534,13 +1622,13 @@
 
         verify(nvlist_lookup_uint64(config,
             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 &&
             !(flags & ZFS_IMPORT_ANY_HOST)) {
                 uint64_t hostid;
 

@@ -2471,19 +2559,17 @@
  */
 static void
 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.

@@ -2494,13 +2580,23 @@
                 if (!first)
                         (void) printf("  ");
                 else
                         first = B_FALSE;
 
+                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", width, header);
                 else

@@ -2555,10 +2651,15 @@
                                 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 = "-";
                 }
 
 

@@ -3894,10 +3995,35 @@
                 (void) printf(gettext("action: Access the pool from a system "
                     "running more recent software, or\n\trestore the pool from "
                     "backup.\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 "
                     "replicas exist for the pool to continue functioning "
                     "in a\n\tdegraded state.\n"));

@@ -4118,11 +4244,12 @@
 
         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_newer && SPA_VERSION_IS_SUPPORTED(version) &&
+            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 "

@@ -4141,17 +4268,18 @@
                         if (!ret) {
                                 (void) printf(gettext("Successfully upgraded "
                                     "'%s'\n\n"), zpool_get_name(zhp));
                         }
                 }
-        } else if (cbp->cb_newer && version > SPA_VERSION) {
+        } else if (cbp->cb_newer && !SPA_VERSION_IS_SUPPORTED(version)) {
                 assert(!cbp->cb_all);
 
                 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 using an unsupported software version "
+                            "and\ncannot be accessed on the current "
+                            "system.\n\n"));
                         (void) printf(gettext("VER  POOL\n"));
                         (void) printf(gettext("---  ------------\n"));
                         cbp->cb_first = B_FALSE;
                 }
 

@@ -4231,12 +4359,12 @@
                 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;

@@ -4277,12 +4405,12 @@
                             "be used along with a pool name\n"));
                         usage(B_FALSE);
                 }
         }
 
-        (void) printf(gettext("This system is currently running "
-            "ZFS pool version %llu.\n\n"), SPA_VERSION);
+        (void) printf(gettext("This system supports ZFS pool feature "
+            "flags.\n\n"));
         cb.cb_first = B_TRUE;
         if (showversions) {
                 (void) printf(gettext("The following versions are "
                     "supported:\n\n"));
                 (void) printf(gettext("VER  DESCRIPTION\n"));

@@ -4529,18 +4657,31 @@
                  */
                 if (pl->pl_prop == ZPOOL_PROP_NAME &&
                     pl == cbp->cb_proplist)
                         continue;
 
-                if (zpool_get_prop(zhp, pl->pl_prop,
-                    value, sizeof (value), &srctype) != 0)
+                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,
-                    NULL);
+                            zpool_prop_to_name(pl->pl_prop), value, srctype,
+                            NULL, NULL);
         }
+        }
         return (0);
 }
 
 int
 zpool_do_get(int argc, char **argv)

@@ -4547,12 +4688,15 @@
 {
         zprop_get_cbdata_t cb = { 0 };
         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;
         cb.cb_columns[0] = GET_COL_NAME;
         cb.cb_columns[1] = GET_COL_PROPERTY;