diff options
author | Olaf Faaland <faaland1@llnl.gov> | 2019-03-28 20:30:57 +0000 |
---|---|---|
committer | Dan McDonald <danmcd@joyent.com> | 2019-04-03 14:42:53 -0400 |
commit | e0f1c0afa46cc84d4b1e40124032a9a87310386e (patch) | |
tree | 8aa10b26a7ae7380d42c5352be4dc06b0ed4cae0 /usr/src/lib/libzfs | |
parent | c93ad993b7959fc974ed6f4a92fce6041d98bd11 (diff) | |
download | illumos-joyent-e0f1c0afa46cc84d4b1e40124032a9a87310386e.tar.gz |
10499 Multi-modifier protection (MMP)
Portions contributed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Portions contributed by: Tim Chase <tim@chase2k.com>
Portions contributed by: sanjeevbagewadi <sanjeev.bagewadi@gmail.com>
Portions contributed by: John L. Hammond <john.hammond@intel.com>
Portions contributed by: Giuseppe Di Natale <dinatale2@llnl.gov>
Portions contributed by: Prakash Surya <surya1@llnl.gov>
Reviewed by: George Melikov <mail@gmelikov.ru>
Reviewed by: Tom Caputi <tcaputi@datto.com>
Reviewed by: Kash Pande <kash@tripleback.net>
Reviewed by: loli10K <ezomori.nozomu@gmail.com>
Reviewed by: George Melikov <mail@gmelikov.ru>
Reviewed by: Tony Hutter <hutter2@llnl.gov>
Reviewed by: Gu Zheng <guzheng2331314@163.com>
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed by: Ned Bass <bass6@llnl.gov>
Reviewed by: Andreas Dilger <andreas.dilger@intel.com>
Reviewed by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed by: Andy Stormont <astormont@racktopsystems.com>
Reviewed by: Toomas Soome <tsoome@me.com>
Reviewed by: Kody Kantor <kody.kantor@joyent.com>
Approved by: Dan McDonald <danmcd@joyent.com>
Diffstat (limited to 'usr/src/lib/libzfs')
-rw-r--r-- | usr/src/lib/libzfs/common/libzfs.h | 11 | ||||
-rw-r--r-- | usr/src/lib/libzfs/common/libzfs_dataset.c | 2 | ||||
-rw-r--r-- | usr/src/lib/libzfs/common/libzfs_impl.h | 2 | ||||
-rw-r--r-- | usr/src/lib/libzfs/common/libzfs_import.c | 77 | ||||
-rw-r--r-- | usr/src/lib/libzfs/common/libzfs_pool.c | 54 | ||||
-rw-r--r-- | usr/src/lib/libzfs/common/libzfs_status.c | 80 | ||||
-rw-r--r-- | usr/src/lib/libzfs/common/libzfs_util.c | 29 | ||||
-rw-r--r-- | usr/src/lib/libzfs/common/mapfile-vers | 2 |
8 files changed, 231 insertions, 26 deletions
diff --git a/usr/src/lib/libzfs/common/libzfs.h b/usr/src/lib/libzfs/common/libzfs.h index ce3ef0f8a8..ef26165d30 100644 --- a/usr/src/lib/libzfs/common/libzfs.h +++ b/usr/src/lib/libzfs/common/libzfs.h @@ -23,7 +23,7 @@ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2011 Pawel Jakub Dawidek. All rights reserved. * Copyright (c) 2011, 2017 by Delphix. All rights reserved. - * Copyright (c) 2012, Joyent, Inc. All rights reserved. + * Copyright 2019 Joyent, Inc. * Copyright (c) 2013 Steven Hartland. All rights reserved. * Copyright (c) 2014 Integros [integros.com] * Copyright 2016 Nexenta Systems, Inc. @@ -130,6 +130,7 @@ typedef enum zfs_error { EZFS_DIFFDATA, /* bad zfs diff data */ EZFS_POOLREADONLY, /* pool is in read-only mode */ EZFS_SCRUB_PAUSED, /* scrub currently paused */ + EZFS_ACTIVE_POOL, /* pool is imported on a different system */ EZFS_NO_PENDING, /* cannot cancel, no operation is pending */ EZFS_CHECKPOINT_EXISTS, /* checkpoint exists */ EZFS_DISCARDING_CHECKPOINT, /* currently discarding a checkpoint */ @@ -313,6 +314,8 @@ typedef enum { /* * The following correspond to faults as defined in the (fault.fs.zfs.*) * event namespace. Each is associated with a corresponding message ID. + * This must be kept in sync with the zfs_msgid_table in + * lib/libzfs/libzfs_status.c. */ ZPOOL_STATUS_CORRUPT_CACHE, /* corrupt /kernel/drv/zpool.cache */ ZPOOL_STATUS_MISSING_DEV_R, /* missing device with replicas */ @@ -325,8 +328,11 @@ typedef enum { ZPOOL_STATUS_FAILING_DEV, /* device experiencing errors */ ZPOOL_STATUS_VERSION_NEWER, /* newer on-disk version */ ZPOOL_STATUS_HOSTID_MISMATCH, /* last accessed by another system */ + ZPOOL_STATUS_HOSTID_ACTIVE, /* currently active on another system */ + ZPOOL_STATUS_HOSTID_REQUIRED, /* multihost=on and hostid=0 */ ZPOOL_STATUS_IO_FAILURE_WAIT, /* failed I/O, failmode 'wait' */ ZPOOL_STATUS_IO_FAILURE_CONTINUE, /* failed I/O, failmode 'continue' */ + ZPOOL_STATUS_IO_FAILURE_MMP, /* failed MMP, failmode not 'panic' */ ZPOOL_STATUS_BAD_LOG, /* cannot read log chain(s) */ /* @@ -404,6 +410,8 @@ typedef struct importargs { } importargs_t; extern nvlist_t *zpool_search_import(libzfs_handle_t *, importargs_t *); +extern int zpool_tryimport(libzfs_handle_t *hdl, char *target, + nvlist_t **configp, importargs_t *args); /* legacy pool search routines */ extern nvlist_t *zpool_find_import(libzfs_handle_t *, int, char **); @@ -723,6 +731,7 @@ extern boolean_t zfs_dataset_exists(libzfs_handle_t *, const char *, zfs_type_t); extern int zfs_spa_version(zfs_handle_t *, int *); extern boolean_t zfs_bookmark_exists(const char *path); +extern ulong_t get_system_hostid(void); /* * Mount support functions. diff --git a/usr/src/lib/libzfs/common/libzfs_dataset.c b/usr/src/lib/libzfs/common/libzfs_dataset.c index 980a1b7366..f27322b694 100644 --- a/usr/src/lib/libzfs/common/libzfs_dataset.c +++ b/usr/src/lib/libzfs/common/libzfs_dataset.c @@ -440,6 +440,8 @@ make_dataset_handle_common(zfs_handle_t *zhp, zfs_cmd_t *zc) zhp->zfs_head_type = ZFS_TYPE_VOLUME; else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS) zhp->zfs_head_type = ZFS_TYPE_FILESYSTEM; + else if (zhp->zfs_dmustats.dds_type == DMU_OST_OTHER) + return (-1); else abort(); diff --git a/usr/src/lib/libzfs/common/libzfs_impl.h b/usr/src/lib/libzfs/common/libzfs_impl.h index cd9a53d91f..9f9890fe58 100644 --- a/usr/src/lib/libzfs/common/libzfs_impl.h +++ b/usr/src/lib/libzfs/common/libzfs_impl.h @@ -139,7 +139,7 @@ typedef enum { SHARED_SMB = 0x4 } zfs_share_type_t; -#define CONFIG_BUF_MINSIZE 65536 +#define CONFIG_BUF_MINSIZE 262144 int zfs_error(libzfs_handle_t *, int, const char *); int zfs_error_fmt(libzfs_handle_t *, int, const char *, ...); diff --git a/usr/src/lib/libzfs/common/libzfs_import.c b/usr/src/lib/libzfs/common/libzfs_import.c index 5bd900ad00..ce5864a62b 100644 --- a/usr/src/lib/libzfs/common/libzfs_import.c +++ b/usr/src/lib/libzfs/common/libzfs_import.c @@ -1438,16 +1438,87 @@ name_or_guid_exists(zpool_handle_t *zhp, void *data) nvlist_t * zpool_search_import(libzfs_handle_t *hdl, importargs_t *import) { + nvlist_t *pools = NULL; + verify(import->poolname == NULL || import->guid == 0); if (import->unique) import->exists = zpool_iter(hdl, name_or_guid_exists, import); if (import->cachefile != NULL) - return (zpool_find_import_cached(hdl, import->cachefile, - import->poolname, import->guid)); + pools = zpool_find_import_cached(hdl, import->cachefile, + import->poolname, import->guid); + else + pools = zpool_find_import_impl(hdl, import); + + return (pools); +} + +static boolean_t +pool_match(nvlist_t *cfg, char *tgt) +{ + uint64_t v, guid = strtoull(tgt, NULL, 0); + char *s; + + if (guid != 0) { + if (nvlist_lookup_uint64(cfg, ZPOOL_CONFIG_POOL_GUID, &v) == 0) + return (v == guid); + } else { + if (nvlist_lookup_string(cfg, ZPOOL_CONFIG_POOL_NAME, &s) == 0) + return (strcmp(s, tgt) == 0); + } + return (B_FALSE); +} + +int +zpool_tryimport(libzfs_handle_t *hdl, char *target, nvlist_t **configp, + importargs_t *args) +{ + nvlist_t *pools; + nvlist_t *match = NULL; + nvlist_t *config = NULL; + char *sepp = NULL; + int count = 0; + char *targetdup = strdup(target); + + *configp = NULL; - return (zpool_find_import_impl(hdl, import)); + if ((sepp = strpbrk(targetdup, "/@")) != NULL) { + *sepp = '\0'; + } + + pools = zpool_search_import(hdl, args); + + if (pools != NULL) { + nvpair_t *elem = NULL; + while ((elem = nvlist_next_nvpair(pools, elem)) != NULL) { + VERIFY0(nvpair_value_nvlist(elem, &config)); + if (pool_match(config, targetdup)) { + count++; + if (match != NULL) { + /* multiple matches found */ + continue; + } else { + match = config; + } + } + } + } + + if (count == 0) { + free(targetdup); + return (ENOENT); + } + + if (count > 1) { + free(targetdup); + return (EINVAL); + } + + *configp = match; + free(targetdup); + + return (0); } boolean_t diff --git a/usr/src/lib/libzfs/common/libzfs_pool.c b/usr/src/lib/libzfs/common/libzfs_pool.c index ab33273781..b749022bf7 100644 --- a/usr/src/lib/libzfs/common/libzfs_pool.c +++ b/usr/src/lib/libzfs/common/libzfs_pool.c @@ -665,6 +665,15 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname, } break; + case ZPOOL_PROP_MULTIHOST: + if (get_system_hostid() == 0) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "requires a non-zero system hostid")); + (void) zfs_error(hdl, EZFS_BADPROP, errbuf); + goto error; + } + break; + default: zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "property '%s'(%d) not defined"), propname, prop); @@ -1802,6 +1811,7 @@ zpool_import_props(libzfs_handle_t *hdl, nvlist_t *config, const char *newname, if (error) { char desc[1024]; + char aux[256]; /* * Dry-run failed, but we print out what success @@ -1847,6 +1857,46 @@ zpool_import_props(libzfs_handle_t *hdl, nvlist_t *config, const char *newname, (void) zfs_error(hdl, EZFS_BADVERSION, desc); break; + case EREMOTEIO: + if (nv != NULL && nvlist_lookup_nvlist(nv, + ZPOOL_CONFIG_LOAD_INFO, &nvinfo) == 0) { + char *hostname = "<unknown>"; + uint64_t hostid = 0; + mmp_state_t mmp_state; + + mmp_state = fnvlist_lookup_uint64(nvinfo, + ZPOOL_CONFIG_MMP_STATE); + + if (nvlist_exists(nvinfo, + ZPOOL_CONFIG_MMP_HOSTNAME)) + hostname = fnvlist_lookup_string(nvinfo, + ZPOOL_CONFIG_MMP_HOSTNAME); + + if (nvlist_exists(nvinfo, + ZPOOL_CONFIG_MMP_HOSTID)) + hostid = fnvlist_lookup_uint64(nvinfo, + ZPOOL_CONFIG_MMP_HOSTID); + + if (mmp_state == MMP_STATE_ACTIVE) { + (void) snprintf(aux, sizeof (aux), + dgettext(TEXT_DOMAIN, "pool is imp" + "orted on host '%s' (hostid=%lx).\n" + "Export the pool on the other " + "system, then run 'zpool import'."), + hostname, (unsigned long) hostid); + } else if (mmp_state == MMP_STATE_NO_HOSTID) { + (void) snprintf(aux, sizeof (aux), + dgettext(TEXT_DOMAIN, "pool has " + "the multihost property on and " + "the\nsystem's hostid is not " + "set.\n")); + } + + (void) zfs_error_aux(hdl, aux); + } + (void) zfs_error(hdl, EZFS_ACTIVE_POOL, desc); + break; + case EINVAL: (void) zfs_error(hdl, EZFS_INVALCONFIG, desc); break; @@ -2387,7 +2437,7 @@ zpool_find_vdev(zpool_handle_t *zhp, const char *path, boolean_t *avail_spare, } static int -vdev_online(nvlist_t *nv) +vdev_is_online(nvlist_t *nv) { uint64_t ival; @@ -2455,7 +2505,7 @@ vdev_get_physpaths(nvlist_t *nv, char *physpath, size_t phypath_size, return (EZFS_INVALCONFIG); } - if (vdev_online(nv)) { + if (vdev_is_online(nv)) { if ((ret = vdev_get_one_physpath(nv, physpath, phypath_size, rsz)) != 0) return (ret); diff --git a/usr/src/lib/libzfs/common/libzfs_status.c b/usr/src/lib/libzfs/common/libzfs_status.c index 71b27a1214..7a701b78c6 100644 --- a/usr/src/lib/libzfs/common/libzfs_status.c +++ b/usr/src/lib/libzfs/common/libzfs_status.c @@ -53,20 +53,36 @@ * of this table, and hence have no associated message ID. */ static char *zfs_msgid_table[] = { - "ZFS-8000-14", - "ZFS-8000-2Q", - "ZFS-8000-3C", - "ZFS-8000-4J", - "ZFS-8000-5E", - "ZFS-8000-6X", - "ZFS-8000-72", - "ZFS-8000-8A", - "ZFS-8000-9P", - "ZFS-8000-A5", - "ZFS-8000-EY", - "ZFS-8000-HC", - "ZFS-8000-JQ", - "ZFS-8000-K4", + "ZFS-8000-14", /* ZPOOL_STATUS_CORRUPT_CACHE */ + "ZFS-8000-2Q", /* ZPOOL_STATUS_MISSING_DEV_R */ + "ZFS-8000-3C", /* ZPOOL_STATUS_MISSING_DEV_NR */ + "ZFS-8000-4J", /* ZPOOL_STATUS_CORRUPT_LABEL_R */ + "ZFS-8000-5E", /* ZPOOL_STATUS_CORRUPT_LABEL_NR */ + "ZFS-8000-6X", /* ZPOOL_STATUS_BAD_GUID_SUM */ + "ZFS-8000-72", /* ZPOOL_STATUS_CORRUPT_POOL */ + "ZFS-8000-8A", /* ZPOOL_STATUS_CORRUPT_DATA */ + "ZFS-8000-9P", /* ZPOOL_STATUS_FAILING_DEV */ + "ZFS-8000-A5", /* ZPOOL_STATUS_VERSION_NEWER */ + "ZFS-8000-EY", /* ZPOOL_STATUS_HOSTID_MISMATCH */ + "ZFS-8000-EY", /* ZPOOL_STATUS_HOSTID_ACTIVE */ + "ZFS-8000-EY", /* ZPOOL_STATUS_HOSTID_REQUIRED */ + "ZFS-8000-HC", /* ZPOOL_STATUS_IO_FAILURE_WAIT */ + "ZFS-8000-JQ", /* ZPOOL_STATUS_IO_FAILURE_CONTINUE */ + "ZFS-8000-MM", /* ZPOOL_STATUS_IO_FAILURE_MMP */ + "ZFS-8000-K4", /* ZPOOL_STATUS_BAD_LOG */ + /* + * The following results have no message ID. + * ZPOOL_STATUS_UNSUP_FEAT_READ + * ZPOOL_STATUS_UNSUP_FEAT_WRITE + * ZPOOL_STATUS_FAULTED_DEV_R + * ZPOOL_STATUS_FAULTED_DEV_NR + * ZPOOL_STATUS_VERSION_OLDER + * ZPOOL_STATUS_FEAT_DISABLED + * ZPOOL_STATUS_RESILVERING + * ZPOOL_STATUS_OFFLINE_DEV + * ZPOOL_STATUS_REMOVED_DEV + * ZPOOL_STATUS_OK + */ }; #define NMSGID (sizeof (zfs_msgid_table) / sizeof (zfs_msgid_table[0])) @@ -193,6 +209,7 @@ check_status(nvlist_t *config, boolean_t isimport) uint64_t stateval; uint64_t suspended; uint64_t hostid = 0; + unsigned long system_hostid = get_system_hostid(); verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION, &version) == 0); @@ -213,10 +230,30 @@ check_status(nvlist_t *config, boolean_t isimport) return (ZPOOL_STATUS_RESILVERING); /* + * The multihost property is set and the pool may be active. + */ + if (vs->vs_state == VDEV_STATE_CANT_OPEN && + vs->vs_aux == VDEV_AUX_ACTIVE) { + mmp_state_t mmp_state; + nvlist_t *nvinfo; + + nvinfo = fnvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO); + mmp_state = fnvlist_lookup_uint64(nvinfo, + ZPOOL_CONFIG_MMP_STATE); + + if (mmp_state == MMP_STATE_ACTIVE) + return (ZPOOL_STATUS_HOSTID_ACTIVE); + else if (mmp_state == MMP_STATE_NO_HOSTID) + return (ZPOOL_STATUS_HOSTID_REQUIRED); + else + return (ZPOOL_STATUS_HOSTID_MISMATCH); + } + + /* * Pool last accessed by another system. */ (void) nvlist_lookup_uint64(config, ZPOOL_CONFIG_HOSTID, &hostid); - if (hostid != 0 && (unsigned long)hostid != gethostid() && + if (hostid != 0 && (unsigned long)hostid != system_hostid && stateval == POOL_STATE_ACTIVE) return (ZPOOL_STATUS_HOSTID_MISMATCH); @@ -249,10 +286,16 @@ check_status(nvlist_t *config, boolean_t isimport) return (ZPOOL_STATUS_BAD_GUID_SUM); /* - * Check whether the pool has suspended due to failed I/O. + * Check whether the pool has suspended. */ if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_SUSPENDED, &suspended) == 0) { + uint64_t reason; + + if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_SUSPENDED_REASON, + &reason) == 0 && reason == ZIO_SUSPEND_MMP) + return (ZPOOL_STATUS_IO_FAILURE_MMP); + if (suspended == ZIO_FAILURE_MODE_CONTINUE) return (ZPOOL_STATUS_IO_FAILURE_CONTINUE); return (ZPOOL_STATUS_IO_FAILURE_WAIT); @@ -341,8 +384,9 @@ check_status(nvlist_t *config, boolean_t isimport) if (isimport) { feat = fnvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO); - feat = fnvlist_lookup_nvlist(feat, - ZPOOL_CONFIG_ENABLED_FEAT); + if (nvlist_exists(feat, ZPOOL_CONFIG_ENABLED_FEAT)) + feat = fnvlist_lookup_nvlist(feat, + ZPOOL_CONFIG_ENABLED_FEAT); } else { feat = fnvlist_lookup_nvlist(config, ZPOOL_CONFIG_FEATURE_STATS); diff --git a/usr/src/lib/libzfs/common/libzfs_util.c b/usr/src/lib/libzfs/common/libzfs_util.c index aa74189cc8..3ea967fbed 100644 --- a/usr/src/lib/libzfs/common/libzfs_util.c +++ b/usr/src/lib/libzfs/common/libzfs_util.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2018 Joyent, Inc. + * Copyright 2019 Joyent, Inc. * Copyright (c) 2011, 2017 by Delphix. All rights reserved. * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com> * Copyright (c) 2017 Datto Inc. @@ -52,6 +52,7 @@ #include "libzfs_impl.h" #include "zfs_prop.h" +#include "zfs_comutil.h" #include "zfeature_common.h" int @@ -249,6 +250,9 @@ libzfs_error_description(libzfs_handle_t *hdl) return (dgettext(TEXT_DOMAIN, "device removal in progress")); case EZFS_VDEV_TOO_BIG: return (dgettext(TEXT_DOMAIN, "device exceeds supported size")); + case EZFS_ACTIVE_POOL: + return (dgettext(TEXT_DOMAIN, "pool is imported on a " + "different host")); case EZFS_TOOMANY: return (dgettext(TEXT_DOMAIN, "argument list too long")); case EZFS_INITIALIZING: @@ -418,6 +422,9 @@ zfs_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...) "pool I/O is currently suspended")); zfs_verror(hdl, EZFS_POOLUNAVAIL, fmt, ap); break; + case EREMOTEIO: + zfs_verror(hdl, EZFS_ACTIVE_POOL, fmt, ap); + break; default: zfs_error_aux(hdl, strerror(error)); zfs_verror(hdl, EZFS_UNKNOWN, fmt, ap); @@ -505,6 +512,9 @@ zpool_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...) case ENOTACTIVE: zfs_verror(hdl, EZFS_NO_PENDING, fmt, ap); break; + case EREMOTEIO: + zfs_verror(hdl, EZFS_ACTIVE_POOL, fmt, ap); + break; case ZFS_ERR_CHECKPOINT_EXISTS: zfs_verror(hdl, EZFS_CHECKPOINT_EXISTS, fmt, ap); break; @@ -1596,3 +1606,20 @@ zfs_get_hole_count(const char *path, uint64_t *count, uint64_t *bs) } return (0); } + +ulong_t +get_system_hostid(void) +{ + char *env; + + /* + * Allow the hostid to be subverted for testing. + */ + env = getenv("ZFS_HOSTID"); + if (env) { + ulong_t hostid = strtoull(env, NULL, 16); + return (hostid & 0xFFFFFFFF); + } + + return (gethostid()); +} diff --git a/usr/src/lib/libzfs/common/mapfile-vers b/usr/src/lib/libzfs/common/mapfile-vers index d747f98452..26a68259af 100644 --- a/usr/src/lib/libzfs/common/mapfile-vers +++ b/usr/src/lib/libzfs/common/mapfile-vers @@ -49,6 +49,7 @@ SYMBOL_VERSION SUNWprivate_1.1 { fletcher_4_byteswap; fletcher_4_incremental_native; fletcher_4_incremental_byteswap; + get_system_hostid; libzfs_add_handle; libzfs_errno; libzfs_error_action; @@ -245,6 +246,7 @@ SYMBOL_VERSION SUNWprivate_1.1 { zpool_skip_pool; zpool_state_to_name; zpool_sync_one; + zpool_tryimport; zpool_unmount_datasets; zpool_upgrade; zpool_vdev_attach; |