PSARC 2006/206 zpool upgrade
authoreschrock
Thu, 06 Apr 2006 20:12:27 -0700
changeset 1760 e1ad2821c30d
parent 1759 a5d56f4e29f5
child 1761 e7a0d80f0c47
PSARC 2006/206 zpool upgrade 6399930 want 'zpool upgrade' to control change of version number 6400742 'zpool destroy' not clean inuse tag that have to need '-f' to use them again
usr/src/cmd/fs.d/zfs/fstyp/fstyp.c
usr/src/cmd/truss/codes.c
usr/src/cmd/zpool/zpool_main.c
usr/src/lib/libzfs/common/libzfs.h
usr/src/lib/libzfs/common/libzfs_import.c
usr/src/lib/libzfs/common/libzfs_pool.c
usr/src/lib/libzfs/common/libzfs_status.c
usr/src/lib/libzfs/spec/libzfs.spec
usr/src/lib/libzfs_jni/common/libzfs_jni_pool.c
usr/src/uts/common/fs/zfs/spa.c
usr/src/uts/common/fs/zfs/spa_config.c
usr/src/uts/common/fs/zfs/sys/spa.h
usr/src/uts/common/fs/zfs/sys/uberblock_impl.h
usr/src/uts/common/fs/zfs/sys/zfs_znode.h
usr/src/uts/common/fs/zfs/uberblock.c
usr/src/uts/common/fs/zfs/zfs_ioctl.c
usr/src/uts/common/fs/zfs/zfs_znode.c
usr/src/uts/common/sys/fs/zfs.h
--- a/usr/src/cmd/fs.d/zfs/fstyp/fstyp.c	Thu Apr 06 19:27:51 2006 -0700
+++ b/usr/src/cmd/fs.d/zfs/fstyp/fstyp.c	Thu Apr 06 20:12:27 2006 -0700
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -112,6 +111,7 @@
 	int c, fd;
 	int verbose = 0;
 	nvlist_t *config;
+	uint64_t state;
 
 	(void) setlocale(LC_ALL, "");
 
@@ -145,6 +145,10 @@
 	if ((config = zpool_read_label(fd)) == NULL)
 		return (1);
 
+	if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE,
+	    &state) != 0 || state == POOL_STATE_DESTROYED)
+		return (1);
+
 	(void) printf("zfs\n");
 
 	if (verbose)
--- a/usr/src/cmd/truss/codes.c	Thu Apr 06 19:27:51 2006 -0700
+++ b/usr/src/cmd/truss/codes.c	Thu Apr 06 20:12:27 2006 -0700
@@ -869,8 +869,6 @@
 		"zfs_cmd_t" },
 	{ (uint_t)ZFS_IOC_POOL_CONFIGS,		"ZFS_IOC_POOL_CONFIGS",
 		"zfs_cmd_t" },
-	{ (uint_t)ZFS_IOC_POOL_GUID,		"ZFS_IOC_POOL_GUID",
-		"zfs_cmd_t" },
 	{ (uint_t)ZFS_IOC_POOL_STATS,		"ZFS_IOC_POOL_STATS",
 		"zfs_cmd_t" },
 	{ (uint_t)ZFS_IOC_POOL_TRYIMPORT,	"ZFS_IOC_POOL_TRYIMPORT",
@@ -879,6 +877,8 @@
 		"zfs_cmd_t" },
 	{ (uint_t)ZFS_IOC_POOL_FREEZE,		"ZFS_IOC_POOL_FREEZE",
 		"zfs_cmd_t" },
