diff options
author | eschrock <none@none> | 2006-04-06 20:12:27 -0700 |
---|---|---|
committer | eschrock <none@none> | 2006-04-06 20:12:27 -0700 |
commit | eaca9bbd5f5d1e4e554da4c7108e8a03c8c33481 (patch) | |
tree | ef2b3aaeb726ac88394c9747efa5d31fd70b9509 | |
parent | 2b490d7cbf676bb735e581d0d9a4d058cca612b7 (diff) | |
download | illumos-gate-eaca9bbd5f5d1e4e554da4c7108e8a03c8c33481.tar.gz |
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
-rw-r--r-- | usr/src/cmd/fs.d/zfs/fstyp/fstyp.c | 12 | ||||
-rw-r--r-- | usr/src/cmd/truss/codes.c | 4 | ||||
-rw-r--r-- | usr/src/cmd/zpool/zpool_main.c | 296 | ||||
-rw-r--r-- | usr/src/lib/libzfs/common/libzfs.h | 4 | ||||
-rw-r--r-- | usr/src/lib/libzfs/common/libzfs_import.c | 39 | ||||
-rw-r--r-- | usr/src/lib/libzfs/common/libzfs_pool.c | 25 | ||||
-rw-r--r-- | usr/src/lib/libzfs/common/libzfs_status.c | 17 | ||||
-rw-r--r-- | usr/src/lib/libzfs/spec/libzfs.spec | 4 | ||||
-rw-r--r-- | usr/src/lib/libzfs_jni/common/libzfs_jni_pool.c | 6 | ||||
-rw-r--r-- | usr/src/uts/common/fs/zfs/spa.c | 25 | ||||
-rw-r--r-- | usr/src/uts/common/fs/zfs/spa_config.c | 2 | ||||
-rw-r--r-- | usr/src/uts/common/fs/zfs/sys/spa.h | 1 | ||||
-rw-r--r-- | usr/src/uts/common/fs/zfs/sys/uberblock_impl.h | 3 | ||||
-rw-r--r-- | usr/src/uts/common/fs/zfs/sys/zfs_znode.h | 6 | ||||
-rw-r--r-- | usr/src/uts/common/fs/zfs/uberblock.c | 5 | ||||
-rw-r--r-- | usr/src/uts/common/fs/zfs/zfs_ioctl.c | 30 | ||||
-rw-r--r-- | usr/src/uts/common/fs/zfs/zfs_znode.c | 12 | ||||
-rw-r--r-- | usr/src/uts/common/sys/fs/zfs.h | 11 |
18 files changed, 419 insertions, 83 deletions
diff --git a/usr/src/cmd/fs.d/zfs/fstyp/fstyp.c b/usr/src/cmd/fs.d/zfs/fstyp/fstyp.c index 90de2fe5a6..26376e36a6 100644 --- a/usr/src/cmd/fs.d/zfs/fstyp/fstyp.c +++ b/usr/src/cmd/fs.d/zfs/fstyp/fstyp.c @@ -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 @@ main(int argc, char **argv) int c, fd; int verbose = 0; nvlist_t *config; + uint64_t state; (void) setlocale(LC_ALL, ""); @@ -145,6 +145,10 @@ main(int argc, char **argv) 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) diff --git a/usr/src/cmd/truss/codes.c b/usr/src/cmd/truss/codes.c index 3fdbace8b7..4e808b8e48 100644 --- a/usr/src/cmd/truss/codes.c +++ b/usr/src/cmd/truss/codes.c @@ -869,8 +869,6 @@ const struct ioc { "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 @@ const struct ioc { "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", diff --git a/usr/src/cmd/zpool/zpool_main.c b/usr/src/cmd/zpool/zpool_main.c index 2b17abc2b7..2cbecad212 100644 --- a/usr/src/cmd/zpool/zpool_main.c +++ b/usr/src/cmd/zpool/zpool_main.c @@ -69,6 +69,8 @@ static int zpool_do_scrub(int, char **); 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 @@ typedef enum { HELP_ONLINE, HELP_REPLACE, HELP_SCRUB, - HELP_STATUS + HELP_STATUS, + HELP_UPGRADE } zpool_help_t; @@ -141,6 +144,7 @@ static zpool_command_t command_table[] = { { 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 @@ get_usage(zpool_help_t idx) { 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 @@ print_import_config(const char *name, nvlist_t *nv, int namewidth, int depth) (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 @@ show_import(nvlist_t *config) "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 @@ show_import(nvlist_t *config) * 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 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts, 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 @@ print_status_config(zpool_handle_t *zhp, const char *name, nvlist_t *nv, (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 @@ status_callback(zpool_handle_t *zhp, void *data) "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 @@ zpool_do_status(int argc, char **argv) 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) { diff --git a/usr/src/lib/libzfs/common/libzfs.h b/usr/src/lib/libzfs/common/libzfs.h index 1d8a4b1ccc..0044ccd7c9 100644 --- a/usr/src/lib/libzfs/common/libzfs.h +++ b/usr/src/lib/libzfs/common/libzfs.h @@ -108,13 +108,14 @@ typedef enum { 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 @@ extern nvlist_t *zpool_find_import(int, char **); * 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 diff --git a/usr/src/lib/libzfs/common/libzfs_import.c b/usr/src/lib/libzfs/common/libzfs_import.c index a06c94af6d..98519c3aae 100644 --- a/usr/src/lib/libzfs/common/libzfs_import.c +++ b/usr/src/lib/libzfs/common/libzfs_import.c @@ -293,6 +293,26 @@ add_config(pool_list_t *pl, const char *path, nvlist_t *config) } /* + * 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 @@ get_configs(pool_list_t *pl) 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 @@ zpool_in_use(int fd, pool_state_t *state, char **namestr) 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 @@ zpool_in_use(int fd, pool_state_t *state, char **namestr) 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 diff --git a/usr/src/lib/libzfs/common/libzfs_pool.c b/usr/src/lib/libzfs/common/libzfs_pool.c index 1165497e72..2889725027 100644 --- a/usr/src/lib/libzfs/common/libzfs_pool.c +++ b/usr/src/lib/libzfs/common/libzfs_pool.c @@ -1590,3 +1590,28 @@ zpool_get_errlog(zpool_handle_t *zhp, nvlist_t ***list, size_t *nelem) 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); +} diff --git a/usr/src/lib/libzfs/common/libzfs_status.c b/usr/src/lib/libzfs/common/libzfs_status.c index 314e452076..258b2e2f7d 100644 --- a/usr/src/lib/libzfs/common/libzfs_status.c +++ b/usr/src/lib/libzfs/common/libzfs_status.c @@ -177,13 +177,23 @@ check_status(nvlist_t *config, int isimport) 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 @@ check_status(nvlist_t *config, int isimport) 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); } diff --git a/usr/src/lib/libzfs/spec/libzfs.spec b/usr/src/lib/libzfs/spec/libzfs.spec index fd87619578..1789122711 100644 --- a/usr/src/lib/libzfs/spec/libzfs.spec +++ b/usr/src/lib/libzfs/spec/libzfs.spec @@ -328,6 +328,10 @@ function zpool_remove_zvol_links version SUNWprivate_1.1 end +function zpool_upgrade +version SUNWprivate_1.1 +end + function zpool_vdev_online version SUNWprivate_1.1 end diff --git a/usr/src/lib/libzfs_jni/common/libzfs_jni_pool.c b/usr/src/lib/libzfs_jni/common/libzfs_jni_pool.c index 4a4e76c160..d9d09804ec 100644 --- a/usr/src/lib/libzfs_jni/common/libzfs_jni_pool.c +++ b/usr/src/lib/libzfs_jni/common/libzfs_jni_pool.c @@ -129,8 +129,10 @@ static zjni_field_mapping_t zpool_status_map[] = { { 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" }, diff --git a/usr/src/uts/common/fs/zfs/spa.c b/usr/src/uts/common/fs/zfs/spa.c index 7a581ba102..f4ecf519cd 100644 --- a/usr/src/uts/common/fs/zfs/spa.c +++ b/usr/src/uts/common/fs/zfs/spa.c @@ -326,6 +326,8 @@ spa_load(spa_t *spa, nvlist_t *config, spa_load_state_t state, int mosconfig) * 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 @@ spa_load(spa_t *spa, nvlist_t *config, spa_load_state_t state, int mosconfig) /* * 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_create(const char *pool, nvlist_t *nvroot, const char *altroot) 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 @@ spa_lookup_by_guid(spa_t *spa, uint64_t guid) { 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); +} diff --git a/usr/src/uts/common/fs/zfs/spa_config.c b/usr/src/uts/common/fs/zfs/spa_config.c index acdac7ba62..906f2e5470 100644 --- a/usr/src/uts/common/fs/zfs/spa_config.c +++ b/usr/src/uts/common/fs/zfs/spa_config.c @@ -279,7 +279,7 @@ spa_config_generate(spa_t *spa, vdev_t *vd, uint64_t txg, int getstats) 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, diff --git a/usr/src/uts/common/fs/zfs/sys/spa.h b/usr/src/uts/common/fs/zfs/sys/spa.h index 21e7ad7938..cbe8257953 100644 --- a/usr/src/uts/common/fs/zfs/sys/spa.h +++ b/usr/src/uts/common/fs/zfs/sys/spa.h @@ -397,6 +397,7 @@ extern void spa_strfree(char *); 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); diff --git a/usr/src/uts/common/fs/zfs/sys/uberblock_impl.h b/usr/src/uts/common/fs/zfs/sys/uberblock_impl.h index 86d9877010..ab0f2dcf8c 100644 --- a/usr/src/uts/common/fs/zfs/sys/uberblock_impl.h +++ b/usr/src/uts/common/fs/zfs/sys/uberblock_impl.h @@ -45,12 +45,11 @@ extern "C" { * 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 */ diff --git a/usr/src/uts/common/fs/zfs/sys/zfs_znode.h b/usr/src/uts/common/fs/zfs/sys/zfs_znode.h index 3c05c5ff79..4d069b5209 100644 --- a/usr/src/uts/common/fs/zfs/sys/zfs_znode.h +++ b/usr/src/uts/common/fs/zfs/sys/zfs_znode.h @@ -58,7 +58,7 @@ extern "C" { #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 @@ extern "C" { #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) diff --git a/usr/src/uts/common/fs/zfs/uberblock.c b/usr/src/uts/common/fs/zfs/uberblock.c index b6d3fe9595..34d7e0c3ac 100644 --- a/usr/src/uts/common/fs/zfs/uberblock.c +++ b/usr/src/uts/common/fs/zfs/uberblock.c @@ -50,8 +50,11 @@ uberblock_update(uberblock_t *ub, vdev_t *rvd, uint64_t txg) { 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(); diff --git a/usr/src/uts/common/fs/zfs/zfs_ioctl.c b/usr/src/uts/common/fs/zfs/zfs_ioctl.c index 422b24a993..cd7e79a8be 100644 --- a/usr/src/uts/common/fs/zfs/zfs_ioctl.c +++ b/usr/src/uts/common/fs/zfs/zfs_ioctl.c @@ -424,20 +424,6 @@ zfs_ioc_pool_configs(zfs_cmd_t *zc) } 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 @@ zfs_ioc_pool_freeze(zfs_cmd_t *zc) } 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 @@ static zfs_ioc_vec_t zfs_ioc_vec[] = { { 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 }, diff --git a/usr/src/uts/common/fs/zfs/zfs_znode.c b/usr/src/uts/common/fs/zfs/zfs_znode.c index cff45752c0..74e5c97cc5 100644 --- a/usr/src/uts/common/fs/zfs/zfs_znode.c +++ b/usr/src/uts/common/fs/zfs/zfs_znode.c @@ -235,7 +235,7 @@ zfs_init_fs(zfsvfs_t *zfsvfs, znode_t **zpp, cred_t *cr) 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 @@ zfs_init_fs(zfsvfs_t *zfsvfs, znode_t **zpp, cred_t *cr) 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 @@ zfs_create_fs(objset_t *os, cred_t *cr, dmu_tx_t *tx) { 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 @@ zfs_create_fs(objset_t *os, cred_t *cr, dmu_tx_t *tx) * 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); /* diff --git a/usr/src/uts/common/sys/fs/zfs.h b/usr/src/uts/common/sys/fs/zfs.h index 0fa884dcaa..f1a331051d 100644 --- a/usr/src/uts/common/sys/fs/zfs.h +++ b/usr/src/uts/common/sys/fs/zfs.h @@ -107,6 +107,11 @@ const char *zfs_prop_default_string(zfs_prop_t); 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 @@ typedef enum vdev_aux { 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 @@ typedef enum zfs_ioc { 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, |