From 2e4bc6ee5c9c9e91c8439d4705837ec11b38c9e7 Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Thu, 22 Feb 2018 02:21:03 +0000 Subject: [PATCH 1/2] 9075 Improve ZFS pool import/load process and corrupted pool recovery illumos/illumos-gate@6f7938128a2c5e23f4b970ea101137eadd1470a1 Some work has been done lately to improve the debugability of the ZFS pool load (and import) process. This includes: https://www.illumos.org/issues/7638: Refactor spa_load_impl into several functions https://www.illumos.org/issues/8961: SPA load/import should tell us why it failed https://www.illumos.org/issues/7277: zdb should be able to print zfs_dbgmsg's To iterate on top of that, there's a few changes that were made to make the import process more resilient and crash free. One of the first tasks during the pool load process is to parse a config provided from userland that describes what devices the pool is composed of. A vdev tree is generated from that config, and then all the vdevs are opened. The Meta Object Set (MOS) of the pool is accessed, and several metadata objects that are necessary to load the pool are read. The exact configuration of the pool is also stored inside the MOS. Since the configuration provided from userland is external and might not accurately describe the vdev tree of the pool at the txg that is being loaded, it cannot be relied upon to safely operate the pool. For that reason, the configuration in the MOS is read early on. In the past, the two configurations were compared together and if there was a mismatch then the load process was aborted and an error was returned. The latter was a good way to ensure a pool does not get corrupted, however it made the pool load process needlessly fragile in cases where the vdev configuration changed or the userland configuration was outdated. Since the MOS is stored in 3 copies, the configuration provided by userland doesn't have to be perfect in order to read its contents. Hence, a new approach has been adopted: The pool is first opened with the untrusted userland configuration just so that the real configuration can be read from the MOS. The trusted MOS configuration is then used to generate a new vdev tree and the pool is re-opened. When the pool is opened with an untrusted configuration, writes are disabled to avoid accidentally damaging it. During reads, some sanity checks are performed on block pointers to see if each DVA points to a known vdev; when the configuration is untrusted, instead of panicking the system if those checks fail we simply avoid issuing reads to the invalid DVAs. This new two-step pool load process now allows rewinding pools accross vdev tree changes such as device replacement, addition, etc. Loading a pool from an external config file in a clustering environment also becomes much safer now since the pool will import even if the config is outdated and didn't, for instance, register a recent device addition. With this code in place, it became relatively easy to implement a long-sought-after feature: the ability to import a pool with missing top level (i.e. non-redundant) devices. Note that since this almost guarantees some loss Of data, this feature is for now restricted to a read-only import. Reviewed by: George Wilson Reviewed by: Matthew Ahrens Reviewed by: Andrew Stormont Approved by: Hans Rosenfeld Author: Pavel Zakharov --- cmd/zpool/zpool_main.c | 9 +++++++++ lib/libzfs/common/libzfs.h | 1 + lib/libzfs/common/libzfs_import.c | 19 +++++++++++++++++-- lib/libzfs/common/libzfs_pool.c | 5 +++-- lib/libzpool/common/kernel.c | 10 ++++++++++ lib/libzpool/common/sys/zfs_context.h | 1 + 6 files changed, 41 insertions(+), 4 deletions(-) diff --git a/cmd/zpool/zpool_main.c b/cmd/zpool/zpool_main.c index c9cba20a0d1..1331fbfe46f 100644 --- a/cmd/zpool/zpool_main.c +++ b/cmd/zpool/zpool_main.c @@ -1562,6 +1562,10 @@ print_status_config(zpool_handle_t *zhp, const char *name, nvlist_t *nv, (void) printf(gettext("split into new pool")); break; + case VDEV_AUX_CHILDREN_OFFLINE: + (void) printf(gettext("all children offline")); + break; + default: (void) printf(gettext("corrupted data")); break; @@ -1649,6 +1653,10 @@ print_import_config(const char *name, nvlist_t *nv, int namewidth, int depth) (void) printf(gettext("too many errors")); break; + case VDEV_AUX_CHILDREN_OFFLINE: + (void) printf(gettext("all children offline")); + break; + default: (void) printf(gettext("corrupted data")); break; @@ -2296,6 +2304,7 @@ zpool_do_import(int argc, char **argv) idata.poolname = searchname; idata.guid = searchguid; idata.cachefile = cachefile; + idata.policy = policy; pools = zpool_search_import(g_zfs, &idata); diff --git a/lib/libzfs/common/libzfs.h b/lib/libzfs/common/libzfs.h index b653f19a761..5011f923d26 100644 --- a/lib/libzfs/common/libzfs.h +++ b/lib/libzfs/common/libzfs.h @@ -388,6 +388,7 @@ typedef struct importargs { int can_be_active : 1; /* can the pool be active? */ int unique : 1; /* does 'poolname' already exist? */ int exists : 1; /* set on return if pool already exists */ + nvlist_t *policy; /* rewind policy (rewind txg, etc.) */ } importargs_t; extern nvlist_t *zpool_search_import(libzfs_handle_t *, importargs_t *); diff --git a/lib/libzfs/common/libzfs_import.c b/lib/libzfs/common/libzfs_import.c index 7fbd9faf0b2..8cbbd586962 100644 --- a/lib/libzfs/common/libzfs_import.c +++ b/lib/libzfs/common/libzfs_import.c @@ -412,7 +412,8 @@ vdev_is_hole(uint64_t *hole_array, uint_t holes, uint_t id) * return to the user. */ static nvlist_t * -get_configs(libzfs_handle_t *hdl, pool_list_t *pl, boolean_t active_ok) +get_configs(libzfs_handle_t *hdl, pool_list_t *pl, boolean_t active_ok, + nvlist_t *policy) { pool_entry_t *pe; vdev_entry_t *ve; @@ -746,6 +747,12 @@ get_configs(libzfs_handle_t *hdl, pool_list_t *pl, boolean_t active_ok) continue; } + if (policy != NULL) { + if (nvlist_add_nvlist(config, ZPOOL_REWIND_POLICY, + policy) != 0) + goto nomem; + } + if ((nvl = refresh_config(hdl, config)) == NULL) { nvlist_free(config); config = NULL; @@ -1251,7 +1258,7 @@ zpool_find_import_impl(libzfs_handle_t *hdl, importargs_t *iarg) goto error; } - ret = get_configs(hdl, &pools, iarg->can_be_active); + ret = get_configs(hdl, &pools, iarg->can_be_active, iarg->policy); error: for (pe = pools.pools; pe != NULL; pe = penext) { @@ -1381,6 +1388,14 @@ zpool_find_import_cached(libzfs_handle_t *hdl, const char *cachefile, if (active) continue; + if (nvlist_add_string(src, ZPOOL_CONFIG_CACHEFILE, + cachefile) != 0) { + (void) no_memory(hdl); + nvlist_free(raw); + nvlist_free(pools); + return (NULL); + } + if ((dst = refresh_config(hdl, src)) == NULL) { nvlist_free(raw); nvlist_free(pools); diff --git a/lib/libzfs/common/libzfs_pool.c b/lib/libzfs/common/libzfs_pool.c index 4f636b6b0d8..01f6b0d8acc 100644 --- a/lib/libzfs/common/libzfs_pool.c +++ b/lib/libzfs/common/libzfs_pool.c @@ -1808,8 +1808,9 @@ zpool_import_props(libzfs_handle_t *hdl, nvlist_t *config, const char *newname, nvlist_lookup_nvlist(nvinfo, ZPOOL_CONFIG_MISSING_DEVICES, &missing) == 0) { (void) printf(dgettext(TEXT_DOMAIN, - "The devices below are missing, use " - "'-m' to import the pool anyway:\n")); + "The devices below are missing or " + "corrupted, use '-m' to import the pool " + "anyway:\n")); print_vdev_tree(hdl, NULL, missing, 2); (void) printf("\n"); } diff --git a/lib/libzpool/common/kernel.c b/lib/libzpool/common/kernel.c index 1d94907b1ab..e74f14cc9bd 100644 --- a/lib/libzpool/common/kernel.c +++ b/lib/libzpool/common/kernel.c @@ -461,6 +461,16 @@ kernel_fini(void) system_taskq_fini(); } +/* ARGSUSED */ +uint32_t +zone_get_hostid(void *zonep) +{ + /* + * We're emulating the system's hostid in userland. + */ + return (strtoul(hw_serial, NULL, 10)); +} + int z_uncompress(void *dst, size_t *dstlen, const void *src, size_t srclen) { diff --git a/lib/libzpool/common/sys/zfs_context.h b/lib/libzpool/common/sys/zfs_context.h index d7aa62cdbda..d1dce801960 100644 --- a/lib/libzpool/common/sys/zfs_context.h +++ b/lib/libzpool/common/sys/zfs_context.h @@ -317,6 +317,7 @@ typedef struct callb_cpr { #define zone_dataset_visible(x, y) (1) #define INGLOBALZONE(z) (1) +extern uint32_t zone_get_hostid(void *zonep); extern int zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr); extern int zfs_secpolicy_rename_perms(const char *from, const char *to, From 250699c3045c9c844223183bcd6eb8c524959492 Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Thu, 22 Feb 2018 02:25:09 +0000 Subject: [PATCH 2/2] =?UTF-8?q?r329793=20|=20mav=20|=202018-02-22=2004:21:?= =?UTF-8?q?03=20+0200=20(=D1=87=D1=82,=2022=20=D1=84=D0=B5=D0=B2=D1=80.=20?= =?UTF-8?q?2018)=20|=2058=20lines?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 9075 Improve ZFS pool import/load process and corrupted pool recovery illumos/illumos-gate@6f7938128a2c5e23f4b970ea101137eadd1470a1 Some work has been done lately to improve the debugability of the ZFS pool load (and import) process. This includes: https://www.illumos.org/issues/7638: Refactor spa_load_impl into several functions https://www.illumos.org/issues/8961: SPA load/import should tell us why it failed https://www.illumos.org/issues/7277: zdb should be able to print zfs_dbgmsg's To iterate on top of that, there's a few changes that were made to make the import process more resilient and crash free. One of the first tasks during the pool load process is to parse a config provided from userland that describes what devices the pool is composed of. A vdev tree is generated from that config, and then all the vdevs are opened. The Meta Object Set (MOS) of the pool is accessed, and several metadata objects that are necessary to load the pool are read. The exact configuration of the pool is also stored inside the MOS. Since the configuration provided from userland is external and might not accurately describe the vdev tree of the pool at the txg that is being loaded, it cannot be relied upon to safely operate the pool. For that reason, the configuration in the MOS is read early on. In the past, the two configurations were compared together and if there was a mismatch then the load process was aborted and an error was returned. The latter was a good way to ensure a pool does not get corrupted, however it made the pool load process needlessly fragile in cases where the vdev configuration changed or the userland configuration was outdated. Since the MOS is stored in 3 copies, the configuration provided by userland doesn't have to be perfect in order to read its contents. Hence, a new approach has been adopted: The pool is first opened with the untrusted userland configuration just so that the real configuration can be read from the MOS. The trusted MOS configuration is then used to generate a new vdev tree and the pool is re-opened. When the pool is opened with an untrusted configuration, writes are disabled to avoid accidentally damaging it. During reads, some sanity checks are performed on block pointers to see if each DVA points to a known vdev; when the configuration is untrusted, instead of panicking the system if those checks fail we simply avoid issuing reads to the invalid DVAs. This new two-step pool load process now allows rewinding pools accross vdev tree changes such as device replacement, addition, etc. Loading a pool from an external config file in a clustering environment also becomes much safer now since the pool will import even if the config is outdated and didn't, for instance, register a recent device addition. With this code in place, it became relatively easy to implement a long-sought-after feature: the ability to import a pool with missing top level (i.e. non-redundant) devices. Note that since this almost guarantees some loss Of data, this feature is for now restricted to a read-only import. Reviewed by: George Wilson Reviewed by: Matthew Ahrens Reviewed by: Andrew Stormont Approved by: Hans Rosenfeld Author: Pavel Zakharov --- uts/common/fs/zfs/spa.c | 905 +++++++++++++++++------------- uts/common/fs/zfs/spa_config.c | 12 +- uts/common/fs/zfs/spa_misc.c | 27 +- uts/common/fs/zfs/sys/spa.h | 6 + uts/common/fs/zfs/sys/spa_impl.h | 13 + uts/common/fs/zfs/sys/vdev.h | 9 +- uts/common/fs/zfs/sys/vdev_impl.h | 1 - uts/common/fs/zfs/vdev.c | 491 +++++++++++----- uts/common/fs/zfs/vdev_label.c | 9 +- uts/common/fs/zfs/vdev_mirror.c | 54 +- uts/common/fs/zfs/vdev_root.c | 41 +- uts/common/fs/zfs/zio.c | 49 +- uts/common/sys/fs/zfs.h | 4 +- 13 files changed, 1069 insertions(+), 552 deletions(-) diff --git a/uts/common/fs/zfs/spa.c b/uts/common/fs/zfs/spa.c index 56d316a16d7..40a323f6bb5 100644 --- a/uts/common/fs/zfs/spa.c +++ b/uts/common/fs/zfs/spa.c @@ -151,9 +151,8 @@ const zio_taskq_info_t zio_taskqs[ZIO_TYPES][ZIO_TASKQ_TYPES] = { static void spa_sync_version(void *arg, dmu_tx_t *tx); static void spa_sync_props(void *arg, dmu_tx_t *tx); static boolean_t spa_has_active_shared_spare(spa_t *spa); -static int spa_load_impl(spa_t *spa, uint64_t, nvlist_t *config, - spa_load_state_t state, spa_import_type_t type, boolean_t trust_config, - char **ereport); +static int spa_load_impl(spa_t *spa, spa_import_type_t type, char **ereport, + boolean_t reloading); static void spa_vdev_resilver_done(spa_t *spa); uint_t zio_taskq_batch_pct = 75; /* 1 thread per cpu in pset */ @@ -176,6 +175,54 @@ boolean_t spa_load_verify_dryrun = B_FALSE; */ #define TRYIMPORT_NAME "$import" +/* + * For debugging purposes: print out vdev tree during pool import. + */ +boolean_t spa_load_print_vdev_tree = B_FALSE; + +/* + * A non-zero value for zfs_max_missing_tvds means that we allow importing + * pools with missing top-level vdevs. This is strictly intended for advanced + * pool recovery cases since missing data is almost inevitable. Pools with + * missing devices can only be imported read-only for safety reasons, and their + * fail-mode will be automatically set to "continue". + * + * With 1 missing vdev we should be able to import the pool and mount all + * datasets. User data that was not modified after the missing device has been + * added should be recoverable. This means that snapshots created prior to the + * addition of that device should be completely intact. + * + * With 2 missing vdevs, some datasets may fail to mount since there are + * dataset statistics that are stored as regular metadata. Some data might be + * recoverable if those vdevs were added recently. + * + * With 3 or more missing vdevs, the pool is severely damaged and MOS entries + * may be missing entirely. Chances of data recovery are very low. Note that + * there are also risks of performing an inadvertent rewind as we might be + * missing all the vdevs with the latest uberblocks. + */ +uint64_t zfs_max_missing_tvds = 0; + +/* + * The parameters below are similar to zfs_max_missing_tvds but are only + * intended for a preliminary open of the pool with an untrusted config which + * might be incomplete or out-dated. + * + * We are more tolerant for pools opened from a cachefile since we could have + * an out-dated cachefile where a device removal was not registered. + * We could have set the limit arbitrarily high but in the case where devices + * are really missing we would want to return the proper error codes; we chose + * SPA_DVAS_PER_BP - 1 so that some copies of the MOS would still be available + * and we get a chance to retrieve the trusted config. + */ +uint64_t zfs_max_missing_tvds_cachefile = SPA_DVAS_PER_BP - 1; +/* + * In the case where config was assembled by scanning device paths (/dev/dsks + * by default) we are less tolerant since all the existing devices should have + * been detected and we want spa_load to return the right error codes. + */ +uint64_t zfs_max_missing_tvds_scan = 0; + /* * ========================================================================== * SPA properties routines @@ -1632,6 +1679,27 @@ load_nvlist(spa_t *spa, uint64_t obj, nvlist_t **value) return (error); } +/* + * Concrete top-level vdevs that are not missing and are not logs. At every + * spa_sync we write new uberblocks to at least SPA_SYNC_MIN_VDEVS core tvds. + */ +static uint64_t +spa_healthy_core_tvds(spa_t *spa) +{ + vdev_t *rvd = spa->spa_root_vdev; + uint64_t tvds = 0; + + for (uint64_t i = 0; i < rvd->vdev_children; i++) { + vdev_t *vd = rvd->vdev_child[i]; + if (vd->vdev_islog) + continue; + if (vdev_is_concrete(vd) && !vdev_is_dead(vd)) + tvds++; + } + + return (tvds); +} + /* * Checks to see if the given vdev could not be opened, in which case we post a * sysevent to notify the autoreplace code that the device has been removed. @@ -1639,7 +1707,7 @@ load_nvlist(spa_t *spa, uint64_t obj, nvlist_t **value) static void spa_check_removed(vdev_t *vd) { - for (int c = 0; c < vd->vdev_children; c++) + for (uint64_t c = 0; c < vd->vdev_children; c++) spa_check_removed(vd->vdev_child[c]); if (vd->vdev_ops->vdev_op_leaf && vdev_is_dead(vd) && @@ -1649,38 +1717,14 @@ spa_check_removed(vdev_t *vd) } } -static void -spa_config_valid_zaps(vdev_t *vd, vdev_t *mvd) +static int +spa_check_for_missing_logs(spa_t *spa) { - ASSERT3U(vd->vdev_children, ==, mvd->vdev_children); - - vd->vdev_top_zap = mvd->vdev_top_zap; - vd->vdev_leaf_zap = mvd->vdev_leaf_zap; - - for (uint64_t i = 0; i < vd->vdev_children; i++) { - spa_config_valid_zaps(vd->vdev_child[i], mvd->vdev_child[i]); - } -} - -/* - * Validate the current config against the MOS config - */ -static boolean_t -spa_config_valid(spa_t *spa, nvlist_t *config) -{ - vdev_t *mrvd, *rvd = spa->spa_root_vdev; - nvlist_t *nv; - - VERIFY(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nv) == 0); - - spa_config_enter(spa, SCL_ALL, FTAG, RW_WRITER); - VERIFY(spa_config_parse(spa, &mrvd, nv, NULL, 0, VDEV_ALLOC_LOAD) == 0); - - ASSERT3U(rvd->vdev_children, ==, mrvd->vdev_children); + vdev_t *rvd = spa->spa_root_vdev; /* * If we're doing a normal import, then build up any additional - * diagnostic information about missing devices in this config. + * diagnostic information about missing log devices. * We'll pass this up to the user for further processing. */ if (!(spa->spa_import_flags & ZFS_IMPORT_MISSING_LOG)) { @@ -1691,109 +1735,52 @@ spa_config_valid(spa_t *spa, nvlist_t *config) KM_SLEEP); VERIFY(nvlist_alloc(&nv, NV_UNIQUE_NAME, KM_SLEEP) == 0); - for (int c = 0; c < rvd->vdev_children; c++) { + for (uint64_t c = 0; c < rvd->vdev_children; c++) { vdev_t *tvd = rvd->vdev_child[c]; - vdev_t *mtvd = mrvd->vdev_child[c]; - if (tvd->vdev_ops == &vdev_missing_ops && - mtvd->vdev_ops != &vdev_missing_ops && - mtvd->vdev_islog) - child[idx++] = vdev_config_generate(spa, mtvd, - B_FALSE, 0); + /* + * We consider a device as missing only if it failed + * to open (i.e. offline or faulted is not considered + * as missing). + */ + if (tvd->vdev_islog && + tvd->vdev_state == VDEV_STATE_CANT_OPEN) { + child[idx++] = vdev_config_generate(spa, tvd, + B_FALSE, VDEV_CONFIG_MISSING); + } } - if (idx) { - VERIFY(nvlist_add_nvlist_array(nv, - ZPOOL_CONFIG_CHILDREN, child, idx) == 0); - VERIFY(nvlist_add_nvlist(spa->spa_load_info, - ZPOOL_CONFIG_MISSING_DEVICES, nv) == 0); + if (idx > 0) { + fnvlist_add_nvlist_array(nv, + ZPOOL_CONFIG_CHILDREN, child, idx); + fnvlist_add_nvlist(spa->spa_load_info, + ZPOOL_CONFIG_MISSING_DEVICES, nv); - for (int i = 0; i < idx; i++) + for (uint64_t i = 0; i < idx; i++) nvlist_free(child[i]); } nvlist_free(nv); kmem_free(child, rvd->vdev_children * sizeof (char **)); - } - /* - * Compare the root vdev tree with the information we have - * from the MOS config (mrvd). Check each top-level vdev - * with the corresponding MOS config top-level (mtvd). - */ - for (int c = 0; c < rvd->vdev_children; c++) { - vdev_t *tvd = rvd->vdev_child[c]; - vdev_t *mtvd = mrvd->vdev_child[c]; - - /* - * Resolve any "missing" vdevs in the current configuration. - * Also trust the MOS config about any "indirect" vdevs. - * If we find that the MOS config has more accurate information - * about the top-level vdev then use that vdev instead. - */ - if ((tvd->vdev_ops == &vdev_missing_ops && - mtvd->vdev_ops != &vdev_missing_ops) || - (mtvd->vdev_ops == &vdev_indirect_ops && - tvd->vdev_ops != &vdev_indirect_ops)) { - - /* - * Device specific actions. - */ - if (mtvd->vdev_islog) { - if (!(spa->spa_import_flags & - ZFS_IMPORT_MISSING_LOG)) { - continue; - } - - spa_set_log_state(spa, SPA_LOG_CLEAR); - } else if (mtvd->vdev_ops != &vdev_indirect_ops) { - continue; - } - - /* - * Swap the missing vdev with the data we were - * able to obtain from the MOS config. - */ - vdev_remove_child(rvd, tvd); - vdev_remove_child(mrvd, mtvd); - - vdev_add_child(rvd, mtvd); - vdev_add_child(mrvd, tvd); - - vdev_reopen(rvd); - } else { - if (mtvd->vdev_islog) { - /* - * Load the slog device's state from the MOS - * config since it's possible that the label - * does not contain the most up-to-date - * information. - */ - vdev_load_log_state(tvd, mtvd); - vdev_reopen(tvd); - } - - /* - * Per-vdev ZAP info is stored exclusively in the MOS. - */ - spa_config_valid_zaps(tvd, mtvd); + if (idx > 0) { + spa_load_failed(spa, "some log devices are missing"); + return (SET_ERROR(ENXIO)); } + } else { + for (uint64_t c = 0; c < rvd->vdev_children; c++) { + vdev_t *tvd = rvd->vdev_child[c]; - /* - * Never trust this info from userland; always use what's - * in the MOS. This prevents it from getting out of sync - * with the rest of the info in the MOS. - */ - tvd->vdev_removing = mtvd->vdev_removing; - tvd->vdev_indirect_config = mtvd->vdev_indirect_config; + if (tvd->vdev_islog && + tvd->vdev_state == VDEV_STATE_CANT_OPEN) { + spa_set_log_state(spa, SPA_LOG_CLEAR); + spa_load_note(spa, "some log devices are " + "missing, ZIL is dropped."); + break; + } + } } - vdev_free(mrvd); - spa_config_exit(spa, SCL_ALL, FTAG); - - /* - * Ensure we were able to validate the config. - */ - return (rvd->vdev_guid_sum == spa->spa_uberblock.ub_guid_sum); + return (0); } /* @@ -2175,53 +2162,15 @@ spa_try_repair(spa_t *spa, nvlist_t *config) } static int -spa_load(spa_t *spa, spa_load_state_t state, spa_import_type_t type, - boolean_t trust_config) +spa_load(spa_t *spa, spa_load_state_t state, spa_import_type_t type) { - nvlist_t *config = spa->spa_config; char *ereport = FM_EREPORT_ZFS_POOL; - char *comment; int error; - uint64_t pool_guid; - nvlist_t *nvl; - if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &pool_guid)) - return (SET_ERROR(EINVAL)); + spa->spa_load_state = state; - ASSERT(spa->spa_comment == NULL); - if (nvlist_lookup_string(config, ZPOOL_CONFIG_COMMENT, &comment) == 0) - spa->spa_comment = spa_strdup(comment); - - /* - * Versioning wasn't explicitly added to the label until later, so if - * it's not present treat it as the initial version. - */ - if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION, - &spa->spa_ubsync.ub_version) != 0) - spa->spa_ubsync.ub_version = SPA_VERSION_INITIAL; - - (void) nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_TXG, - &spa->spa_config_txg); - - if ((state == SPA_LOAD_IMPORT || state == SPA_LOAD_TRYIMPORT) && - spa_guid_exists(pool_guid, 0)) { - error = SET_ERROR(EEXIST); - } else { - spa->spa_config_guid = pool_guid; - - if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_SPLIT, - &nvl) == 0) { - VERIFY(nvlist_dup(nvl, &spa->spa_config_splitting, - KM_SLEEP) == 0); - } - - nvlist_free(spa->spa_load_info); - spa->spa_load_info = fnvlist_alloc(); - - gethrestime(&spa->spa_loaded_ts); - error = spa_load_impl(spa, pool_guid, config, state, type, - trust_config, &ereport); - } + gethrestime(&spa->spa_loaded_ts); + error = spa_load_impl(spa, type, &ereport, B_FALSE); /* * Don't count references from objsets that are already closed @@ -2273,13 +2222,80 @@ vdev_count_verify_zaps(vdev_t *vd) } static int -spa_ld_parse_config(spa_t *spa, uint64_t pool_guid, nvlist_t *config, - spa_import_type_t type) +spa_verify_host(spa_t *spa, nvlist_t *mos_config) +{ + uint64_t hostid; + char *hostname; + uint64_t myhostid = 0; + + if (!spa_is_root(spa) && nvlist_lookup_uint64(mos_config, + ZPOOL_CONFIG_HOSTID, &hostid) == 0) { + hostname = fnvlist_lookup_string(mos_config, + ZPOOL_CONFIG_HOSTNAME); + + myhostid = zone_get_hostid(NULL); + + if (hostid != 0 && myhostid != 0 && hostid != myhostid) { + cmn_err(CE_WARN, "pool '%s' could not be " + "loaded as it was last accessed by " + "another system (host: %s hostid: 0x%llx). " + "See: http://illumos.org/msg/ZFS-8000-EY", + spa_name(spa), hostname, (u_longlong_t)hostid); + spa_load_failed(spa, "hostid verification failed: pool " + "last accessed by host: %s (hostid: 0x%llx)", + hostname, (u_longlong_t)hostid); + return (SET_ERROR(EBADF)); + } + } + + return (0); +} + +static int +spa_ld_parse_config(spa_t *spa, spa_import_type_t type) { int error = 0; - nvlist_t *nvtree = NULL; + nvlist_t *nvtree, *nvl, *config = spa->spa_config; int parse; vdev_t *rvd; + uint64_t pool_guid; + char *comment; + + /* + * Versioning wasn't explicitly added to the label until later, so if + * it's not present treat it as the initial version. + */ + if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION, + &spa->spa_ubsync.ub_version) != 0) + spa->spa_ubsync.ub_version = SPA_VERSION_INITIAL; + + if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &pool_guid)) { + spa_load_failed(spa, "invalid config provided: '%s' missing", + ZPOOL_CONFIG_POOL_GUID); + return (SET_ERROR(EINVAL)); + } + + if ((spa->spa_load_state == SPA_LOAD_IMPORT || spa->spa_load_state == + SPA_LOAD_TRYIMPORT) && spa_guid_exists(pool_guid, 0)) { + spa_load_failed(spa, "a pool with guid %llu is already open", + (u_longlong_t)pool_guid); + return (SET_ERROR(EEXIST)); + } + + spa->spa_config_guid = pool_guid; + + nvlist_free(spa->spa_load_info); + spa->spa_load_info = fnvlist_alloc(); + + ASSERT(spa->spa_comment == NULL); + if (nvlist_lookup_string(config, ZPOOL_CONFIG_COMMENT, &comment) == 0) + spa->spa_comment = spa_strdup(comment); + + (void) nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_TXG, + &spa->spa_config_txg); + + if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_SPLIT, &nvl) == 0) + spa->spa_config_splitting = fnvlist_dup(nvl); if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nvtree)) { spa_load_failed(spa, "invalid config provided: '%s' missing", @@ -2287,9 +2303,6 @@ spa_ld_parse_config(spa_t *spa, uint64_t pool_guid, nvlist_t *config, return (SET_ERROR(EINVAL)); } - parse = (type == SPA_IMPORT_EXISTING ? - VDEV_ALLOC_LOAD : VDEV_ALLOC_SPLIT); - /* * Create "The Godfather" zio to hold all async IOs */ @@ -2307,6 +2320,8 @@ spa_ld_parse_config(spa_t *spa, uint64_t pool_guid, nvlist_t *config, * configuration requires knowing the version number. */ spa_config_enter(spa, SCL_ALL, FTAG, RW_WRITER); + parse = (type == SPA_IMPORT_EXISTING ? + VDEV_ALLOC_LOAD : VDEV_ALLOC_SPLIT); error = spa_config_parse(spa, &rvd, nvtree, NULL, 0, parse); spa_config_exit(spa, SCL_ALL, FTAG); @@ -2327,71 +2342,105 @@ spa_ld_parse_config(spa_t *spa, uint64_t pool_guid, nvlist_t *config, return (0); } +/* + * Recursively open all vdevs in the vdev tree. This function is called twice: + * first with the untrusted config, then with the trusted config. + */ static int spa_ld_open_vdevs(spa_t *spa) { int error = 0; + /* + * spa_missing_tvds_allowed defines how many top-level vdevs can be + * missing/unopenable for the root vdev to be still considered openable. + */ + if (spa->spa_trust_config) { + spa->spa_missing_tvds_allowed = zfs_max_missing_tvds; + } else if (spa->spa_config_source == SPA_CONFIG_SRC_CACHEFILE) { + spa->spa_missing_tvds_allowed = zfs_max_missing_tvds_cachefile; + } else if (spa->spa_config_source == SPA_CONFIG_SRC_SCAN) { + spa->spa_missing_tvds_allowed = zfs_max_missing_tvds_scan; + } else { + spa->spa_missing_tvds_allowed = 0; + } + + spa->spa_missing_tvds_allowed = + MAX(zfs_max_missing_tvds, spa->spa_missing_tvds_allowed); + spa_config_enter(spa, SCL_ALL, FTAG, RW_WRITER); error = vdev_open(spa->spa_root_vdev); spa_config_exit(spa, SCL_ALL, FTAG); + + if (spa->spa_missing_tvds != 0) { + spa_load_note(spa, "vdev tree has %lld missing top-level " + "vdevs.", (u_longlong_t)spa->spa_missing_tvds); + if (spa->spa_trust_config && (spa->spa_mode & FWRITE)) { + /* + * Although theoretically we could allow users to open + * incomplete pools in RW mode, we'd need to add a lot + * of extra logic (e.g. adjust pool space to account + * for missing vdevs). + * This limitation also prevents users from accidentally + * opening the pool in RW mode during data recovery and + * damaging it further. + */ + spa_load_note(spa, "pools with missing top-level " + "vdevs can only be opened in read-only mode."); + error = SET_ERROR(ENXIO); + } else { + spa_load_note(spa, "current settings allow for maximum " + "%lld missing top-level vdevs at this stage.", + (u_longlong_t)spa->spa_missing_tvds_allowed); + } + } if (error != 0) { spa_load_failed(spa, "unable to open vdev tree [error=%d]", error); } + if (spa->spa_missing_tvds != 0 || error != 0) + vdev_dbgmsg_print_tree(spa->spa_root_vdev, 2); return (error); } +/* + * We need to validate the vdev labels against the configuration that + * we have in hand. This function is called twice: first with an untrusted + * config, then with a trusted config. The validation is more strict when the + * config is trusted. + */ static int -spa_ld_validate_vdevs(spa_t *spa, spa_import_type_t type, - boolean_t trust_config) +spa_ld_validate_vdevs(spa_t *spa) { int error = 0; vdev_t *rvd = spa->spa_root_vdev; - /* - * We need to validate the vdev labels against the configuration that - * we have in hand, which is dependent on the setting of trust_config. - * If trust_config is true then we're validating the vdev labels based - * on that config. Otherwise, we're validating against the cached - * config (zpool.cache) that was read when we loaded the zfs module, and - * then later we will recursively call spa_load() and validate against - * the vdev config. - * - * If we're assembling a new pool that's been split off from an - * existing pool, the labels haven't yet been updated so we skip - * validation for now. - */ - if (type != SPA_IMPORT_ASSEMBLE) { - spa_config_enter(spa, SCL_ALL, FTAG, RW_WRITER); - error = vdev_validate(rvd, trust_config); - spa_config_exit(spa, SCL_ALL, FTAG); + spa_config_enter(spa, SCL_ALL, FTAG, RW_WRITER); + error = vdev_validate(rvd); + spa_config_exit(spa, SCL_ALL, FTAG); - if (error != 0) { - spa_load_failed(spa, "vdev_validate failed [error=%d]", - error); - return (error); - } + if (error != 0) { + spa_load_failed(spa, "vdev_validate failed [error=%d]", error); + return (error); + } - if (rvd->vdev_state <= VDEV_STATE_CANT_OPEN) { - spa_load_failed(spa, "cannot open vdev tree after " - "invalidating some vdevs"); - return (SET_ERROR(ENXIO)); - } + if (rvd->vdev_state <= VDEV_STATE_CANT_OPEN) { + spa_load_failed(spa, "cannot open vdev tree after invalidating " + "some vdevs"); + vdev_dbgmsg_print_tree(rvd, 2); + return (SET_ERROR(ENXIO)); } return (0); } static int -spa_ld_select_uberblock(spa_t *spa, nvlist_t *config, spa_import_type_t type, - boolean_t trust_config) +spa_ld_select_uberblock(spa_t *spa, spa_import_type_t type) { vdev_t *rvd = spa->spa_root_vdev; nvlist_t *label; uberblock_t *ub = &spa->spa_uberblock; - uint64_t children; /* * Find the best uberblock. @@ -2484,26 +2533,9 @@ spa_ld_select_uberblock(spa_t *spa, nvlist_t *config, spa_import_type_t type, nvlist_free(unsup_feat); } - /* - * If the vdev guid sum doesn't match the uberblock, we have an - * incomplete configuration. We first check to see if the pool - * is aware of the complete config (i.e ZPOOL_CONFIG_VDEV_CHILDREN). - * If it is, defer the vdev_guid_sum check till later so we - * can handle missing vdevs. - */ - if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_VDEV_CHILDREN, - &children) != 0 && trust_config && type != SPA_IMPORT_ASSEMBLE && - rvd->vdev_guid_sum != ub->ub_guid_sum) { - spa_load_failed(spa, "guid sum in config doesn't match guid " - "sum in uberblock (%llu != %llu)", - (u_longlong_t)rvd->vdev_guid_sum, - (u_longlong_t)ub->ub_guid_sum); - return (spa_vdev_err(rvd, VDEV_AUX_BAD_GUID_SUM, ENXIO)); - } - if (type != SPA_IMPORT_ASSEMBLE && spa->spa_config_splitting) { spa_config_enter(spa, SCL_ALL, FTAG, RW_WRITER); - spa_try_repair(spa, config); + spa_try_repair(spa, spa->spa_config); spa_config_exit(spa, SCL_ALL, FTAG); nvlist_free(spa->spa_config_splitting); spa->spa_config_splitting = NULL; @@ -2542,49 +2574,167 @@ spa_ld_open_rootbp(spa_t *spa) } static int -spa_ld_validate_config(spa_t *spa, spa_import_type_t type) +spa_ld_load_trusted_config(spa_t *spa, spa_import_type_t type, + boolean_t reloading) { - vdev_t *rvd = spa->spa_root_vdev; + vdev_t *mrvd, *rvd = spa->spa_root_vdev; + nvlist_t *nv, *mos_config, *policy; + int error = 0, copy_error; + uint64_t healthy_tvds, healthy_tvds_mos; + uint64_t mos_config_txg; if (spa_dir_prop(spa, DMU_POOL_CONFIG, &spa->spa_config_object, B_TRUE) != 0) return (spa_vdev_err(rvd, VDEV_AUX_CORRUPT_DATA, EIO)); /* - * Validate the config, using the MOS config to fill in any - * information which might be missing. If we fail to validate - * the config then declare the pool unfit for use. If we're - * assembling a pool from a split, the log is not transferred - * over. + * If we're assembling a pool from a split, the config provided is + * already trusted so there is nothing to do. */ - if (type != SPA_IMPORT_ASSEMBLE) { - nvlist_t *mos_config; - if (load_nvlist(spa, spa->spa_config_object, &mos_config) - != 0) { - spa_load_failed(spa, "unable to retrieve MOS config"); - return (spa_vdev_err(rvd, VDEV_AUX_CORRUPT_DATA, EIO)); - } + if (type == SPA_IMPORT_ASSEMBLE) + return (0); - if (!spa_config_valid(spa, mos_config)) { + healthy_tvds = spa_healthy_core_tvds(spa); + + if (load_nvlist(spa, spa->spa_config_object, &mos_config) + != 0) { + spa_load_failed(spa, "unable to retrieve MOS config"); + return (spa_vdev_err(rvd, VDEV_AUX_CORRUPT_DATA, EIO)); + } + + /* + * If we are doing an open, pool owner wasn't verified yet, thus do + * the verification here. + */ + if (spa->spa_load_state == SPA_LOAD_OPEN) { + error = spa_verify_host(spa, mos_config); + if (error != 0) { nvlist_free(mos_config); - spa_load_failed(spa, "mismatch between config provided " - "and config stored in MOS"); - return (spa_vdev_err(rvd, VDEV_AUX_BAD_GUID_SUM, - ENXIO)); + return (error); } - nvlist_free(mos_config); + } + nv = fnvlist_lookup_nvlist(mos_config, ZPOOL_CONFIG_VDEV_TREE); + + spa_config_enter(spa, SCL_ALL, FTAG, RW_WRITER); + + /* + * Build a new vdev tree from the trusted config + */ + VERIFY(spa_config_parse(spa, &mrvd, nv, NULL, 0, VDEV_ALLOC_LOAD) == 0); + + /* + * Vdev paths in the MOS may be obsolete. If the untrusted config was + * obtained by scanning /dev/dsk, then it will have the right vdev + * paths. We update the trusted MOS config with this information. + * We first try to copy the paths with vdev_copy_path_strict, which + * succeeds only when both configs have exactly the same vdev tree. + * If that fails, we fall back to a more flexible method that has a + * best effort policy. + */ + copy_error = vdev_copy_path_strict(rvd, mrvd); + if (copy_error != 0 || spa_load_print_vdev_tree) { + spa_load_note(spa, "provided vdev tree:"); + vdev_dbgmsg_print_tree(rvd, 2); + spa_load_note(spa, "MOS vdev tree:"); + vdev_dbgmsg_print_tree(mrvd, 2); + } + if (copy_error != 0) { + spa_load_note(spa, "vdev_copy_path_strict failed, falling " + "back to vdev_copy_path_relaxed"); + vdev_copy_path_relaxed(rvd, mrvd); + } + + vdev_close(rvd); + vdev_free(rvd); + spa->spa_root_vdev = mrvd; + rvd = mrvd; + spa_config_exit(spa, SCL_ALL, FTAG); + + /* + * We will use spa_config if we decide to reload the spa or if spa_load + * fails and we rewind. We must thus regenerate the config using the + * MOS information with the updated paths. Rewind policy is an import + * setting and is not in the MOS. We copy it over to our new, trusted + * config. + */ + mos_config_txg = fnvlist_lookup_uint64(mos_config, + ZPOOL_CONFIG_POOL_TXG); + nvlist_free(mos_config); + mos_config = spa_config_generate(spa, NULL, mos_config_txg, B_FALSE); + if (nvlist_lookup_nvlist(spa->spa_config, ZPOOL_REWIND_POLICY, + &policy) == 0) + fnvlist_add_nvlist(mos_config, ZPOOL_REWIND_POLICY, policy); + spa_config_set(spa, mos_config); + spa->spa_config_source = SPA_CONFIG_SRC_MOS; + + /* + * Now that we got the config from the MOS, we should be more strict + * in checking blkptrs and can make assumptions about the consistency + * of the vdev tree. spa_trust_config must be set to true before opening + * vdevs in order for them to be writeable. + */ + spa->spa_trust_config = B_TRUE; + + /* + * Open and validate the new vdev tree + */ + error = spa_ld_open_vdevs(spa); + if (error != 0) + return (error); + + error = spa_ld_validate_vdevs(spa); + if (error != 0) + return (error); + + if (copy_error != 0 || spa_load_print_vdev_tree) { + spa_load_note(spa, "final vdev tree:"); + vdev_dbgmsg_print_tree(rvd, 2); + } + + if (spa->spa_load_state != SPA_LOAD_TRYIMPORT && + !spa->spa_extreme_rewind && zfs_max_missing_tvds == 0) { /* - * Now that we've validated the config, check the state of the - * root vdev. If it can't be opened, it indicates one or - * more toplevel vdevs are faulted. + * Sanity check to make sure that we are indeed loading the + * latest uberblock. If we missed SPA_SYNC_MIN_VDEVS tvds + * in the config provided and they happened to be the only ones + * to have the latest uberblock, we could involuntarily perform + * an extreme rewind. */ - if (rvd->vdev_state <= VDEV_STATE_CANT_OPEN) { - spa_load_failed(spa, "some top vdevs are unavailable"); - return (SET_ERROR(ENXIO)); + healthy_tvds_mos = spa_healthy_core_tvds(spa); + if (healthy_tvds_mos - healthy_tvds >= + SPA_SYNC_MIN_VDEVS) { + spa_load_note(spa, "config provided misses too many " + "top-level vdevs compared to MOS (%lld vs %lld). ", + (u_longlong_t)healthy_tvds, + (u_longlong_t)healthy_tvds_mos); + spa_load_note(spa, "vdev tree:"); + vdev_dbgmsg_print_tree(rvd, 2); + if (reloading) { + spa_load_failed(spa, "config was already " + "provided from MOS. Aborting."); + return (spa_vdev_err(rvd, + VDEV_AUX_CORRUPT_DATA, EIO)); + } + spa_load_note(spa, "spa must be reloaded using MOS " + "config"); + return (SET_ERROR(EAGAIN)); } } + error = spa_check_for_missing_logs(spa); + if (error != 0) + return (spa_vdev_err(rvd, VDEV_AUX_BAD_GUID_SUM, ENXIO)); + + if (rvd->vdev_guid_sum != spa->spa_uberblock.ub_guid_sum) { + spa_load_failed(spa, "uberblock guid sum doesn't match MOS " + "guid sum (%llu != %llu)", + (u_longlong_t)spa->spa_uberblock.ub_guid_sum, + (u_longlong_t)rvd->vdev_guid_sum); + return (spa_vdev_err(rvd, VDEV_AUX_BAD_GUID_SUM, + ENXIO)); + } + return (0); } @@ -2750,62 +2900,6 @@ spa_ld_load_special_directories(spa_t *spa) return (0); } -static int -spa_ld_prepare_for_reload(spa_t *spa, int orig_mode) -{ - vdev_t *rvd = spa->spa_root_vdev; - - uint64_t hostid; - nvlist_t *policy = NULL; - nvlist_t *mos_config; - - if (load_nvlist(spa, spa->spa_config_object, &mos_config) != 0) { - spa_load_failed(spa, "unable to retrieve MOS config"); - return (spa_vdev_err(rvd, VDEV_AUX_CORRUPT_DATA, EIO)); - } - - if (!spa_is_root(spa) && nvlist_lookup_uint64(mos_config, - ZPOOL_CONFIG_HOSTID, &hostid) == 0) { - char *hostname; - unsigned long myhostid = 0; - - VERIFY(nvlist_lookup_string(mos_config, - ZPOOL_CONFIG_HOSTNAME, &hostname) == 0); - -#ifdef _KERNEL - myhostid = zone_get_hostid(NULL); -#else /* _KERNEL */ - /* - * We're emulating the system's hostid in userland, so - * we can't use zone_get_hostid(). - */ - (void) ddi_strtoul(hw_serial, NULL, 10, &myhostid); -#endif /* _KERNEL */ - if (hostid != 0 && myhostid != 0 && - hostid != myhostid) { - nvlist_free(mos_config); - cmn_err(CE_WARN, "pool '%s' could not be " - "loaded as it was last accessed by " - "another system (host: %s hostid: 0x%lx). " - "See: http://illumos.org/msg/ZFS-8000-EY", - spa_name(spa), hostname, - (unsigned long)hostid); - return (SET_ERROR(EBADF)); - } - } - if (nvlist_lookup_nvlist(spa->spa_config, - ZPOOL_REWIND_POLICY, &policy) == 0) - VERIFY(nvlist_add_nvlist(mos_config, - ZPOOL_REWIND_POLICY, policy) == 0); - - spa_config_set(spa, mos_config); - spa_unload(spa); - spa_deactivate(spa); - spa_activate(spa, orig_mode); - - return (0); -} - static int spa_ld_get_props(spa_t *spa) { @@ -2933,6 +3027,19 @@ spa_ld_get_props(spa_t *spa) spa->spa_autoreplace = (autoreplace != 0); } + /* + * If we are importing a pool with missing top-level vdevs, + * we enforce that the pool doesn't panic or get suspended on + * error since the likelihood of missing data is extremely high. + */ + if (spa->spa_missing_tvds > 0 && + spa->spa_failmode != ZIO_FAILURE_MODE_CONTINUE && + spa->spa_load_state != SPA_LOAD_TRYIMPORT) { + spa_load_note(spa, "forcing failmode to 'continue' " + "as some top level vdevs are missing"); + spa->spa_failmode = ZIO_FAILURE_MODE_CONTINUE; + } + return (0); } @@ -3063,9 +3170,15 @@ spa_ld_verify_logs(spa_t *spa, spa_import_type_t type, char **ereport) if (type != SPA_IMPORT_ASSEMBLE && spa_writeable(spa)) { boolean_t missing = spa_check_logs(spa); if (missing) { - *ereport = FM_EREPORT_ZFS_LOG_REPLAY; - spa_load_failed(spa, "spa_check_logs failed"); - return (spa_vdev_err(rvd, VDEV_AUX_BAD_LOG, ENXIO)); + if (spa->spa_missing_tvds != 0) { + spa_load_note(spa, "spa_check_logs failed " + "so dropping the logs"); + } else { + *ereport = FM_EREPORT_ZFS_LOG_REPLAY; + spa_load_failed(spa, "spa_check_logs failed"); + return (spa_vdev_err(rvd, VDEV_AUX_BAD_LOG, + ENXIO)); + } } } @@ -3121,7 +3234,8 @@ spa_ld_claim_log_blocks(spa_t *spa) } static void -spa_ld_check_for_config_update(spa_t *spa, uint64_t config_cache_txg) +spa_ld_check_for_config_update(spa_t *spa, uint64_t config_cache_txg, + boolean_t reloading) { vdev_t *rvd = spa->spa_root_vdev; int need_update = B_FALSE; @@ -3133,7 +3247,7 @@ spa_ld_check_for_config_update(spa_t *spa, uint64_t config_cache_txg) * If this is a verbatim import, trust the current * in-core spa_config and update the disk labels. */ - if (config_cache_txg != spa->spa_config_txg || + if (reloading || config_cache_txg != spa->spa_config_txg || spa->spa_load_state == SPA_LOAD_IMPORT || spa->spa_load_state == SPA_LOAD_RECOVER || (spa->spa_import_flags & ZFS_IMPORT_VERBATIM)) @@ -3151,6 +3265,24 @@ spa_ld_check_for_config_update(spa_t *spa, uint64_t config_cache_txg) spa_async_request(spa, SPA_ASYNC_CONFIG_UPDATE); } +static void +spa_ld_prepare_for_reload(spa_t *spa) +{ + int mode = spa->spa_mode; + int async_suspended = spa->spa_async_suspended; + + spa_unload(spa); + spa_deactivate(spa); + spa_activate(spa, mode); + + /* + * We save the value of spa_async_suspended as it gets reset to 0 by + * spa_unload(). We want to restore it back to the original value before + * returning as we might be calling spa_async_resume() later. + */ + spa->spa_async_suspended = async_suspended; +} + /* * Load an existing storage pool, using the config provided. This config * describes which vdevs are part of the pool and is later validated against @@ -3158,32 +3290,35 @@ spa_ld_check_for_config_update(spa_t *spa, uint64_t config_cache_txg) * config stored in the MOS. */ static int -spa_load_impl(spa_t *spa, uint64_t pool_guid, nvlist_t *config, - spa_load_state_t state, spa_import_type_t type, boolean_t trust_config, - char **ereport) +spa_load_impl(spa_t *spa, spa_import_type_t type, char **ereport, + boolean_t reloading) { int error = 0; - uint64_t config_cache_txg = spa->spa_config_txg; - int orig_mode = spa->spa_mode; boolean_t missing_feat_write = B_FALSE; ASSERT(MUTEX_HELD(&spa_namespace_lock)); - - spa->spa_load_state = state; - spa_load_note(spa, "LOADING"); + ASSERT(spa->spa_config_source != SPA_CONFIG_SRC_NONE); /* - * If this is an untrusted config, first access the pool in read-only - * mode. We will then retrieve a trusted copy of the config from the MOS - * and use it to reopen the pool in read-write mode. + * Never trust the config that is provided unless we are assembling + * a pool following a split. + * This means don't trust blkptrs and the vdev tree in general. This + * also effectively puts the spa in read-only mode since + * spa_writeable() checks for spa_trust_config to be true. + * We will later load a trusted config from the MOS. */ - if (!trust_config) - spa->spa_mode = FREAD; + if (type != SPA_IMPORT_ASSEMBLE) + spa->spa_trust_config = B_FALSE; + + if (reloading) + spa_load_note(spa, "RELOADING"); + else + spa_load_note(spa, "LOADING"); /* * Parse the config provided to create a vdev tree. */ - error = spa_ld_parse_config(spa, pool_guid, config, type); + error = spa_ld_parse_config(spa, type); if (error != 0) return (error); @@ -3201,10 +3336,15 @@ spa_load_impl(spa_t *spa, uint64_t pool_guid, nvlist_t *config, /* * Read the label of each vdev and make sure that the GUIDs stored * there match the GUIDs in the config provided. + * If we're assembling a new pool that's been split off from an + * existing pool, the labels haven't yet been updated so we skip + * validation for now. */ - error = spa_ld_validate_vdevs(spa, type, trust_config); - if (error != 0) - return (error); + if (type != SPA_IMPORT_ASSEMBLE) { + error = spa_ld_validate_vdevs(spa); + if (error != 0) + return (error); + } /* * Read vdev labels to find the best uberblock (i.e. latest, unless @@ -3213,7 +3353,7 @@ spa_load_impl(spa_t *spa, uint64_t pool_guid, nvlist_t *config, * label with the best uberblock and verify that our version of zfs * supports them all. */ - error = spa_ld_select_uberblock(spa, config, type, trust_config); + error = spa_ld_select_uberblock(spa, type); if (error != 0) return (error); @@ -3227,13 +3367,21 @@ spa_load_impl(spa_t *spa, uint64_t pool_guid, nvlist_t *config, return (error); /* - * Retrieve the config stored in the MOS and use it to validate the - * config provided. Also extract some information from the MOS config - * to update our vdev tree. + * Retrieve the trusted config stored in the MOS and use it to create + * a new, exact version of the vdev tree, then reopen all vdevs. */ - error = spa_ld_validate_config(spa, type); - if (error != 0) + error = spa_ld_load_trusted_config(spa, type, reloading); + if (error == EAGAIN) { + VERIFY(!reloading); + /* + * Redo the loading process with the trusted config if it is + * too different from the untrusted config. + */ + spa_ld_prepare_for_reload(spa); + return (spa_load_impl(spa, type, ereport, B_TRUE)); + } else if (error != 0) { return (error); + } /* * Retrieve the mapping of indirect vdevs. Those vdevs were removed @@ -3263,19 +3411,6 @@ spa_load_impl(spa_t *spa, uint64_t pool_guid, nvlist_t *config, if (error != 0) return (error); - /* - * If the config provided is not trusted, discard it and use the config - * from the MOS to reload the pool. - */ - if (!trust_config) { - error = spa_ld_prepare_for_reload(spa, orig_mode); - if (error != 0) - return (error); - - spa_load_note(spa, "RELOADING"); - return (spa_load(spa, state, SPA_IMPORT_EXISTING, B_TRUE)); - } - /* * Retrieve pool properties from the MOS. */ @@ -3312,7 +3447,7 @@ spa_load_impl(spa_t *spa, uint64_t pool_guid, nvlist_t *config, return (error); if (missing_feat_write) { - ASSERT(state == SPA_LOAD_TRYIMPORT); + ASSERT(spa->spa_load_state == SPA_LOAD_TRYIMPORT); /* * At this point, we know that we can open the pool in @@ -3344,9 +3479,11 @@ spa_load_impl(spa_t *spa, uint64_t pool_guid, nvlist_t *config, * pool. If we are importing the pool in read-write mode, a few * additional steps must be performed to finish the import. */ - if (spa_writeable(spa) && (state == SPA_LOAD_RECOVER || + if (spa_writeable(spa) && (spa->spa_load_state == SPA_LOAD_RECOVER || spa->spa_load_max_txg == UINT64_MAX)) { - ASSERT(state != SPA_LOAD_TRYIMPORT); + uint64_t config_cache_txg = spa->spa_config_txg; + + ASSERT(spa->spa_load_state != SPA_LOAD_TRYIMPORT); /* * We must check this before we start the sync thread, because @@ -3385,7 +3522,8 @@ spa_load_impl(spa_t *spa, uint64_t pool_guid, nvlist_t *config, * next sync, we would update the config stored in vdev labels * and the cachefile (by default /etc/zfs/zpool.cache). */ - spa_ld_check_for_config_update(spa, config_cache_txg); + spa_ld_check_for_config_update(spa, config_cache_txg, + reloading); /* * Check all DTLs to see if anything needs resilvering. @@ -3428,7 +3566,7 @@ spa_load_impl(spa_t *spa, uint64_t pool_guid, nvlist_t *config, } static int -spa_load_retry(spa_t *spa, spa_load_state_t state, int trust_config) +spa_load_retry(spa_t *spa, spa_load_state_t state) { int mode = spa->spa_mode; @@ -3443,7 +3581,7 @@ spa_load_retry(spa_t *spa, spa_load_state_t state, int trust_config) spa_load_note(spa, "spa_load_retry: rewind, max txg: %llu", (u_longlong_t)spa->spa_load_max_txg); - return (spa_load(spa, state, SPA_IMPORT_EXISTING, trust_config)); + return (spa_load(spa, state, SPA_IMPORT_EXISTING)); } /* @@ -3454,8 +3592,8 @@ spa_load_retry(spa_t *spa, spa_load_state_t state, int trust_config) * spa_load(). */ static int -spa_load_best(spa_t *spa, spa_load_state_t state, int trust_config, - uint64_t max_request, int rewind_flags) +spa_load_best(spa_t *spa, spa_load_state_t state, uint64_t max_request, + int rewind_flags) { nvlist_t *loadinfo = NULL; nvlist_t *config = NULL; @@ -3472,8 +3610,7 @@ spa_load_best(spa_t *spa, spa_load_state_t state, int trust_config, spa->spa_extreme_rewind = B_TRUE; } - load_error = rewind_error = spa_load(spa, state, SPA_IMPORT_EXISTING, - trust_config); + load_error = rewind_error = spa_load(spa, state, SPA_IMPORT_EXISTING); if (load_error == 0) return (0); @@ -3514,7 +3651,7 @@ spa_load_best(spa_t *spa, spa_load_state_t state, int trust_config, spa->spa_uberblock.ub_txg <= spa->spa_load_max_txg) { if (spa->spa_load_max_txg < safe_rewind_txg) spa->spa_extreme_rewind = B_TRUE; - rewind_error = spa_load_retry(spa, state, trust_config); + rewind_error = spa_load_retry(spa, state); } spa->spa_extreme_rewind = B_FALSE; @@ -3593,9 +3730,10 @@ spa_open_common(const char *pool, spa_t **spapp, void *tag, nvlist_t *nvpolicy, if (state != SPA_LOAD_RECOVER) spa->spa_last_ubsync_txg = spa->spa_load_txg = 0; + spa->spa_config_source = SPA_CONFIG_SRC_CACHEFILE; zfs_dbgmsg("spa_open_common: opening %s", pool); - error = spa_load_best(spa, state, B_FALSE, policy.zrp_txg, + error = spa_load_best(spa, state, policy.zrp_txg, policy.zrp_request); if (error == EBADF) { @@ -4616,18 +4754,16 @@ spa_import(const char *pool, nvlist_t *config, nvlist_t *props, uint64_t flags) if (policy.zrp_request & ZPOOL_DO_REWIND) state = SPA_LOAD_RECOVER; - /* - * Pass off the heavy lifting to spa_load(). Pass TRUE for trust_config - * because the user-supplied config is actually the one to trust when - * doing an import. - */ - if (state != SPA_LOAD_RECOVER) - spa->spa_last_ubsync_txg = spa->spa_load_txg = 0; + spa->spa_config_source = SPA_CONFIG_SRC_TRYIMPORT; - zfs_dbgmsg("spa_import: importing %s%s", pool, - (state == SPA_LOAD_RECOVER) ? " (RECOVERY MODE)" : ""); - error = spa_load_best(spa, state, B_TRUE, policy.zrp_txg, - policy.zrp_request); + if (state != SPA_LOAD_RECOVER) { + spa->spa_last_ubsync_txg = spa->spa_load_txg = 0; + zfs_dbgmsg("spa_import: importing %s", pool); + } else { + zfs_dbgmsg("spa_import: importing %s, max_txg=%lld " + "(RECOVERY MODE)", pool, (longlong_t)policy.zrp_txg); + } + error = spa_load_best(spa, state, policy.zrp_txg, policy.zrp_request); /* * Propagate anything learned while loading the pool and pass it @@ -4745,10 +4881,11 @@ nvlist_t * spa_tryimport(nvlist_t *tryconfig) { nvlist_t *config = NULL; - char *poolname; + char *poolname, *cachefile; spa_t *spa; uint64_t state; int error; + zpool_rewind_policy_t policy; if (nvlist_lookup_string(tryconfig, ZPOOL_CONFIG_POOL_NAME, &poolname)) return (NULL); @@ -4763,14 +4900,30 @@ spa_tryimport(nvlist_t *tryconfig) spa = spa_add(TRYIMPORT_NAME, tryconfig, NULL); spa_activate(spa, FREAD); - zfs_dbgmsg("spa_tryimport: importing %s", poolname); - /* - * Pass off the heavy lifting to spa_load(). - * Pass TRUE for trust_config because the user-supplied config - * is actually the one to trust when doing an import. + * Rewind pool if a max txg was provided. Note that even though we + * retrieve the complete rewind policy, only the rewind txg is relevant + * for tryimport. */ - error = spa_load(spa, SPA_LOAD_TRYIMPORT, SPA_IMPORT_EXISTING, B_TRUE); + zpool_get_rewind_policy(spa->spa_config, &policy); + if (policy.zrp_txg != UINT64_MAX) { + spa->spa_load_max_txg = policy.zrp_txg; + spa->spa_extreme_rewind = B_TRUE; + zfs_dbgmsg("spa_tryimport: importing %s, max_txg=%lld", + poolname, (longlong_t)policy.zrp_txg); + } else { + zfs_dbgmsg("spa_tryimport: importing %s", poolname); + } + + if (nvlist_lookup_string(tryconfig, ZPOOL_CONFIG_CACHEFILE, &cachefile) + == 0) { + zfs_dbgmsg("spa_tryimport: using cachefile '%s'", cachefile); + spa->spa_config_source = SPA_CONFIG_SRC_CACHEFILE; + } else { + spa->spa_config_source = SPA_CONFIG_SRC_SCAN; + } + + error = spa_load(spa, SPA_LOAD_TRYIMPORT, SPA_IMPORT_EXISTING); /* * If 'tryconfig' was at least parsable, return the current config. @@ -5775,8 +5928,10 @@ spa_vdev_split_mirror(spa_t *spa, char *newname, nvlist_t *config, spa_activate(newspa, spa_mode_global); spa_async_suspend(newspa); + newspa->spa_config_source = SPA_CONFIG_SRC_SPLIT; + /* create the new pool from the disks of the original pool */ - error = spa_load(newspa, SPA_LOAD_IMPORT, SPA_IMPORT_ASSEMBLE, B_TRUE); + error = spa_load(newspa, SPA_LOAD_IMPORT, SPA_IMPORT_ASSEMBLE); if (error) goto out; @@ -7079,7 +7234,7 @@ spa_sync(spa_t *spa, uint64_t txg) spa_config_enter(spa, SCL_STATE, FTAG, RW_READER); if (list_is_empty(&spa->spa_config_dirty_list)) { - vdev_t *svd[SPA_DVAS_PER_BP]; + vdev_t *svd[SPA_SYNC_MIN_VDEVS]; int svdcount = 0; int children = rvd->vdev_children; int c0 = spa_get_random(children); @@ -7090,7 +7245,7 @@ spa_sync(spa_t *spa, uint64_t txg) !vdev_is_concrete(vd)) continue; svd[svdcount++] = vd; - if (svdcount == SPA_DVAS_PER_BP) + if (svdcount == SPA_SYNC_MIN_VDEVS) break; } error = vdev_config_sync(svd, svdcount, txg); diff --git a/uts/common/fs/zfs/spa_config.c b/uts/common/fs/zfs/spa_config.c index e118317adb9..1fe675337fb 100644 --- a/uts/common/fs/zfs/spa_config.c +++ b/uts/common/fs/zfs/spa_config.c @@ -339,7 +339,8 @@ void spa_config_set(spa_t *spa, nvlist_t *config) { mutex_enter(&spa->spa_props_lock); - nvlist_free(spa->spa_config); + if (spa->spa_config != NULL && spa->spa_config != config) + nvlist_free(spa->spa_config); spa->spa_config = config; mutex_exit(&spa->spa_props_lock); } @@ -386,15 +387,8 @@ spa_config_generate(spa_t *spa, vdev_t *vd, uint64_t txg, int getstats) spa->spa_comment); } -#ifdef _KERNEL hostid = zone_get_hostid(NULL); -#else /* _KERNEL */ - /* - * We're emulating the system's hostid in userland, so we can't use - * zone_get_hostid(). - */ - (void) ddi_strtoul(hw_serial, NULL, 10, &hostid); -#endif /* _KERNEL */ + if (hostid != 0) { fnvlist_add_uint64(config, ZPOOL_CONFIG_HOSTID, hostid); } diff --git a/uts/common/fs/zfs/spa_misc.c b/uts/common/fs/zfs/spa_misc.c index 33857db7ac8..e79664dd337 100644 --- a/uts/common/fs/zfs/spa_misc.c +++ b/uts/common/fs/zfs/spa_misc.c @@ -365,7 +365,8 @@ spa_load_failed(spa_t *spa, const char *fmt, ...) (void) vsnprintf(buf, sizeof (buf), fmt, adx); va_end(adx); - zfs_dbgmsg("spa_load(%s): FAILED: %s", spa->spa_name, buf); + zfs_dbgmsg("spa_load(%s, config %s): FAILED: %s", spa->spa_name, + spa->spa_trust_config ? "trusted" : "untrusted", buf); } /*PRINTFLIKE2*/ @@ -379,7 +380,8 @@ spa_load_note(spa_t *spa, const char *fmt, ...) (void) vsnprintf(buf, sizeof (buf), fmt, adx); va_end(adx); - zfs_dbgmsg("spa_load(%s): %s", spa->spa_name, buf); + zfs_dbgmsg("spa_load(%s, config %s): %s", spa->spa_name, + spa->spa_trust_config ? "trusted" : "untrusted", buf); } /* @@ -620,6 +622,7 @@ spa_add(const char *name, nvlist_t *config, const char *altroot) spa->spa_load_max_txg = UINT64_MAX; spa->spa_proc = &p0; spa->spa_proc_state = SPA_PROC_NONE; + spa->spa_trust_config = B_TRUE; hdlr.cyh_func = spa_deadman; hdlr.cyh_arg = spa; @@ -2013,7 +2016,7 @@ spa_is_root(spa_t *spa) boolean_t spa_writeable(spa_t *spa) { - return (!!(spa->spa_mode & FWRITE)); + return (!!(spa->spa_mode & FWRITE) && spa->spa_trust_config); } /* @@ -2161,3 +2164,21 @@ spa_get_last_removal_txg(spa_t *spa) return (ret); } + +boolean_t +spa_trust_config(spa_t *spa) +{ + return (spa->spa_trust_config); +} + +uint64_t +spa_missing_tvds_allowed(spa_t *spa) +{ + return (spa->spa_missing_tvds_allowed); +} + +void +spa_set_missing_tvds(spa_t *spa, uint64_t missing) +{ + spa->spa_missing_tvds = missing; +} diff --git a/uts/common/fs/zfs/sys/spa.h b/uts/common/fs/zfs/sys/spa.h index aff251b80e4..d6d48ddfbf5 100644 --- a/uts/common/fs/zfs/sys/spa.h +++ b/uts/common/fs/zfs/sys/spa.h @@ -322,6 +322,7 @@ typedef enum bp_embedded_type { #define SPA_BLKPTRSHIFT 7 /* blkptr_t is 128 bytes */ #define SPA_DVAS_PER_BP 3 /* Number of DVAs in a bp */ +#define SPA_SYNC_MIN_VDEVS 3 /* min vdevs to update during sync */ /* * A block is a hole when it has either 1) never been written to, or @@ -836,11 +837,16 @@ extern boolean_t spa_writeable(spa_t *spa); extern boolean_t spa_has_pending_synctask(spa_t *spa); extern int spa_maxblocksize(spa_t *spa); extern void zfs_blkptr_verify(spa_t *spa, const blkptr_t *bp); +extern boolean_t zfs_dva_valid(spa_t *spa, const dva_t *dva, + const blkptr_t *bp); typedef void (*spa_remap_cb_t)(uint64_t vdev, uint64_t offset, uint64_t size, void *arg); extern boolean_t spa_remap_blkptr(spa_t *spa, blkptr_t *bp, spa_remap_cb_t callback, void *arg); extern uint64_t spa_get_last_removal_txg(spa_t *spa); +extern boolean_t spa_trust_config(spa_t *spa); +extern uint64_t spa_missing_tvds_allowed(spa_t *spa); +extern void spa_set_missing_tvds(spa_t *spa, uint64_t missing); extern int spa_mode(spa_t *spa); extern uint64_t zfs_strtonum(const char *str, char **nptr); diff --git a/uts/common/fs/zfs/sys/spa_impl.h b/uts/common/fs/zfs/sys/spa_impl.h index 955568c05b9..cbcd3e923c4 100644 --- a/uts/common/fs/zfs/sys/spa_impl.h +++ b/uts/common/fs/zfs/sys/spa_impl.h @@ -181,6 +181,15 @@ typedef enum spa_all_vdev_zap_action { AVZ_ACTION_INITIALIZE } spa_avz_action_t; +typedef enum spa_config_source { + SPA_CONFIG_SRC_NONE = 0, + SPA_CONFIG_SRC_SCAN, /* scan of path (default: /dev/dsk) */ + SPA_CONFIG_SRC_CACHEFILE, /* any cachefile */ + SPA_CONFIG_SRC_TRYIMPORT, /* returned from call to tryimport */ + SPA_CONFIG_SRC_SPLIT, /* new pool in a pool split */ + SPA_CONFIG_SRC_MOS /* MOS, but not always from right txg */ +} spa_config_source_t; + struct spa { /* * Fields protected by spa_namespace_lock. @@ -199,6 +208,8 @@ struct spa { uint8_t spa_sync_on; /* sync threads are running */ spa_load_state_t spa_load_state; /* current load operation */ boolean_t spa_indirect_vdevs_loaded; /* mappings loaded? */ + boolean_t spa_trust_config; /* do we trust vdev tree? */ + spa_config_source_t spa_config_source; /* where config comes from? */ uint64_t spa_import_flags; /* import specific flags */ spa_taskqs_t spa_zio_taskq[ZIO_TYPES][ZIO_TASKQ_TYPES]; dsl_pool_t *spa_dsl_pool; @@ -259,6 +270,8 @@ struct spa { int spa_async_suspended; /* async tasks suspended */ kcondvar_t spa_async_cv; /* wait for thread_exit() */ uint16_t spa_async_tasks; /* async task mask */ + uint64_t spa_missing_tvds; /* unopenable tvds on load */ + uint64_t spa_missing_tvds_allowed; /* allow loading spa? */ spa_removing_phys_t spa_removing_phys; spa_vdev_removal_t *spa_vdev_removal; diff --git a/uts/common/fs/zfs/sys/vdev.h b/uts/common/fs/zfs/sys/vdev.h index 1297590eb3c..91d4db62f94 100644 --- a/uts/common/fs/zfs/sys/vdev.h +++ b/uts/common/fs/zfs/sys/vdev.h @@ -48,10 +48,13 @@ typedef enum vdev_dtl_type { extern boolean_t zfs_nocacheflush; extern void vdev_dbgmsg(vdev_t *vd, const char *fmt, ...); +extern void vdev_dbgmsg_print_tree(vdev_t *, int); extern int vdev_open(vdev_t *); extern void vdev_open_children(vdev_t *); extern boolean_t vdev_uses_zvols(vdev_t *); -extern int vdev_validate(vdev_t *, boolean_t); +extern int vdev_validate(vdev_t *); +extern int vdev_copy_path_strict(vdev_t *, vdev_t *); +extern void vdev_copy_path_relaxed(vdev_t *, vdev_t *); extern void vdev_close(vdev_t *); extern int vdev_create(vdev_t *, uint64_t txg, boolean_t isreplace); extern void vdev_reopen(vdev_t *); @@ -99,6 +102,7 @@ extern void vdev_scan_stat_init(vdev_t *vd); extern void vdev_propagate_state(vdev_t *vd); extern void vdev_set_state(vdev_t *vd, boolean_t isopen, vdev_state_t state, vdev_aux_t aux); +extern boolean_t vdev_children_are_offline(vdev_t *vd); extern void vdev_space_update(vdev_t *vd, int64_t alloc_delta, int64_t defer_delta, int64_t space_delta); @@ -140,7 +144,8 @@ typedef enum vdev_config_flag { VDEV_CONFIG_SPARE = 1 << 0, VDEV_CONFIG_L2CACHE = 1 << 1, VDEV_CONFIG_REMOVING = 1 << 2, - VDEV_CONFIG_MOS = 1 << 3 + VDEV_CONFIG_MOS = 1 << 3, + VDEV_CONFIG_MISSING = 1 << 4 } vdev_config_flag_t; extern void vdev_top_config_generate(spa_t *spa, nvlist_t *config); diff --git a/uts/common/fs/zfs/sys/vdev_impl.h b/uts/common/fs/zfs/sys/vdev_impl.h index 10ea47f4955..25cefdfe2da 100644 --- a/uts/common/fs/zfs/sys/vdev_impl.h +++ b/uts/common/fs/zfs/sys/vdev_impl.h @@ -405,7 +405,6 @@ extern void vdev_remove_parent(vdev_t *cvd); /* * vdev sync load and sync */ -extern void vdev_load_log_state(vdev_t *nvd, vdev_t *ovd); extern boolean_t vdev_log_state_valid(vdev_t *vd); extern int vdev_load(vdev_t *vd); extern int vdev_dtl_load(vdev_t *vd); diff --git a/uts/common/fs/zfs/vdev.c b/uts/common/fs/zfs/vdev.c index 656008bf9e2..73306aec85a 100644 --- a/uts/common/fs/zfs/vdev.c +++ b/uts/common/fs/zfs/vdev.c @@ -77,6 +77,8 @@ int zfs_scrub_limit = 10; */ int metaslabs_per_vdev = 200; +boolean_t vdev_validate_skip = B_FALSE; + /*PRINTFLIKE2*/ void vdev_dbgmsg(vdev_t *vd, const char *fmt, ...) @@ -99,6 +101,57 @@ vdev_dbgmsg(vdev_t *vd, const char *fmt, ...) } } +void +vdev_dbgmsg_print_tree(vdev_t *vd, int indent) +{ + char state[20]; + + if (vd->vdev_ishole || vd->vdev_ops == &vdev_missing_ops) { + zfs_dbgmsg("%*svdev %u: %s", indent, "", vd->vdev_id, + vd->vdev_ops->vdev_op_type); + return; + } + + switch (vd->vdev_state) { + case VDEV_STATE_UNKNOWN: + (void) snprintf(state, sizeof (state), "unknown"); + break; + case VDEV_STATE_CLOSED: + (void) snprintf(state, sizeof (state), "closed"); + break; + case VDEV_STATE_OFFLINE: + (void) snprintf(state, sizeof (state), "offline"); + break; + case VDEV_STATE_REMOVED: + (void) snprintf(state, sizeof (state), "removed"); + break; + case VDEV_STATE_CANT_OPEN: + (void) snprintf(state, sizeof (state), "can't open"); + break; + case VDEV_STATE_FAULTED: + (void) snprintf(state, sizeof (state), "faulted"); + break; + case VDEV_STATE_DEGRADED: + (void) snprintf(state, sizeof (state), "degraded"); + break; + case VDEV_STATE_HEALTHY: + (void) snprintf(state, sizeof (state), "healthy"); + break; + default: + (void) snprintf(state, sizeof (state), "", + (uint_t)vd->vdev_state); + } + + zfs_dbgmsg("%*svdev %u: %s%s, guid: %llu, path: %s, %s", indent, + "", vd->vdev_id, vd->vdev_ops->vdev_op_type, + vd->vdev_islog ? " (log)" : "", + (u_longlong_t)vd->vdev_guid, + vd->vdev_path ? vd->vdev_path : "N/A", state); + + for (uint64_t i = 0; i < vd->vdev_children; i++) + vdev_dbgmsg_print_tree(vd->vdev_child[i], indent + 2); +} + /* * Given a vdev type, return the appropriate ops vector. */ @@ -1287,8 +1340,13 @@ vdev_open(vdev_t *vd) vd->vdev_stat.vs_aux != VDEV_AUX_OPEN_FAILED) vd->vdev_removed = B_FALSE; - vdev_set_state(vd, B_TRUE, VDEV_STATE_CANT_OPEN, - vd->vdev_stat.vs_aux); + if (vd->vdev_stat.vs_aux == VDEV_AUX_CHILDREN_OFFLINE) { + vdev_set_state(vd, B_TRUE, VDEV_STATE_OFFLINE, + vd->vdev_stat.vs_aux); + } else { + vdev_set_state(vd, B_TRUE, VDEV_STATE_CANT_OPEN, + vd->vdev_stat.vs_aux); + } return (error); } @@ -1453,29 +1511,29 @@ vdev_open(vdev_t *vd) /* * Called once the vdevs are all opened, this routine validates the label - * contents. This needs to be done before vdev_load() so that we don't + * contents. This needs to be done before vdev_load() so that we don't * inadvertently do repair I/Os to the wrong device. * - * If 'strict' is false ignore the spa guid check. This is necessary because - * if the machine crashed during a re-guid the new guid might have been written - * to all of the vdev labels, but not the cached config. The strict check - * will be performed when the pool is opened again using the mos config. - * * This function will only return failure if one of the vdevs indicates that it * has since been destroyed or exported. This is only possible if * /etc/zfs/zpool.cache was readonly at the time. Otherwise, the vdev state * will be updated but the function will return 0. */ int -vdev_validate(vdev_t *vd, boolean_t strict) +vdev_validate(vdev_t *vd) { spa_t *spa = vd->vdev_spa; nvlist_t *label; - uint64_t guid = 0, top_guid; + uint64_t guid = 0, aux_guid = 0, top_guid; uint64_t state; + nvlist_t *nvl; + uint64_t txg; - for (int c = 0; c < vd->vdev_children; c++) - if (vdev_validate(vd->vdev_child[c], strict) != 0) + if (vdev_validate_skip) + return (0); + + for (uint64_t c = 0; c < vd->vdev_children; c++) + if (vdev_validate(vd->vdev_child[c]) != 0) return (SET_ERROR(EBADF)); /* @@ -1483,115 +1541,276 @@ vdev_validate(vdev_t *vd, boolean_t strict) * any further validation. Otherwise, label I/O will fail and we will * overwrite the previous state. */ - if (vd->vdev_ops->vdev_op_leaf && vdev_readable(vd)) { - uint64_t aux_guid = 0; - nvlist_t *nvl; - uint64_t txg = spa_last_synced_txg(spa) != 0 ? - spa_last_synced_txg(spa) : -1ULL; + if (!vd->vdev_ops->vdev_op_leaf || !vdev_readable(vd)) + return (0); - if ((label = vdev_label_read_config(vd, txg)) == NULL) { - vdev_set_state(vd, B_TRUE, VDEV_STATE_CANT_OPEN, - VDEV_AUX_BAD_LABEL); - vdev_dbgmsg(vd, "vdev_validate: failed reading config"); - return (0); - } + /* + * If we are performing an extreme rewind, we allow for a label that + * was modified at a point after the current txg. + */ + if (spa->spa_extreme_rewind || spa_last_synced_txg(spa) == 0) + txg = UINT64_MAX; + else + txg = spa_last_synced_txg(spa); - /* - * Determine if this vdev has been split off into another - * pool. If so, then refuse to open it. - */ - if (nvlist_lookup_uint64(label, ZPOOL_CONFIG_SPLIT_GUID, - &aux_guid) == 0 && aux_guid == spa_guid(spa)) { - vdev_set_state(vd, B_FALSE, VDEV_STATE_CANT_OPEN, - VDEV_AUX_SPLIT_POOL); - nvlist_free(label); - vdev_dbgmsg(vd, "vdev_validate: vdev split into other " - "pool"); - return (0); - } - - if (strict && (nvlist_lookup_uint64(label, - ZPOOL_CONFIG_POOL_GUID, &guid) != 0 || - guid != spa_guid(spa))) { - vdev_set_state(vd, B_FALSE, VDEV_STATE_CANT_OPEN, - VDEV_AUX_CORRUPT_DATA); - nvlist_free(label); - vdev_dbgmsg(vd, "vdev_validate: vdev label pool_guid " - "doesn't match config (%llu != %llu)", - (u_longlong_t)guid, - (u_longlong_t)spa_guid(spa)); - return (0); - } - - if (nvlist_lookup_nvlist(label, ZPOOL_CONFIG_VDEV_TREE, &nvl) - != 0 || nvlist_lookup_uint64(nvl, ZPOOL_CONFIG_ORIG_GUID, - &aux_guid) != 0) - aux_guid = 0; - - /* - * If this vdev just became a top-level vdev because its - * sibling was detached, it will have adopted the parent's - * vdev guid -- but the label may or may not be on disk yet. - * Fortunately, either version of the label will have the - * same top guid, so if we're a top-level vdev, we can - * safely compare to that instead. - * - * If we split this vdev off instead, then we also check the - * original pool's guid. We don't want to consider the vdev - * corrupt if it is partway through a split operation. - */ - if (nvlist_lookup_uint64(label, ZPOOL_CONFIG_GUID, - &guid) != 0 || - nvlist_lookup_uint64(label, ZPOOL_CONFIG_TOP_GUID, - &top_guid) != 0 || - ((vd->vdev_guid != guid && vd->vdev_guid != aux_guid) && - (vd->vdev_guid != top_guid || vd != vd->vdev_top))) { - vdev_set_state(vd, B_FALSE, VDEV_STATE_CANT_OPEN, - VDEV_AUX_CORRUPT_DATA); - nvlist_free(label); - vdev_dbgmsg(vd, "vdev_validate: config guid doesn't " - "match label guid (%llu != %llu)", - (u_longlong_t)vd->vdev_guid, (u_longlong_t)guid); - return (0); - } - - if (nvlist_lookup_uint64(label, ZPOOL_CONFIG_POOL_STATE, - &state) != 0) { - vdev_set_state(vd, B_FALSE, VDEV_STATE_CANT_OPEN, - VDEV_AUX_CORRUPT_DATA); - nvlist_free(label); - vdev_dbgmsg(vd, "vdev_validate: '%s' missing", - ZPOOL_CONFIG_POOL_STATE); - return (0); - } - - nvlist_free(label); - - /* - * If this is a verbatim import, no need to check the - * state of the pool. - */ - if (!(spa->spa_import_flags & ZFS_IMPORT_VERBATIM) && - spa_load_state(spa) == SPA_LOAD_OPEN && - state != POOL_STATE_ACTIVE) { - vdev_dbgmsg(vd, "vdev_validate: invalid pool state " - "(%llu) for spa %s", (u_longlong_t)state, - spa->spa_name); - return (SET_ERROR(EBADF)); - } - - /* - * If we were able to open and validate a vdev that was - * previously marked permanently unavailable, clear that state - * now. - */ - if (vd->vdev_not_present) - vd->vdev_not_present = 0; + if ((label = vdev_label_read_config(vd, txg)) == NULL) { + vdev_set_state(vd, B_TRUE, VDEV_STATE_CANT_OPEN, + VDEV_AUX_BAD_LABEL); + vdev_dbgmsg(vd, "vdev_validate: failed reading config"); + return (0); } + /* + * Determine if this vdev has been split off into another + * pool. If so, then refuse to open it. + */ + if (nvlist_lookup_uint64(label, ZPOOL_CONFIG_SPLIT_GUID, + &aux_guid) == 0 && aux_guid == spa_guid(spa)) { + vdev_set_state(vd, B_FALSE, VDEV_STATE_CANT_OPEN, + VDEV_AUX_SPLIT_POOL); + nvlist_free(label); + vdev_dbgmsg(vd, "vdev_validate: vdev split into other pool"); + return (0); + } + + if (nvlist_lookup_uint64(label, ZPOOL_CONFIG_POOL_GUID, &guid) != 0) { + vdev_set_state(vd, B_FALSE, VDEV_STATE_CANT_OPEN, + VDEV_AUX_CORRUPT_DATA); + nvlist_free(label); + vdev_dbgmsg(vd, "vdev_validate: '%s' missing from label", + ZPOOL_CONFIG_POOL_GUID); + return (0); + } + + /* + * If config is not trusted then ignore the spa guid check. This is + * necessary because if the machine crashed during a re-guid the new + * guid might have been written to all of the vdev labels, but not the + * cached config. The check will be performed again once we have the + * trusted config from the MOS. + */ + if (spa->spa_trust_config && guid != spa_guid(spa)) { + vdev_set_state(vd, B_FALSE, VDEV_STATE_CANT_OPEN, + VDEV_AUX_CORRUPT_DATA); + nvlist_free(label); + vdev_dbgmsg(vd, "vdev_validate: vdev label pool_guid doesn't " + "match config (%llu != %llu)", (u_longlong_t)guid, + (u_longlong_t)spa_guid(spa)); + return (0); + } + + if (nvlist_lookup_nvlist(label, ZPOOL_CONFIG_VDEV_TREE, &nvl) + != 0 || nvlist_lookup_uint64(nvl, ZPOOL_CONFIG_ORIG_GUID, + &aux_guid) != 0) + aux_guid = 0; + + if (nvlist_lookup_uint64(label, ZPOOL_CONFIG_GUID, &guid) != 0) { + vdev_set_state(vd, B_FALSE, VDEV_STATE_CANT_OPEN, + VDEV_AUX_CORRUPT_DATA); + nvlist_free(label); + vdev_dbgmsg(vd, "vdev_validate: '%s' missing from label", + ZPOOL_CONFIG_GUID); + return (0); + } + + if (nvlist_lookup_uint64(label, ZPOOL_CONFIG_TOP_GUID, &top_guid) + != 0) { + vdev_set_state(vd, B_FALSE, VDEV_STATE_CANT_OPEN, + VDEV_AUX_CORRUPT_DATA); + nvlist_free(label); + vdev_dbgmsg(vd, "vdev_validate: '%s' missing from label", + ZPOOL_CONFIG_TOP_GUID); + return (0); + } + + /* + * If this vdev just became a top-level vdev because its sibling was + * detached, it will have adopted the parent's vdev guid -- but the + * label may or may not be on disk yet. Fortunately, either version + * of the label will have the same top guid, so if we're a top-level + * vdev, we can safely compare to that instead. + * However, if the config comes from a cachefile that failed to update + * after the detach, a top-level vdev will appear as a non top-level + * vdev in the config. Also relax the constraints if we perform an + * extreme rewind. + * + * If we split this vdev off instead, then we also check the + * original pool's guid. We don't want to consider the vdev + * corrupt if it is partway through a split operation. + */ + if (vd->vdev_guid != guid && vd->vdev_guid != aux_guid) { + boolean_t mismatch = B_FALSE; + if (spa->spa_trust_config && !spa->spa_extreme_rewind) { + if (vd != vd->vdev_top || vd->vdev_guid != top_guid) + mismatch = B_TRUE; + } else { + if (vd->vdev_guid != top_guid && + vd->vdev_top->vdev_guid != guid) + mismatch = B_TRUE; + } + + if (mismatch) { + vdev_set_state(vd, B_FALSE, VDEV_STATE_CANT_OPEN, + VDEV_AUX_CORRUPT_DATA); + nvlist_free(label); + vdev_dbgmsg(vd, "vdev_validate: config guid " + "doesn't match label guid"); + vdev_dbgmsg(vd, "CONFIG: guid %llu, top_guid %llu", + (u_longlong_t)vd->vdev_guid, + (u_longlong_t)vd->vdev_top->vdev_guid); + vdev_dbgmsg(vd, "LABEL: guid %llu, top_guid %llu, " + "aux_guid %llu", (u_longlong_t)guid, + (u_longlong_t)top_guid, (u_longlong_t)aux_guid); + return (0); + } + } + + if (nvlist_lookup_uint64(label, ZPOOL_CONFIG_POOL_STATE, + &state) != 0) { + vdev_set_state(vd, B_FALSE, VDEV_STATE_CANT_OPEN, + VDEV_AUX_CORRUPT_DATA); + nvlist_free(label); + vdev_dbgmsg(vd, "vdev_validate: '%s' missing from label", + ZPOOL_CONFIG_POOL_STATE); + return (0); + } + + nvlist_free(label); + + /* + * If this is a verbatim import, no need to check the + * state of the pool. + */ + if (!(spa->spa_import_flags & ZFS_IMPORT_VERBATIM) && + spa_load_state(spa) == SPA_LOAD_OPEN && + state != POOL_STATE_ACTIVE) { + vdev_dbgmsg(vd, "vdev_validate: invalid pool state (%llu) " + "for spa %s", (u_longlong_t)state, spa->spa_name); + return (SET_ERROR(EBADF)); + } + + /* + * If we were able to open and validate a vdev that was + * previously marked permanently unavailable, clear that state + * now. + */ + if (vd->vdev_not_present) + vd->vdev_not_present = 0; + return (0); } +static void +vdev_copy_path_impl(vdev_t *svd, vdev_t *dvd) +{ + if (svd->vdev_path != NULL && dvd->vdev_path != NULL) { + if (strcmp(svd->vdev_path, dvd->vdev_path) != 0) { + zfs_dbgmsg("vdev_copy_path: vdev %llu: path changed " + "from '%s' to '%s'", (u_longlong_t)dvd->vdev_guid, + dvd->vdev_path, svd->vdev_path); + spa_strfree(dvd->vdev_path); + dvd->vdev_path = spa_strdup(svd->vdev_path); + } + } else if (svd->vdev_path != NULL) { + dvd->vdev_path = spa_strdup(svd->vdev_path); + zfs_dbgmsg("vdev_copy_path: vdev %llu: path set to '%s'", + (u_longlong_t)dvd->vdev_guid, dvd->vdev_path); + } +} + +/* + * Recursively copy vdev paths from one vdev to another. Source and destination + * vdev trees must have same geometry otherwise return error. Intended to copy + * paths from userland config into MOS config. + */ +int +vdev_copy_path_strict(vdev_t *svd, vdev_t *dvd) +{ + if ((svd->vdev_ops == &vdev_missing_ops) || + (svd->vdev_ishole && dvd->vdev_ishole) || + (dvd->vdev_ops == &vdev_indirect_ops)) + return (0); + + if (svd->vdev_ops != dvd->vdev_ops) { + vdev_dbgmsg(svd, "vdev_copy_path: vdev type mismatch: %s != %s", + svd->vdev_ops->vdev_op_type, dvd->vdev_ops->vdev_op_type); + return (SET_ERROR(EINVAL)); + } + + if (svd->vdev_guid != dvd->vdev_guid) { + vdev_dbgmsg(svd, "vdev_copy_path: guids mismatch (%llu != " + "%llu)", (u_longlong_t)svd->vdev_guid, + (u_longlong_t)dvd->vdev_guid); + return (SET_ERROR(EINVAL)); + } + + if (svd->vdev_children != dvd->vdev_children) { + vdev_dbgmsg(svd, "vdev_copy_path: children count mismatch: " + "%llu != %llu", (u_longlong_t)svd->vdev_children, + (u_longlong_t)dvd->vdev_children); + return (SET_ERROR(EINVAL)); + } + + for (uint64_t i = 0; i < svd->vdev_children; i++) { + int error = vdev_copy_path_strict(svd->vdev_child[i], + dvd->vdev_child[i]); + if (error != 0) + return (error); + } + + if (svd->vdev_ops->vdev_op_leaf) + vdev_copy_path_impl(svd, dvd); + + return (0); +} + +static void +vdev_copy_path_search(vdev_t *stvd, vdev_t *dvd) +{ + ASSERT(stvd->vdev_top == stvd); + ASSERT3U(stvd->vdev_id, ==, dvd->vdev_top->vdev_id); + + for (uint64_t i = 0; i < dvd->vdev_children; i++) { + vdev_copy_path_search(stvd, dvd->vdev_child[i]); + } + + if (!dvd->vdev_ops->vdev_op_leaf || !vdev_is_concrete(dvd)) + return; + + /* + * The idea here is that while a vdev can shift positions within + * a top vdev (when replacing, attaching mirror, etc.) it cannot + * step outside of it. + */ + vdev_t *vd = vdev_lookup_by_guid(stvd, dvd->vdev_guid); + + if (vd == NULL || vd->vdev_ops != dvd->vdev_ops) + return; + + ASSERT(vd->vdev_ops->vdev_op_leaf); + + vdev_copy_path_impl(vd, dvd); +} + +/* + * Recursively copy vdev paths from one root vdev to another. Source and + * destination vdev trees may differ in geometry. For each destination leaf + * vdev, search a vdev with the same guid and top vdev id in the source. + * Intended to copy paths from userland config into MOS config. + */ +void +vdev_copy_path_relaxed(vdev_t *srvd, vdev_t *drvd) +{ + uint64_t children = MIN(srvd->vdev_children, drvd->vdev_children); + ASSERT(srvd->vdev_ops == &vdev_root_ops); + ASSERT(drvd->vdev_ops == &vdev_root_ops); + + for (uint64_t i = 0; i < children; i++) { + vdev_copy_path_search(srvd->vdev_child[i], + drvd->vdev_child[i]); + } +} + /* * Close a virtual device. */ @@ -1687,7 +1906,7 @@ vdev_reopen(vdev_t *vd) !l2arc_vdev_present(vd)) l2arc_add_vdev(spa, vd); } else { - (void) vdev_validate(vd, B_TRUE); + (void) vdev_validate(vd); } /* @@ -3551,6 +3770,19 @@ vdev_set_state(vdev_t *vd, boolean_t isopen, vdev_state_t state, vdev_aux_t aux) vdev_propagate_state(vd->vdev_parent); } +boolean_t +vdev_children_are_offline(vdev_t *vd) +{ + ASSERT(!vd->vdev_ops->vdev_op_leaf); + + for (uint64_t i = 0; i < vd->vdev_children; i++) { + if (vd->vdev_child[i]->vdev_state != VDEV_STATE_OFFLINE) + return (B_FALSE); + } + + return (B_TRUE); +} + /* * Check the vdev configuration to ensure that it's capable of supporting * a root pool. We do not support partial configuration. @@ -3590,35 +3822,6 @@ vdev_is_concrete(vdev_t *vd) } } -/* - * Load the state from the original vdev tree (ovd) which - * we've retrieved from the MOS config object. If the original - * vdev was offline or faulted then we transfer that state to the - * device in the current vdev tree (nvd). - */ -void -vdev_load_log_state(vdev_t *nvd, vdev_t *ovd) -{ - spa_t *spa = nvd->vdev_spa; - - ASSERT(nvd->vdev_top->vdev_islog); - ASSERT(spa_config_held(spa, SCL_STATE_ALL, RW_WRITER) == SCL_STATE_ALL); - ASSERT3U(nvd->vdev_guid, ==, ovd->vdev_guid); - - for (int c = 0; c < nvd->vdev_children; c++) - vdev_load_log_state(nvd->vdev_child[c], ovd->vdev_child[c]); - - if (nvd->vdev_ops->vdev_op_leaf) { - /* - * Restore the persistent vdev state - */ - nvd->vdev_offline = ovd->vdev_offline; - nvd->vdev_faulted = ovd->vdev_faulted; - nvd->vdev_degraded = ovd->vdev_degraded; - nvd->vdev_removed = ovd->vdev_removed; - } -} - /* * Determine if a log device has valid content. If the vdev was * removed or faulted in the MOS config then we know that diff --git a/uts/common/fs/zfs/vdev_label.c b/uts/common/fs/zfs/vdev_label.c index be3b89ee37d..7ccc6724371 100644 --- a/uts/common/fs/zfs/vdev_label.c +++ b/uts/common/fs/zfs/vdev_label.c @@ -265,7 +265,7 @@ vdev_config_generate(spa_t *spa, vdev_t *vd, boolean_t getstats, fnvlist_add_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK, vd->vdev_wholedisk); - if (vd->vdev_not_present) + if (vd->vdev_not_present && !(flags & VDEV_CONFIG_MISSING)) fnvlist_add_uint64(nv, ZPOOL_CONFIG_NOT_PRESENT, 1); if (vd->vdev_isspare) @@ -1062,6 +1062,11 @@ vdev_uberblock_load(vdev_t *rvd, uberblock_t *ub, nvlist_t **config) "txg %llu", spa->spa_name, (u_longlong_t)ub->ub_txg); *config = vdev_label_read_config(cb.ubl_vd, ub->ub_txg); + if (*config == NULL && spa->spa_extreme_rewind) { + vdev_dbgmsg(cb.ubl_vd, "failed to read label config. " + "Trying again without txg restrictions."); + *config = vdev_label_read_config(cb.ubl_vd, UINT64_MAX); + } if (*config == NULL) { vdev_dbgmsg(cb.ubl_vd, "failed to read label config"); } @@ -1088,7 +1093,7 @@ vdev_uberblock_sync_done(zio_t *zio) static void vdev_uberblock_sync(zio_t *zio, uberblock_t *ub, vdev_t *vd, int flags) { - for (int c = 0; c < vd->vdev_children; c++) + for (uint64_t c = 0; c < vd->vdev_children; c++) vdev_uberblock_sync(zio, ub, vd->vdev_child[c], flags); if (!vd->vdev_ops->vdev_op_leaf) diff --git a/uts/common/fs/zfs/vdev_mirror.c b/uts/common/fs/zfs/vdev_mirror.c index a2a4925942f..9d181a874e2 100644 --- a/uts/common/fs/zfs/vdev_mirror.c +++ b/uts/common/fs/zfs/vdev_mirror.c @@ -84,9 +84,32 @@ vdev_mirror_map_alloc(zio_t *zio) if (vd == NULL) { dva_t *dva = zio->io_bp->blk_dva; spa_t *spa = zio->io_spa; + dva_t dva_copy[SPA_DVAS_PER_BP]; c = BP_GET_NDVAS(zio->io_bp); + /* + * If we do not trust the pool config, some DVAs might be + * invalid or point to vdevs that do not exist. We skip them. + */ + if (!spa_trust_config(spa)) { + ASSERT3U(zio->io_type, ==, ZIO_TYPE_READ); + int j = 0; + for (int i = 0; i < c; i++) { + if (zfs_dva_valid(spa, &dva[i], zio->io_bp)) + dva_copy[j++] = dva[i]; + } + if (j == 0) { + zio->io_vsd = NULL; + zio->io_error = ENXIO; + return (NULL); + } + if (j < c) { + dva = dva_copy; + c = j; + } + } + mm = kmem_zalloc(offsetof(mirror_map_t, mm_child[c]), KM_SLEEP); mm->mm_children = c; mm->mm_resilvering = B_FALSE; @@ -201,7 +224,10 @@ vdev_mirror_open(vdev_t *vd, uint64_t *asize, uint64_t *max_asize, } if (numerrors == vd->vdev_children) { - vd->vdev_stat.vs_aux = VDEV_AUX_NO_REPLICAS; + if (vdev_children_are_offline(vd)) + vd->vdev_stat.vs_aux = VDEV_AUX_CHILDREN_OFFLINE; + else + vd->vdev_stat.vs_aux = VDEV_AUX_NO_REPLICAS; return (lasterror); } @@ -311,6 +337,13 @@ vdev_mirror_io_start(zio_t *zio) mm = vdev_mirror_map_alloc(zio); + if (mm == NULL) { + ASSERT(!spa_trust_config(zio->io_spa)); + ASSERT(zio->io_type == ZIO_TYPE_READ); + zio_execute(zio); + return; + } + if (zio->io_type == ZIO_TYPE_READ) { if ((zio->io_flags & ZIO_FLAG_SCRUB) && !mm->mm_resilvering) { /* @@ -381,6 +414,9 @@ vdev_mirror_io_done(zio_t *zio) int good_copies = 0; int unexpected_errors = 0; + if (mm == NULL) + return; + for (c = 0; c < mm->mm_children; c++) { mc = &mm->mm_child[c]; @@ -486,13 +522,19 @@ vdev_mirror_io_done(zio_t *zio) static void vdev_mirror_state_change(vdev_t *vd, int faulted, int degraded) { - if (faulted == vd->vdev_children) - vdev_set_state(vd, B_FALSE, VDEV_STATE_CANT_OPEN, - VDEV_AUX_NO_REPLICAS); - else if (degraded + faulted != 0) + if (faulted == vd->vdev_children) { + if (vdev_children_are_offline(vd)) { + vdev_set_state(vd, B_FALSE, VDEV_STATE_OFFLINE, + VDEV_AUX_CHILDREN_OFFLINE); + } else { + vdev_set_state(vd, B_FALSE, VDEV_STATE_CANT_OPEN, + VDEV_AUX_NO_REPLICAS); + } + } else if (degraded + faulted != 0) { vdev_set_state(vd, B_FALSE, VDEV_STATE_DEGRADED, VDEV_AUX_NONE); - else + } else { vdev_set_state(vd, B_FALSE, VDEV_STATE_HEALTHY, VDEV_AUX_NONE); + } } vdev_ops_t vdev_mirror_ops = { diff --git a/uts/common/fs/zfs/vdev_root.c b/uts/common/fs/zfs/vdev_root.c index 6971b942f0c..b3433c24244 100644 --- a/uts/common/fs/zfs/vdev_root.c +++ b/uts/common/fs/zfs/vdev_root.c @@ -37,6 +37,23 @@ * Virtual device vector for the pool's root vdev. */ +static uint64_t +vdev_root_core_tvds(vdev_t *vd) +{ + uint64_t tvds = 0; + + for (uint64_t c = 0; c < vd->vdev_children; c++) { + vdev_t *cvd = vd->vdev_child[c]; + + if (!cvd->vdev_ishole && !cvd->vdev_islog && + cvd->vdev_ops != &vdev_indirect_ops) { + tvds++; + } + } + + return (tvds); +} + /* * We should be able to tolerate one failure with absolutely no damage * to our metadata. Two failures will take out space maps, a bunch of @@ -46,17 +63,28 @@ * probably fine. Adding bean counters during alloc/free can make this * future guesswork more accurate. */ -static int -too_many_errors(vdev_t *vd, int numerrors) +static boolean_t +too_many_errors(vdev_t *vd, uint64_t numerrors) { - ASSERT3U(numerrors, <=, vd->vdev_children); - return (numerrors > 0); + uint64_t tvds; + + if (numerrors == 0) + return (B_FALSE); + + tvds = vdev_root_core_tvds(vd); + ASSERT3U(numerrors, <=, tvds); + + if (numerrors == tvds) + return (B_TRUE); + + return (numerrors > spa_missing_tvds_allowed(vd->vdev_spa)); } static int vdev_root_open(vdev_t *vd, uint64_t *asize, uint64_t *max_asize, uint64_t *ashift) { + spa_t *spa = vd->vdev_spa; int lasterror = 0; int numerrors = 0; @@ -76,6 +104,9 @@ vdev_root_open(vdev_t *vd, uint64_t *asize, uint64_t *max_asize, } } + if (spa_load_state(spa) != SPA_LOAD_NONE) + spa_set_missing_tvds(spa, numerrors); + if (too_many_errors(vd, numerrors)) { vd->vdev_stat.vs_aux = VDEV_AUX_NO_REPLICAS; return (lasterror); @@ -101,7 +132,7 @@ vdev_root_state_change(vdev_t *vd, int faulted, int degraded) if (too_many_errors(vd, faulted)) { vdev_set_state(vd, B_FALSE, VDEV_STATE_CANT_OPEN, VDEV_AUX_NO_REPLICAS); - } else if (degraded) { + } else if (degraded || faulted) { vdev_set_state(vd, B_FALSE, VDEV_STATE_DEGRADED, VDEV_AUX_NONE); } else { vdev_set_state(vd, B_FALSE, VDEV_STATE_HEALTHY, VDEV_AUX_NONE); diff --git a/uts/common/fs/zfs/zio.c b/uts/common/fs/zfs/zio.c index f763747e060..1e5e3f69eaa 100644 --- a/uts/common/fs/zfs/zio.c +++ b/uts/common/fs/zfs/zio.c @@ -692,6 +692,13 @@ zfs_blkptr_verify(spa_t *spa, const blkptr_t *bp) } } + /* + * Do not verify individual DVAs if the config is not trusted. This + * will be done once the zio is executed in vdev_mirror_map_alloc. + */ + if (!spa->spa_trust_config) + return; + /* * Pool-specific checks. * @@ -741,6 +748,36 @@ zfs_blkptr_verify(spa_t *spa, const blkptr_t *bp) } } +boolean_t +zfs_dva_valid(spa_t *spa, const dva_t *dva, const blkptr_t *bp) +{ + uint64_t vdevid = DVA_GET_VDEV(dva); + + if (vdevid >= spa->spa_root_vdev->vdev_children) + return (B_FALSE); + + vdev_t *vd = spa->spa_root_vdev->vdev_child[vdevid]; + if (vd == NULL) + return (B_FALSE); + + if (vd->vdev_ops == &vdev_hole_ops) + return (B_FALSE); + + if (vd->vdev_ops == &vdev_missing_ops) { + return (B_FALSE); + } + + uint64_t offset = DVA_GET_OFFSET(dva); + uint64_t asize = DVA_GET_ASIZE(dva); + + if (BP_IS_GANG(bp)) + asize = vdev_psize_to_asize(vd, SPA_GANGBLOCKSIZE); + if (offset + asize > vd->vdev_asize) + return (B_FALSE); + + return (B_TRUE); +} + zio_t * zio_read(zio_t *pio, spa_t *spa, const blkptr_t *bp, abd_t *data, uint64_t size, zio_done_func_t *done, void *private, @@ -3041,10 +3078,14 @@ zio_vdev_io_start(zio_t *zio) } ASSERT3P(zio->io_logical, !=, zio); - if (zio->io_type == ZIO_TYPE_WRITE && zio->io_vd->vdev_removing) { - ASSERT(zio->io_flags & - (ZIO_FLAG_PHYSICAL | ZIO_FLAG_SELF_HEAL | - ZIO_FLAG_INDUCE_DAMAGE)); + if (zio->io_type == ZIO_TYPE_WRITE) { + ASSERT(spa->spa_trust_config); + + if (zio->io_vd->vdev_removing) { + ASSERT(zio->io_flags & + (ZIO_FLAG_PHYSICAL | ZIO_FLAG_SELF_HEAL | + ZIO_FLAG_INDUCE_DAMAGE)); + } } /* diff --git a/uts/common/sys/fs/zfs.h b/uts/common/sys/fs/zfs.h index b37fa81211f..5d3bf831fc7 100644 --- a/uts/common/sys/fs/zfs.h +++ b/uts/common/sys/fs/zfs.h @@ -578,6 +578,7 @@ typedef struct zpool_rewind_policy { #define ZPOOL_CONFIG_VDEV_TOP_ZAP "com.delphix:vdev_zap_top" #define ZPOOL_CONFIG_VDEV_LEAF_ZAP "com.delphix:vdev_zap_leaf" #define ZPOOL_CONFIG_HAS_PER_VDEV_ZAPS "com.delphix:has_per_vdev_zaps" +#define ZPOOL_CONFIG_CACHEFILE "cachefile" /* not stored on disk */ /* * The persistent vdev state is stored as separate values rather than a single * 'vdev_state' entry. This is because a device can be in multiple states, such @@ -678,7 +679,8 @@ typedef enum vdev_aux { VDEV_AUX_IO_FAILURE, /* experienced I/O failure */ VDEV_AUX_BAD_LOG, /* cannot read log chain(s) */ VDEV_AUX_EXTERNAL, /* external diagnosis */ - VDEV_AUX_SPLIT_POOL /* vdev was split off into another pool */ + VDEV_AUX_SPLIT_POOL, /* vdev was split off into another pool */ + VDEV_AUX_CHILDREN_OFFLINE /* all children are offline */ } vdev_aux_t; /*