+	{ (uint_t)ZFS_IOC_POOL_UPGRADE,		"ZFS_IOC_POOL_UPGRADE",
+		"zfs_cmd_t" },
 	{ (uint_t)ZFS_IOC_VDEV_ADD,		"ZFS_IOC_VDEV_ADD",
 		"zfs_cmd_t" },
 	{ (uint_t)ZFS_IOC_VDEV_REMOVE,		"ZFS_IOC_VDEV_REMOVE",
--- a/usr/src/cmd/zpool/zpool_main.c	Thu Apr 06 19:27:51 2006 -0700
+++ b/usr/src/cmd/zpool/zpool_main.c	Thu Apr 06 20:12:27 2006 -0700
@@ -69,6 +69,8 @@
 static int zpool_do_import(int, char **);
 static int zpool_do_export(int, char **);
 
+static int zpool_do_upgrade(int, char **);
+
 /*
  * These libumem hooks provide a reasonable set of defaults for the allocator's
  * debugging facilities.
@@ -100,7 +102,8 @@
 	HELP_ONLINE,
 	HELP_REPLACE,
 	HELP_SCRUB,
-	HELP_STATUS
+	HELP_STATUS,
+	HELP_UPGRADE
 } zpool_help_t;
 
 
@@ -141,6 +144,7 @@
 	{ NULL },
 	{ "import",	zpool_do_import,	HELP_IMPORT		},
 	{ "export",	zpool_do_export,	HELP_EXPORT		},
+	{ "upgrade",	zpool_do_upgrade,	HELP_UPGRADE		}
 };
 
 #define	NCOMMAND	(sizeof (command_table) / sizeof (command_table[0]))
@@ -188,6 +192,10 @@
 		return (gettext("\tscrub [-s] <pool> ...\n"));
 	case HELP_STATUS:
 		return (gettext("\tstatus [-vx] [pool] ...\n"));
+	case HELP_UPGRADE:
+		return (gettext("\tupgrade\n"
+		    "\tupgrade -v\n"
+		    "\tupgrade <-a | pool>\n"));
 	}
 
 	abort();
@@ -793,6 +801,10 @@
 			(void) printf(gettext("insufficient replicas"));
 			break;
 
+		case VDEV_AUX_VERSION_NEWER:
+			(void) printf(gettext("newer version"));
+			break;
+
 		default:
 			(void) printf(gettext("corrupted data"));
 			break;
@@ -882,6 +894,16 @@
 		    "corrupted.\n"));
 		break;
 
+	case ZPOOL_STATUS_VERSION_OLDER:
+		(void) printf(gettext("status: The pool is formatted using an "
+		    "older on-disk version.\n"));
+		break;
+
+	case ZPOOL_STATUS_VERSION_NEWER:
+		(void) printf(gettext("status: The pool is formatted using an "
+		    "incompatible version.\n"));
+		break;
+
 	default:
 		/*
 		 * No other status can be seen when importing pools.
@@ -893,40 +915,48 @@
 	 * Print out an action according to the overall state of the pool.
 	 */
 	if (strcmp(health, gettext("ONLINE")) == 0) {
-		(void) printf(gettext("action: The pool can be imported"
-		    " using its name or numeric identifier."));
-	if (pool_state == POOL_STATE_DESTROYED)
-		(void) printf(gettext("  The\n\tpool was destroyed, "
-		    "but can be imported using the '-Df' flags.\n"));
-		else if (pool_state != POOL_STATE_EXPORTED)
-			(void) printf(gettext("  The\n\tpool may be active on "
-			    "on another system, but can be imported using\n\t"
-			    "the '-f' flag.\n"));
+		if (reason == ZPOOL_STATUS_VERSION_OLDER)
+			(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
-			(void) printf("\n");
+			(void) printf(gettext("action: The pool can be "
+			    "imported using its name or numeric "
+			    "identifier.\n"));
 	} else if (strcmp(health, gettext("DEGRADED")) == 0) {
 		(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."));
-		if (pool_state == POOL_STATE_DESTROYED)
-			(void) printf(gettext("  The\n\tpool was destroyed, "
-			    "but can be imported using the '-Df' flags.\n"));
-		else if (pool_state != POOL_STATE_EXPORTED)
-			(void) printf(gettext("  The\n\tpool may be active on "
-			    "on another system, but can be imported using\n\t"
-			    "the '-f' flag.\n"));
-		else
-			(void) printf("\n");
+		    "tolerance of the pool may be compromised if imported.\n"));
 	} else {
-		if (reason == ZPOOL_STATUS_MISSING_DEV_R ||
-		    reason == ZPOOL_STATUS_MISSING_DEV_NR ||
-		    reason == ZPOOL_STATUS_BAD_GUID_SUM)
+		switch (reason) {
+		case ZPOOL_STATUS_VERSION_NEWER:
+			(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_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 "
 			    "again.\n"));
-		else
+			break;
+		default:
 			(void) printf(gettext("action: The pool cannot be "
 			    "imported due to damaged devices or data.\n"));
+		}
+	}
+
+	if (strcmp(health, gettext("FAULTED")) != 0) {
+		if (pool_state == POOL_STATE_DESTROYED)
+			(void) printf(gettext("\tThe pool was destroyed, "
+			    "but can be imported using the '-Df' flags.\n"));
+		else if (pool_state != POOL_STATE_EXPORTED)
+			(void) printf(gettext("\tThe pool may be active on "
+			    "on another system, but can be imported using\n\t"
+			    "the '-f' flag.\n"));
 	}
 
 	if (msgid != NULL)
@@ -959,13 +989,20 @@
 	zpool_handle_t *zhp;
 	char *name;
 	uint64_t state;
+	uint64_t version;
 
 	verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
 	    &name) == 0);
 
 	verify(nvlist_lookup_uint64(config,
 	    ZPOOL_CONFIG_POOL_STATE, &state) == 0);
