6721908 A hot spare "in use" in an exported zpool, is stolen when a disk fails in an imported pool.
--- a/usr/src/cmd/zpool/zpool_main.c Tue Jul 29 06:42:46 2008 -0700
+++ b/usr/src/cmd/zpool/zpool_main.c Tue Jul 29 10:34:44 2008 -0700
@@ -918,7 +918,7 @@
continue;
}
- if (zpool_export(zhp) != 0)
+ if (zpool_export(zhp, force) != 0)
ret = 1;
zpool_close(zhp);
--- a/usr/src/cmd/ztest/ztest.c Tue Jul 29 06:42:46 2008 -0700
+++ b/usr/src/cmd/ztest/ztest.c Tue Jul 29 10:34:44 2008 -0700
@@ -2938,7 +2938,7 @@
/*
* Export it.
*/
- error = spa_export(oldname, &config);
+ error = spa_export(oldname, &config, B_FALSE);
if (error)
fatal(0, "spa_export('%s') = %d", oldname, error);
--- a/usr/src/lib/libzfs/common/libzfs.h Tue Jul 29 06:42:46 2008 -0700
+++ b/usr/src/lib/libzfs/common/libzfs.h Tue Jul 29 10:34:44 2008 -0700
@@ -116,6 +116,7 @@
EZFS_ISL2CACHE, /* device is for the level 2 ARC */
EZFS_VDEVNOTSUP, /* unsupported vdev type */
EZFS_NOTSUP, /* ops not supported on this dataset */
+ EZFS_ACTIVE_SPARE, /* pool has active shared spare devices */
EZFS_UNKNOWN
};
@@ -288,7 +289,7 @@
/*
* Import and export functions
*/
-extern int zpool_export(zpool_handle_t *);
+extern int zpool_export(zpool_handle_t *, boolean_t);
extern int zpool_import(libzfs_handle_t *, nvlist_t *, const char *,
char *altroot);
extern int zpool_import_props(libzfs_handle_t *, nvlist_t *, const char *,
--- a/usr/src/lib/libzfs/common/libzfs_pool.c Tue Jul 29 06:42:46 2008 -0700
+++ b/usr/src/lib/libzfs/common/libzfs_pool.c Tue Jul 29 10:34:44 2008 -0700
@@ -1081,19 +1081,36 @@
* mounted datasets in the pool.
*/
int
-zpool_export(zpool_handle_t *zhp)
+zpool_export(zpool_handle_t *zhp, boolean_t force)
{
zfs_cmd_t zc = { 0 };
+ char msg[1024];
if (zpool_remove_zvol_links(zhp) != 0)
return (-1);
+ (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
+ "cannot export '%s'"), zhp->zpool_name);
+
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
-
- if (zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_POOL_EXPORT, &zc) != 0)
- return (zpool_standard_error_fmt(zhp->zpool_hdl, errno,
- dgettext(TEXT_DOMAIN, "cannot export '%s'"),
- zhp->zpool_name));
+ zc.zc_cookie = force;
+
+ if (zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_POOL_EXPORT, &zc) != 0) {
+ switch (errno) {
+ case EXDEV:
+ zfs_error_aux(zhp->zpool_hdl, dgettext(TEXT_DOMAIN,
+ "use '-f' to override the following errors:\n"
+ "'%s' has an active shared spare which could be"
+ " used by other pools once '%s' is exported."),
+ zhp->zpool_name, zhp->zpool_name);
+ return (zfs_error(zhp->zpool_hdl, EZFS_ACTIVE_SPARE,
+ msg));
+ default:
+ return (zpool_standard_error_fmt(zhp->zpool_hdl, errno,
+ msg));
+ }
+ }
+
return (0);
}
--- a/usr/src/lib/libzfs/common/libzfs_util.c Tue Jul 29 06:42:46 2008 -0700
+++ b/usr/src/lib/libzfs/common/libzfs_util.c Tue Jul 29 10:34:44 2008 -0700
@@ -209,6 +209,9 @@
case EZFS_NOTSUP:
return (dgettext(TEXT_DOMAIN, "operation not supported "
"on this dataset"));
+ case EZFS_ACTIVE_SPARE:
+ return (dgettext(TEXT_DOMAIN, "pool has active shared spare "
+ "device"));
case EZFS_UNKNOWN:
return (dgettext(TEXT_DOMAIN, "unknown error"));
default:
--- a/usr/src/uts/common/fs/zfs/spa.c Tue Jul 29 06:42:46 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/spa.c Tue Jul 29 10:34:44 2008 -0700
@@ -68,6 +68,7 @@
int zio_taskq_threads = 8;
static void spa_sync_props(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx);
+static boolean_t spa_has_active_shared_spare(spa_t *spa);
/*
* ==========================================================================
@@ -1543,7 +1544,8 @@
for (i = 0; i < nspares; i++) {
VERIFY(nvlist_lookup_uint64(spares[i],
ZPOOL_CONFIG_GUID, &guid) == 0);
- if (spa_spare_exists(guid, &pool) && pool != 0ULL) {
+ if (spa_spare_exists(guid, &pool, NULL) &&
+ pool != 0ULL) {
VERIFY(nvlist_lookup_uint64_array(
spares[i], ZPOOL_CONFIG_STATS,
(uint64_t **)&vs, &vsc) == 0);
@@ -2498,7 +2500,8 @@
* configuration from the cache afterwards.
*/
static int
-spa_export_common(char *pool, int new_state, nvlist_t **oldconfig)
+spa_export_common(char *pool, int new_state, nvlist_t **oldconfig,
+ boolean_t force)
{
spa_t *spa;
@@ -2549,6 +2552,19 @@
}
/*
+ * A pool cannot be exported if it has an active shared spare.
+ * This is to prevent other pools stealing the active spare
+ * from an exported pool. At user's own will, such pool can
+ * be forcedly exported.
+ */
+ if (!force && new_state == POOL_STATE_EXPORTED &&
+ spa_has_active_shared_spare(spa)) {
+ spa_async_resume(spa);
+ mutex_exit(&spa_namespace_lock);
+ return (EXDEV);
+ }
+
+ /*
* We want this to be reflected on every label,
* so mark them all dirty. spa_unload() will do the
* final sync that pushes these changes out.
@@ -2587,16 +2603,16 @@
int
spa_destroy(char *pool)
{
- return (spa_export_common(pool, POOL_STATE_DESTROYED, NULL));
+ return (spa_export_common(pool, POOL_STATE_DESTROYED, NULL, B_FALSE));
}
/*
* Export a storage pool.
*/
int
-spa_export(char *pool, nvlist_t **oldconfig)
+spa_export(char *pool, nvlist_t **oldconfig, boolean_t force)
{
- return (spa_export_common(pool, POOL_STATE_EXPORTED, oldconfig));
+ return (spa_export_common(pool, POOL_STATE_EXPORTED, oldconfig, force));
}
/*
@@ -2606,7 +2622,8 @@
int
spa_reset(char *pool)
{
- return (spa_export_common(pool, POOL_STATE_UNINITIALIZED, NULL));
+ return (spa_export_common(pool, POOL_STATE_UNINITIALIZED, NULL,
+ B_FALSE));
}
/*
@@ -4137,6 +4154,27 @@
}
/*
+ * Check if a pool has an active shared spare device.
+ * Note: reference count of an active spare is 2, as a spare and as a replace
+ */
+static boolean_t
+spa_has_active_shared_spare(spa_t *spa)
+{
+ int i, refcnt;
+ uint64_t pool;
+ spa_aux_vdev_t *sav = &spa->spa_spares;
+
+ for (i = 0; i < sav->sav_count; i++) {
+ if (spa_spare_exists(sav->sav_vdevs[i]->vdev_guid, &pool,
+ &refcnt) && pool != 0ULL && pool == spa_guid(spa) &&
+ refcnt > 2)
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
+/*
* Post a sysevent corresponding to the given event. The 'name' must be one of
* the event definitions in sys/sysevent/eventdefs.h. The payload will be
* filled in from the spa and (optionally) the vdev. This doesn't do anything
--- a/usr/src/uts/common/fs/zfs/spa_misc.c Tue Jul 29 06:42:46 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/spa_misc.c Tue Jul 29 10:34:44 2008 -0700
@@ -555,13 +555,12 @@
}
boolean_t
-spa_aux_exists(uint64_t guid, uint64_t *pool, avl_tree_t *avl)
+spa_aux_exists(uint64_t guid, uint64_t *pool, int *refcnt, avl_tree_t *avl)
{
spa_aux_t search, *found;
- avl_index_t where;
search.aux_guid = guid;
- found = avl_find(avl, &search, &where);
+ found = avl_find(avl, &search, NULL);
if (pool) {
if (found)
@@ -570,6 +569,13 @@
*pool = 0ULL;
}
+ if (refcnt) {
+ if (found)
+ *refcnt = found->aux_count;
+ else
+ *refcnt = 0;
+ }
+
return (found != NULL);
}
@@ -636,12 +642,12 @@
}
boolean_t
-spa_spare_exists(uint64_t guid, uint64_t *pool)
+spa_spare_exists(uint64_t guid, uint64_t *pool, int *refcnt)
{
boolean_t found;
mutex_enter(&spa_spare_lock);
- found = spa_aux_exists(guid, pool, &spa_spare_avl);
+ found = spa_aux_exists(guid, pool, refcnt, &spa_spare_avl);
mutex_exit(&spa_spare_lock);
return (found);
@@ -694,7 +700,7 @@
boolean_t found;
mutex_enter(&spa_l2cache_lock);
- found = spa_aux_exists(guid, pool, &spa_l2cache_avl);
+ found = spa_aux_exists(guid, pool, NULL, &spa_l2cache_avl);
mutex_exit(&spa_l2cache_lock);
return (found);
--- a/usr/src/uts/common/fs/zfs/sys/spa.h Tue Jul 29 06:42:46 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/sys/spa.h Tue Jul 29 10:34:44 2008 -0700
@@ -334,7 +334,7 @@
extern int spa_import_faulted(const char *, nvlist_t *, nvlist_t *);
extern nvlist_t *spa_tryimport(nvlist_t *tryconfig);
extern int spa_destroy(char *pool);
-extern int spa_export(char *pool, nvlist_t **oldconfig);
+extern int spa_export(char *pool, nvlist_t **oldconfig, boolean_t force);
extern int spa_reset(char *pool);
extern void spa_async_request(spa_t *spa, int flag);
extern void spa_async_unrequest(spa_t *spa, int flag);
@@ -359,7 +359,7 @@
/* spare state (which is global across all pools) */
extern void spa_spare_add(vdev_t *vd);
extern void spa_spare_remove(vdev_t *vd);
-extern boolean_t spa_spare_exists(uint64_t guid, uint64_t *pool);
+extern boolean_t spa_spare_exists(uint64_t guid, uint64_t *pool, int *refcnt);
extern void spa_spare_activate(vdev_t *vd);
/* L2ARC state (which is global across all pools) */
--- a/usr/src/uts/common/fs/zfs/vdev_label.c Tue Jul 29 06:42:46 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/vdev_label.c Tue Jul 29 10:34:44 2008 -0700
@@ -421,7 +421,7 @@
*/
if (state != POOL_STATE_SPARE && state != POOL_STATE_L2CACHE &&
!spa_guid_exists(pool_guid, device_guid) &&
- !spa_spare_exists(device_guid, NULL) &&
+ !spa_spare_exists(device_guid, NULL, NULL) &&
!spa_l2cache_exists(device_guid, NULL))
return (B_FALSE);
@@ -441,7 +441,7 @@
* spa_has_spare() here because it may be on our pending list of spares
* to add. We also check if it is an l2cache device.
*/
- if (spa_spare_exists(device_guid, &spare_pool) ||
+ if (spa_spare_exists(device_guid, &spare_pool, NULL) ||
spa_has_spare(spa, device_guid)) {
if (spare_guid)
*spare_guid = device_guid;
@@ -691,7 +691,7 @@
*/
if (error == 0 && !vd->vdev_isspare &&
(reason == VDEV_LABEL_SPARE ||
- spa_spare_exists(vd->vdev_guid, NULL)))
+ spa_spare_exists(vd->vdev_guid, NULL, NULL)))
spa_spare_add(vd);
if (error == 0 && !vd->vdev_isl2cache &&
--- a/usr/src/uts/common/fs/zfs/zfs_ioctl.c Tue Jul 29 06:42:46 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/zfs_ioctl.c Tue Jul 29 10:34:44 2008 -0700
@@ -856,8 +856,10 @@
zfs_ioc_pool_export(zfs_cmd_t *zc)
{
int error;
+ boolean_t force = (boolean_t)zc->zc_cookie;
+
zfs_log_history(zc);
- error = spa_export(zc->zc_name, NULL);
+ error = spa_export(zc->zc_name, NULL, force);
return (error);
}