ZFS panic on zvol resize

Kristof Provost kristof at sigsegv.be
Sat Jul 19 22:42:26 UTC 2014


On 2014-07-19 18:41:13 (+0200), Kristof Provost <kristof at sigsegv.be> wrote:
> With this patch I no longer see the original panic, but I get a shiny
> new one in its place:
> 
> panic: solaris assert: txg_how != TXG_WAIT || !dsl_pool_config_held(tx->tx_pool), file: /usr/src/sys/modules/zfs/../../cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_tx.c, line: 1279

Both problems appear to be fixed in Illumos commit
3b2aab18808792cbd248a12f1edf139b89833c13

Essentially they've changed from using dmu_objset_hold() to
dmu_objset_own().

See:
 - https://www.illumos.org/issues/3464
 - https://github.com/illumos/illumos-gate/commit/3b2aab18808792cbd248a12f1edf139b89833c13
 - ZoL: https://github.com/zfsonlinux/zfs/pull/2048

I included the zvol resize bits of the patch in my tree, and can now
resize zvols without panicing the machine.


Index: cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zvol.h
===================================================================
--- cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zvol.h	(revision 268881)
+++ cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zvol.h	(working copy)
@@ -43,7 +43,7 @@
 extern int zvol_create_minor(const char *);
 extern int zvol_remove_minor(const char *);
 extern void zvol_remove_minors(const char *);
-extern int zvol_set_volsize(const char *, major_t, uint64_t);
+extern int zvol_set_volsize(const char *, uint64_t);
 
 #ifdef sun
 extern int zvol_open(dev_t *devp, int flag, int otyp, cred_t *cr);
Index: cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
===================================================================
--- cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c	(revision 268881)
+++ cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c	(working copy)
@@ -2482,8 +2482,7 @@
 		err = dsl_dataset_set_refreservation(dsname, source, intval);
 		break;
 	case ZFS_PROP_VOLSIZE:
-		err = zvol_set_volsize(dsname, ddi_driver_major(zfs_dip),
-		    intval);
+		err = zvol_set_volsize(dsname, intval);
 		break;
 	case ZFS_PROP_VERSION:
 	{
Index: cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c
===================================================================
--- cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c	(revision 268881)
+++ cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c	(working copy)
@@ -203,11 +203,12 @@
 static void zvol_geom_worker(void *arg);
 
 static void
-zvol_size_changed(zvol_state_t *zv)
+zvol_size_changed(zvol_state_t *zv, uint64_t volsize)
 {
 #ifdef sun
 	dev_t dev = makedevice(maj, min);
 
+	zv->zv_volsize = volsize;
 	VERIFY(ddi_prop_update_int64(dev, zfs_dip,
 	    "Size", volsize) == DDI_SUCCESS);
 	VERIFY(ddi_prop_update_int64(dev, zfs_dip,
@@ -765,9 +766,9 @@
 		dmu_objset_disown(os, zvol_tag);
 		return (error);
 	}
-	zv->zv_volsize = volsize;
+
+	zvol_size_changed(zv, volsize);
 	zv->zv_zilog = zil_open(os, zvol_get_data);
-	zvol_size_changed(zv);
 
 	VERIFY(dsl_prop_get_integer(zv->zv_name, "readonly", &readonly,
 	    NULL) == 0);
@@ -891,65 +892,42 @@
 	PICKUP_GIANT();
 }
 
-int
-zvol_set_volsize(const char *name, major_t maj, uint64_t volsize)
+static int
+zvol_update_live_volsize(zvol_state_t *zv, uint64_t volsize)
 {
-	zvol_state_t *zv = NULL;
-	objset_t *os;
-	int error;
-	dmu_object_info_t doi;
 	uint64_t old_volsize = 0ULL;
-	uint64_t readonly;
+	int error = 0;
 
-	mutex_enter(&spa_namespace_lock);
-	zv = zvol_minor_lookup(name);
-	if ((error = dmu_objset_hold(name, FTAG, &os)) != 0) {
-		mutex_exit(&spa_namespace_lock);
-		return (error);
-	}
+	ASSERT(MUTEX_HELD(&spa_namespace_lock));
 
-	if ((error = dmu_object_info(os, ZVOL_OBJ, &doi)) != 0 ||
-	    (error = zvol_check_volsize(volsize,
-	    doi.doi_data_block_size)) != 0)
-		goto out;
-
-	VERIFY(dsl_prop_get_integer(name, "readonly", &readonly,
-	    NULL) == 0);
-	if (readonly) {
-		error = EROFS;
-		goto out;
-	}
-
-	error = zvol_update_volsize(os, volsize);
 	/*
 	 * Reinitialize the dump area to the new size. If we
 	 * failed to resize the dump area then restore it back to
-	 * its original size.
+	 * its original size.  We must set the new volsize prior
+	 * to calling dumpvp_resize() to ensure that the devices'
+	 * size(9P) is not visible by the dump subsystem.
 	 */
-	if (zv && error == 0) {
+	old_volsize = zv->zv_volsize;
+	zvol_size_changed(zv, volsize);
 #ifdef ZVOL_DUMP
-		if (zv->zv_flags & ZVOL_DUMPIFIED) {
-			old_volsize = zv->zv_volsize;
-			zv->zv_volsize = volsize;
-			if ((error = zvol_dumpify(zv)) != 0 ||
-			    (error = dumpvp_resize()) != 0) {
-				(void) zvol_update_volsize(os, old_volsize);
-				zv->zv_volsize = old_volsize;
-				error = zvol_dumpify(zv);
-			}
+	if (zv->zv_flags & ZVOL_DUMPIFIED) {
+		if ((error = zvol_dumpify(zv)) != 0 ||
+		    (error = dumpvp_resize()) != 0) {
+			int dumpify_error;
+
+			(void) zvol_update_volsize(zv->zv_objset, old_volsize);
+			zvol_size_changed(zv, old_volsize);
+			dumpify_error = zvol_dumpify(zv);
+			error = dumpify_error ? dumpify_error : error;
 		}
-#endif	/* ZVOL_DUMP */
-		if (error == 0) {
-			zv->zv_volsize = volsize;
-			zvol_size_changed(zv);
-		}
 	}
+#endif /* ZVOL_DUMP */
 
 #ifdef sun
 	/*
 	 * Generate a LUN expansion event.
 	 */
-	if (zv && error == 0) {
+	if (error == 0) {
 		sysevent_id_t eid;
 		nvlist_t *attr;
 		char *physpath = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
@@ -967,12 +945,57 @@
 		kmem_free(physpath, MAXPATHLEN);
 	}
 #endif	/* sun */
+	return (error);
+}
 
+int
+zvol_set_volsize(const char *name, uint64_t volsize)
+{
+	zvol_state_t *zv = NULL;
+	objset_t *os;
+	int error;
+	dmu_object_info_t doi;
+	uint64_t readonly;
+	boolean_t owned = B_FALSE;
+
+	error = dsl_prop_get_integer(name,
+	    zfs_prop_to_name(ZFS_PROP_READONLY), &readonly, NULL);
+	if (error != 0)
+		return (error);
+	if (readonly)
+		return (SET_ERROR(EROFS));
+
+	mutex_enter(&spa_namespace_lock);
+	zv = zvol_minor_lookup(name);
+
+	if (zv == NULL || zv->zv_objset == NULL) {
+		if ((error = dmu_objset_own(name, DMU_OST_ZVOL, B_FALSE,
+		    FTAG, &os)) != 0) {
+			mutex_exit(&spa_namespace_lock);
+			return (error);
+		}
+		owned = B_TRUE;
+		if (zv != NULL)
+			zv->zv_objset = os;
+	} else {
+		os = zv->zv_objset;
+	}
+
+	if ((error = dmu_object_info(os, ZVOL_OBJ, &doi)) != 0 ||
+	    (error = zvol_check_volsize(volsize, doi.doi_data_block_size)) != 0)
+		goto out;
+
+	error = zvol_update_volsize(os, volsize);
+
+	if (error == 0 && zv != NULL)
+		error = zvol_update_live_volsize(zv, volsize);
 out:
-	dmu_objset_rele(os, FTAG);
-
+	if (owned) {
+		dmu_objset_disown(os, FTAG);
+		if (zv != NULL)
+			zv->zv_objset = NULL;
+	}
 	mutex_exit(&spa_namespace_lock);
-
 	return (error);
 }
 
Regards,
Kristof


More information about the freebsd-fs mailing list