-	if (state != POOL_STATE_EXPORTED && !force) {
+	verify(nvlist_lookup_uint64(config,
+	    ZPOOL_CONFIG_VERSION, &version) == 0);
+	if (version > ZFS_VERSION) {
+		(void) fprintf(stderr, gettext("cannot import '%s': pool "
+		    "is formatted using a newer ZFS version\n"), name);
+		return (1);
+	} else if (state != POOL_STATE_EXPORTED && !force) {
 		(void) fprintf(stderr, gettext("cannot import '%s': pool "
 		    "may be in use from other system\n"), name);
 		(void) fprintf(stderr, gettext("use '-f' to import anyway\n"));
@@ -2324,6 +2361,10 @@
 			(void) printf(gettext("insufficient replicas"));
 			break;
 
+		case VDEV_AUX_VERSION_NEWER:
+			(void) printf(gettext("newer version"));
+			break;
+
 		default:
 			(void) printf(gettext("corrupted data"));
 			break;
@@ -2529,6 +2570,24 @@
 		    "from a backup source.\n"));
 		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("action: Upgrade the pool using 'zpool "
+		    "upgrade'.  Once this is done, the\n\tpool will no longer "
+		    "be accessible on older software versions.\n"));
+		break;
+
+	case ZPOOL_STATUS_VERSION_NEWER:
+		(void) printf(gettext("status: The pool has been upgraded to a "
+		    "newer, incompatible on-disk version.\n\tThe pool cannot "
+		    "be accessed on this system.\n"));
+		(void) printf(gettext("action: Access the pool from a system "
+		    "running more recent software, or\n\trestore the pool from "
+		    "backup.\n"));
+		break;
+
 	default:
 		/*
 		 * The remaining errors can't actually be generated, yet.
@@ -2644,6 +2703,191 @@
 	return (ret);
 }
 
+typedef struct upgrade_cbdata {
+	int	cb_all;
+	int	cb_first;
+	int	cb_newer;
+} upgrade_cbdata_t;
+
+static int
+upgrade_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 < ZFS_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 = FALSE;
+			}
+
+			(void) printf("%2llu   %s\n", version,
+			    zpool_get_name(zhp));
+		} else {
+			cbp->cb_first = FALSE;
+			ret = zpool_upgrade(zhp);
+			if (ret == 0)
+				(void) printf(gettext("Successfully upgraded "
+				    "'%s'\n"), zpool_get_name(zhp));
+		}
+	} else if (cbp->cb_newer && version > ZFS_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"));
+			(void) printf(gettext("VER  POOL\n"));
+			(void) printf(gettext("---  ------------\n"));
+			cbp->cb_first = FALSE;
+		}
+
+		(void) printf("%2llu   %s\n", version,
+		    zpool_get_name(zhp));
+	}
+
+	zpool_close(zhp);
+	return (ret);
+}
+
+/* ARGSUSED */
+static int
+upgrade_one(zpool_handle_t *zhp, void *unused)
+{
+	nvlist_t *config;
+	uint64_t version;
+	int ret;
+
+	config = zpool_get_config(zhp, NULL);
+	verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION,
+	    &version) == 0);
+
+	if (version == ZFS_VERSION) {
+		(void) printf(gettext("Pool '%s' is already formatted "
+		    "using the current version.\n"), zpool_get_name(zhp));
+		return (0);
+	}
+
+	ret = zpool_upgrade(zhp);
+	if (ret == 0)
+		(void) printf(gettext("Successfully upgraded '%s'\n"),
+		    zpool_get_name(zhp));
+
+	return (ret != 0);
+}
+
+/*
+ * zpool upgrade
+ * zpool upgrade -v
+ * zpool upgrade <-a | pool>
+ *
+ * With no arguments, display downrev'd ZFS pool available for upgrade.
+ * Individual pools can be upgraded by specifying the pool, and '-a' will
+ * upgrade all pools.
+ */
+int
+zpool_do_upgrade(int argc, char **argv)
+{
+	int c;
+	upgrade_cbdata_t cb = { 0 };
+	int ret = 0;
+	boolean_t showversions = B_FALSE;
+
+	/* check options */
+	while ((c = getopt(argc, argv, "av")) != -1) {
+		switch (c) {
+		case 'a':
+			cb.cb_all = TRUE;
+			break;
+		case 'v':
+			showversions = B_TRUE;
+			break;
+		case '?':
+			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
+			    optopt);
+			usage(FALSE);
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+
+	if (showversions) {
+		if (cb.cb_all || argc != 0) {
+			(void) fprintf(stderr, gettext("-v option is "
+			    "incompatible with other arguments\n"));
+			usage(FALSE);
+		}
+	} else if (cb.cb_all) {
+		if (argc != 0) {
+			(void) fprintf(stderr, gettext("-a option is "
+			    "incompatible with other arguments\n"));
+			usage(FALSE);
+		}
+	}
+
+	(void) printf(gettext("This system is currently running ZFS version "
+	    "%llu.\n\n"), ZFS_VERSION);
+	cb.cb_first = TRUE;
+	if (showversions) {
+		(void) printf(gettext("The following versions are "
+		    "suppored:\n\n"));
+		(void) printf(gettext("VER  DESCRIPTION\n"));
+		(void) printf("---  -----------------------------------------"
+		    "---------------\n");
+		(void) printf(gettext(" 1   Initial ZFS version.\n\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;
+
+		ret = zpool_iter(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(upgrade_cb, &cb);
+			if (!cb.cb_first) {
+				notfound = B_FALSE;
+				(void) printf("\n");
+			}
+		}
+
+		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"));
+		}
+	} else {
+		ret = for_each_pool(argc, argv, FALSE, upgrade_one, NULL);
+	}
+
+	return (ret);
+}
+
 int
 main(int argc, char **argv)
 {
--- a/usr/src/lib/libzfs/common/libzfs.h	Thu Apr 06 19:27:51 2006 -0700
+++ b/usr/src/lib/libzfs/common/libzfs.h	Thu Apr 06 20:12:27 2006 -0700
@@ -108,13 +108,14 @@
 	ZPOOL_STATUS_CORRUPT_POOL,	/* pool metadata is corrupted */
 	ZPOOL_STATUS_CORRUPT_DATA,	/* data errors in user (meta)data */
 	ZPOOL_STATUS_FAILING_DEV,	/* device experiencing errors */
-	ZPOOL_STATUS_VERSION_MISMATCH,	/* bad on-disk version */
+	ZPOOL_STATUS_VERSION_NEWER,	/* newer on-disk version */
 
 	/*
 	 * The following are not faults per se, but still an error possibly
 	 * requiring administrative attention.  There is no corresponding
 	 * message ID.
 	 */
+	ZPOOL_STATUS_VERSION_OLDER,	/* older on-disk version */
 	ZPOOL_STATUS_RESILVERING,	/* device being resilvered */
 	ZPOOL_STATUS_OFFLINE_DEV,	/* device online */
 
@@ -153,6 +154,7 @@
  * Miscellaneous pool functions
  */
 extern char *zpool_vdev_name(zpool_handle_t *, nvlist_t *);
+extern int zpool_upgrade(zpool_handle_t *);
 
 /*
  * Basic handle manipulations.  These functions do not create or destroy the
--- a/usr/src/lib/libzfs/common/libzfs_import.c	Thu Apr 06 19:27:51 2006 -0700
+++ b/usr/src/lib/libzfs/common/libzfs_import.c	Thu Apr 06 20:12:27 2006 -0700
@@ -293,6 +293,26 @@
 }
 
 /*
+ * Returns true if the named pool matches the given GUID.
+ */
+boolean_t
+pool_active(const char *name, uint64_t guid)
+{
+	zpool_handle_t *zhp;
+	uint64_t theguid;
+
+	if ((zhp = zpool_open_silent(name)) == NULL)
+		return (B_FALSE);
+
+	verify(nvlist_lookup_uint64(zhp->zpool_config, ZPOOL_CONFIG_POOL_GUID,
+	    &theguid) == 0);
+
+	zpool_close(zhp);
+
+	return (theguid == guid);
+}
+
+/*
  * Convert our list of pools into the definitive set of configurations.  We
  * start by picking the best config for each toplevel vdev.  Once that's done,
  * we assemble the toplevel vdevs into a full config for the pool.  We make a
@@ -481,9 +501,7 @@
 		verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
 		    &guid) == 0);
 
-		(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
-		if (zfs_ioctl(ZFS_IOC_POOL_GUID, &zc) == 0 &&
-		    guid == zc.zc_guid) {
+		if (pool_active(name, guid)) {
 			nvlist_free(config);
 			continue;
 		}
@@ -706,7 +724,6 @@
 	nvlist_t *config;
 	char *name;
 	int ret;
-	zfs_cmd_t zc = { 0 };
 	uint64_t guid, vdev_guid;
 	zpool_handle_t *zhp;
 	nvlist_t *pool_config;
@@ -732,16 +749,12 @@
 	case POOL_STATE_ACTIVE:
 		/*
 		 * For an active pool, we have to determine if it's really part
-		 * of an active pool (in which case the pool will exist and the
-		 * guid will be the same), or whether it's part of an active
-		 * pool that was disconnected without being explicitly exported.
-		 *
-		 * We use the direct ioctl() first to avoid triggering an error
-		 * message if the pool cannot be opened.
+		 * of a currently active pool (in which case the pool will exist
+		 * and the guid will be the same), or whether it's part of an
+		 * active pool that was disconnected without being explicitly
+		 * exported.
 		 */
-		(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
-		if (zfs_ioctl(ZFS_IOC_POOL_GUID, &zc) == 0 &&
-		    guid == zc.zc_guid) {
+		if (pool_active(name, guid)) {
 			/*
 			 * Because the device may have been removed while
 			 * offlined, we only report it as active if the vdev is
--- a/usr/src/lib/libzfs/common/libzfs_pool.c	Thu Apr 06 19:27:51 2006 -0700
+++ b/usr/src/lib/libzfs/common/libzfs_pool.c	Thu Apr 06 20:12:27 2006 -0700
@@ -1590,3 +1590,28 @@
 
 	return (0);
 }
+
+/*
+ * Upgrade a ZFS pool to the latest on-disk version.
+ */
+int
+zpool_upgrade(zpool_handle_t *zhp)
+{
+	zfs_cmd_t zc = { 0 };
+
+	(void) strcpy(zc.zc_name, zhp->zpool_name);
+	if (zfs_ioctl(ZFS_IOC_POOL_UPGRADE, &zc) != 0) {
+		switch (errno) {
+		case EPERM:
+			zfs_error(dgettext(TEXT_DOMAIN, "cannot upgrade '%s': "
+			    "permission denied"), zhp->zpool_name);
+			break;
+		default:
+			zfs_baderror(errno);
+		}
+
+		return (-1);
+	}
+
+	return (0);
+}
--- a/usr/src/lib/libzfs/common/libzfs_status.c	Thu Apr 06 19:27:51 2006 -0700
+++ b/usr/src/lib/libzfs/common/libzfs_status.c	Thu Apr 06 20:12:27 2006 -0700
@@ -177,13 +177,23 @@
 	vdev_stat_t *vs;
 	uint_t vsc;
 	uint64_t nerr;
+	uint64_t version;
 
+	verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION,
+	    &version) == 0);
 	verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
 	    &nvroot) == 0);
 	verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_STATS,
 	    (uint64_t **)&vs, &vsc) == 0);
 
 	/*
+	 * Newer on-disk version.
+	 */
+	if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
+	    vs->vs_aux == VDEV_AUX_VERSION_NEWER)
+		return (ZPOOL_STATUS_VERSION_NEWER);
+
+	/*
 	 * Check that the config is complete.
 	 */
 	if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
