6721908 A hot spare "in use" in an exported zpool, is stolen when a disk fails in an imported pool.
authorlling
Tue, 29 Jul 2008 10:34:44 -0700
changeset 7214 04c540040a32
parent 7213 31d434619637
child 7215 032e4392cc5f
6721908 A hot spare "in use" in an exported zpool, is stolen when a disk fails in an imported pool.
usr/src/cmd/zpool/zpool_main.c
usr/src/cmd/ztest/ztest.c
usr/src/lib/libzfs/common/libzfs.h
usr/src/lib/libzfs/common/libzfs_pool.c
usr/src/lib/libzfs/common/libzfs_util.c
usr/src/uts/common/fs/zfs/spa.c
usr/src/uts/common/fs/zfs/spa_misc.c
usr/src/uts/common/fs/zfs/sys/spa.h
usr/src/uts/common/fs/zfs/vdev_label.c
usr/src/uts/common/fs/zfs/zfs_ioctl.c
--- 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);
 }