@@ -244,11 +254,10 @@
 		return (ZPOOL_STATUS_RESILVERING);
 
 	/*
-	 * We currently have no way to detect the following errors:
-	 *
-	 * 	CORRUPT_CACHE
-	 * 	VERSION_MISMATCH
+	 * Outdated, but usable, version
 	 */
+	if (version < ZFS_VERSION)
+		return (ZPOOL_STATUS_VERSION_OLDER);
 
 	return (ZPOOL_STATUS_OK);
 }
--- a/usr/src/lib/libzfs/spec/libzfs.spec	Thu Apr 06 19:27:51 2006 -0700
+++ b/usr/src/lib/libzfs/spec/libzfs.spec	Thu Apr 06 20:12:27 2006 -0700
@@ -328,6 +328,10 @@
 version SUNWprivate_1.1
 end
 
+function zpool_upgrade
+version SUNWprivate_1.1
+end
+
 function zpool_vdev_online
 version SUNWprivate_1.1
 end
--- a/usr/src/lib/libzfs_jni/common/libzfs_jni_pool.c	Thu Apr 06 19:27:51 2006 -0700
+++ b/usr/src/lib/libzfs_jni/common/libzfs_jni_pool.c	Thu Apr 06 20:12:27 2006 -0700
@@ -129,8 +129,10 @@
 	{ ZPOOL_STATUS_CORRUPT_POOL, "ZPOOL_STATUS_CORRUPT_POOL" },
 	{ ZPOOL_STATUS_CORRUPT_DATA, "ZPOOL_STATUS_CORRUPT_DATA" },
 	{ ZPOOL_STATUS_FAILING_DEV, "ZPOOL_STATUS_FAILING_DEV" },
-	{ ZPOOL_STATUS_VERSION_MISMATCH,
-	    "ZPOOL_STATUS_VERSION_MISMATCH" },
+	{ ZPOOL_STATUS_VERSION_OLDER,
+	    "ZPOOL_STATUS_VERSION_OLDER" },
+	{ ZPOOL_STATUS_VERSION_NEWER,
+	    "ZPOOL_STATUS_VERSION_NEWER" },
 	{ ZPOOL_STATUS_RESILVERING, "ZPOOL_STATUS_RESILVERING" },
 	{ ZPOOL_STATUS_OFFLINE_DEV, "ZPOOL_STATUS_OFFLINE_DEV" },
 	{ ZPOOL_STATUS_OK, "ZPOOL_STATUS_OK" },
--- a/usr/src/uts/common/fs/zfs/spa.c	Thu Apr 06 19:27:51 2006 -0700
+++ b/usr/src/uts/common/fs/zfs/spa.c	Thu Apr 06 20:12:27 2006 -0700
@@ -326,6 +326,8 @@
 	 * If we weren't able to find a single valid uberblock, return failure.
 	 */
 	if (ub->ub_txg == 0) {
+		vdev_set_state(rvd, B_TRUE, VDEV_STATE_CANT_OPEN,
+		    VDEV_AUX_CORRUPT_DATA);
 		error = ENXIO;
 		goto out;
 	}
@@ -333,7 +335,9 @@
 	/*
 	 * If the pool is newer than the code, we can't open it.
 	 */
-	if (ub->ub_version > UBERBLOCK_VERSION) {
+	if (ub->ub_version > ZFS_VERSION) {
+		vdev_set_state(rvd, B_TRUE, VDEV_STATE_CANT_OPEN,
+		    VDEV_AUX_VERSION_NEWER);
 		error = ENOTSUP;
 		goto out;
 	}
@@ -729,6 +733,7 @@
 	spa_activate(spa);
 
 	spa->spa_uberblock.ub_txg = txg - 1;
+	spa->spa_uberblock.ub_version = ZFS_VERSION;
 	spa->spa_ubsync = spa->spa_uberblock;
 
 	/*
@@ -2267,3 +2272,21 @@
 {
 	return (vdev_lookup_by_guid(spa->spa_root_vdev, guid));
 }
+
+void
+spa_upgrade(spa_t *spa)
+{
+	spa_config_enter(spa, RW_WRITER, FTAG);
+
+	/*
+	 * This should only be called for a non-faulted pool, and since a
+	 * future version would result in an unopenable pool, this shouldn't be
+	 * possible.
+	 */
+	ASSERT(spa->spa_uberblock.ub_version <= ZFS_VERSION);
+
+	spa->spa_uberblock.ub_version = ZFS_VERSION;
+	vdev_config_dirty(spa->spa_root_vdev);
+
+	spa_config_exit(spa, FTAG);
+}
--- a/usr/src/uts/common/fs/zfs/spa_config.c	Thu Apr 06 19:27:51 2006 -0700
+++ b/usr/src/uts/common/fs/zfs/spa_config.c	Thu Apr 06 20:12:27 2006 -0700
@@ -279,7 +279,7 @@
 	VERIFY(nvlist_alloc(&config, NV_UNIQUE_NAME, KM_SLEEP) == 0);
 
 	VERIFY(nvlist_add_uint64(config, ZPOOL_CONFIG_VERSION,
-	    UBERBLOCK_VERSION) == 0);
+	    spa->spa_uberblock.ub_version) == 0);
 	VERIFY(nvlist_add_string(config, ZPOOL_CONFIG_POOL_NAME,
 	    spa_name(spa)) == 0);
 	VERIFY(nvlist_add_uint64(config, ZPOOL_CONFIG_POOL_STATE,
--- a/usr/src/uts/common/fs/zfs/sys/spa.h	Thu Apr 06 19:27:51 2006 -0700
+++ b/usr/src/uts/common/fs/zfs/sys/spa.h	Thu Apr 06 20:12:27 2006 -0700
@@ -397,6 +397,7 @@
 extern uint64_t spa_get_random(uint64_t range);
 extern void sprintf_blkptr(char *buf, int len, blkptr_t *bp);
 extern void spa_freeze(spa_t *spa);
+extern void spa_upgrade(spa_t *spa);
 extern void spa_evict_all(void);
 extern vdev_t *spa_lookup_by_guid(spa_t *spa, uint64_t guid);
 
--- a/usr/src/uts/common/fs/zfs/sys/uberblock_impl.h	Thu Apr 06 19:27:51 2006 -0700
+++ b/usr/src/uts/common/fs/zfs/sys/uberblock_impl.h	Thu Apr 06 20:12:27 2006 -0700
@@ -45,12 +45,11 @@
  * expect the magic number in the first word won't work.
  */
 #define	UBERBLOCK_MAGIC		0x00bab10c		/* oo-ba-bloc!	*/
-#define	UBERBLOCK_VERSION	1ULL
 #define	UBERBLOCK_SHIFT		10			/* up to 1K	*/
 
 struct uberblock {
 	uint64_t	ub_magic;	/* UBERBLOCK_MAGIC		*/
-	uint64_t	ub_version;	/* UBERBLOCK_VERSION		*/
+	uint64_t	ub_version;	/* ZFS_VERSION			*/
 	uint64_t	ub_txg;		/* txg of last sync		*/
 	uint64_t	ub_guid_sum;	/* sum of all vdev guids	*/
 	uint64_t	ub_timestamp;	/* UTC time of last sync	*/
--- a/usr/src/uts/common/fs/zfs/sys/zfs_znode.h	Thu Apr 06 19:27:51 2006 -0700
+++ b/usr/src/uts/common/fs/zfs/sys/zfs_znode.h	Thu Apr 06 20:12:27 2006 -0700
@@ -58,7 +58,7 @@
 #define	ZFS_FSID		"FSID"
 #define	ZFS_DELETE_QUEUE	"DELETE_QUEUE"
 #define	ZFS_ROOT_OBJ		"ROOT"
-#define	ZFS_VERSION_OBJ		"VERSION"
+#define	ZPL_VERSION_OBJ		"VERSION"
 #define	ZFS_PROP_BLOCKPERPAGE	"BLOCKPERPAGE"
 #define	ZFS_PROP_NOGROWBLOCKS	"NOGROWBLOCKS"
 
@@ -66,11 +66,11 @@
 #define	ZFS_FLAG_NOGROWBLOCKS	0x2
 
 /*
- * ZFS version - rev'd whenever an incompatible on-disk format change
+ * ZPL version - rev'd whenever an incompatible on-disk format change
  * occurs.  Independent of SPA/DMU/ZAP versioning.
  */
 
-#define	ZFS_VERSION		1ULL
+#define	ZPL_VERSION		1ULL
 
 #define	ZFS_MAX_BLOCKSIZE	(SPA_MAXBLOCKSIZE)
 
--- a/usr/src/uts/common/fs/zfs/uberblock.c	Thu Apr 06 19:27:51 2006 -0700
+++ b/usr/src/uts/common/fs/zfs/uberblock.c	Thu Apr 06 20:12:27 2006 -0700
@@ -50,8 +50,11 @@
 {
 	ASSERT(ub->ub_txg < txg);
 
+	/*
+	 * We explicitly do not set ub_version here, so that older versions
+	 * continue to be written with the previous uberblock version.
+	 */
 	ub->ub_magic = UBERBLOCK_MAGIC;
-	ub->ub_version = UBERBLOCK_VERSION;
 	ub->ub_txg = txg;
 	ub->ub_guid_sum = rvd->vdev_guid_sum;
 	ub->ub_timestamp = gethrestime_sec();
--- a/usr/src/uts/common/fs/zfs/zfs_ioctl.c	Thu Apr 06 19:27:51 2006 -0700
+++ b/usr/src/uts/common/fs/zfs/zfs_ioctl.c	Thu Apr 06 20:12:27 2006 -0700
@@ -424,20 +424,6 @@
 }
 
 static int
-zfs_ioc_pool_guid(zfs_cmd_t *zc)
-{
-	spa_t *spa;
-	int error;
-
-	error = spa_open(zc->zc_name, &spa, FTAG);
-	if (error == 0) {
-		zc->zc_guid = spa_guid(spa);
-		spa_close(spa, FTAG);
-	}
-	return (error);
-}
-
-static int
 zfs_ioc_pool_stats(zfs_cmd_t *zc)
 {
 	nvlist_t *config;
@@ -545,6 +531,20 @@
 }
 
 static int
+zfs_ioc_pool_upgrade(zfs_cmd_t *zc)
+{
+	spa_t *spa;
+	int error;
+
+	error = spa_open(zc->zc_name, &spa, FTAG);
+	if (error == 0) {
+		spa_upgrade(spa);
+		spa_close(spa, FTAG);
+	}
+	return (error);
+}
+
+static int
 zfs_ioc_vdev_add(zfs_cmd_t *zc)
 {
 	spa_t *spa;
@@ -1182,11 +1182,11 @@
 	{ zfs_ioc_pool_import,		zfs_secpolicy_config,	pool_name },
 	{ zfs_ioc_pool_export,		zfs_secpolicy_config,	pool_name },
 	{ zfs_ioc_pool_configs,		zfs_secpolicy_none,	no_name },
-	{ zfs_ioc_pool_guid,		zfs_secpolicy_read,	pool_name },
 	{ zfs_ioc_pool_stats,		zfs_secpolicy_read,	pool_name },
 	{ zfs_ioc_pool_tryimport,	zfs_secpolicy_config,	no_name },
 	{ zfs_ioc_pool_scrub,		zfs_secpolicy_config,	pool_name },
 	{ zfs_ioc_pool_freeze,		zfs_secpolicy_config,	no_name },
+	{ zfs_ioc_pool_upgrade,		zfs_secpolicy_config,	pool_name },
 	{ zfs_ioc_vdev_add,		zfs_secpolicy_config,	pool_name },
 	{ zfs_ioc_vdev_remove,		zfs_secpolicy_config,	pool_name },
 	{ zfs_ioc_vdev_online,		zfs_secpolicy_config,	pool_name },
--- a/usr/src/uts/common/fs/zfs/zfs_znode.c	Thu Apr 06 19:27:51 2006 -0700
+++ b/usr/src/uts/common/fs/zfs/zfs_znode.c	Thu Apr 06 20:12:27 2006 -0700
@@ -235,7 +235,7 @@
 
 	objset_t	*os = zfsvfs->z_os;
 	uint64_t	zoid;
-	uint64_t	version = ZFS_VERSION;
+	uint64_t	version = ZPL_VERSION;
 	int		i, error;
 	dmu_object_info_t doi;
 	dmu_objset_stats_t *stats;
@@ -258,15 +258,15 @@
 		dmu_tx_commit(tx);
 	}
 
-	error = zap_lookup(os, MASTER_NODE_OBJ, ZFS_VERSION_OBJ, 8, 1,
+	error = zap_lookup(os, MASTER_NODE_OBJ, ZPL_VERSION_OBJ, 8, 1,
 	    &version);
 	if (error) {
 		return (error);
-	} else if (version != ZFS_VERSION) {
+	} else if (version != ZPL_VERSION) {
 		(void) printf("Mismatched versions:  File system "
 		    "is version %lld on-disk format, which is "
 		    "incompatible with this software version %lld!",
-		    (u_longlong_t)version, ZFS_VERSION);
+		    (u_longlong_t)version, ZPL_VERSION);
 		return (ENOTSUP);
 	}
 
@@ -942,7 +942,7 @@
 {
 	zfsvfs_t	zfsvfs;
 	uint64_t	moid, doid, roid = 0;
-	uint64_t	version = ZFS_VERSION;
+	uint64_t	version = ZPL_VERSION;
 	int		error;
 	znode_t		*rootzp = NULL;
 	vnode_t		*vp;
@@ -964,7 +964,7 @@
 	 * Set starting attributes.
 	 */
 
-	error = zap_update(os, moid, ZFS_VERSION_OBJ, 8, 1, &version, tx);
+	error = zap_update(os, moid, ZPL_VERSION_OBJ, 8, 1, &version, tx);
 	ASSERT(error == 0);
 
 	/*
--- a/usr/src/uts/common/sys/fs/zfs.h	Thu Apr 06 19:27:51 2006 -0700
+++ b/usr/src/uts/common/sys/fs/zfs.h	Thu Apr 06 20:12:27 2006 -0700
@@ -107,6 +107,11 @@
 uint64_t zfs_prop_default_numeric(zfs_prop_t);
 
 /*
+ * On-disk format version.
+ */
+#define	ZFS_VERSION			1ULL
+
+/*
  * The following are configuration names used in the nvlist describing a pool's
  * configuration.
  */
@@ -183,7 +188,9 @@
 	VDEV_AUX_NO_REPLICAS,	/* insufficient number of replicas	*/
 	VDEV_AUX_BAD_GUID_SUM,	/* vdev guid sum doesn't match		*/
 	VDEV_AUX_TOO_SMALL,	/* vdev size is too small		*/
-	VDEV_AUX_BAD_LABEL	/* the label is OK but invalid		*/
+	VDEV_AUX_BAD_LABEL,	/* the label is OK but invalid		*/
+	VDEV_AUX_VERSION_NEWER,	/* on-disk version is too new		*/
+	VDEV_AUX_VERSION_OLDER	/* on-disk version is too old		*/
 } vdev_aux_t;
 
 /*
@@ -279,11 +286,11 @@
 	ZFS_IOC_POOL_IMPORT,
 	ZFS_IOC_POOL_EXPORT,
 	ZFS_IOC_POOL_CONFIGS,
-	ZFS_IOC_POOL_GUID,
 	ZFS_IOC_POOL_STATS,
 	ZFS_IOC_POOL_TRYIMPORT,
 	ZFS_IOC_POOL_SCRUB,
 	ZFS_IOC_POOL_FREEZE,
+	ZFS_IOC_POOL_UPGRADE,
 	ZFS_IOC_VDEV_ADD,
 	ZFS_IOC_VDEV_REMOVE,
 	ZFS_IOC_VDEV_ONLINE,