changeset 6068:44728d72cfec

6564619 panic after a filesystem with snapshots that have files open was destroyed or umounted 6632751 Different in use detect between create a snapshot by 'mkdir' vs 'zfs snapshot'
author ck153898
date Fri, 22 Feb 2008 11:48:10 -0800
parents 6cc2e3cc43ac
children f47d11a9118f
files usr/src/uts/common/fs/zfs/sys/zfs_ctldir.h usr/src/uts/common/fs/zfs/zfs_ctldir.c
diffstat 2 files changed, 66 insertions(+), 69 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/uts/common/fs/zfs/sys/zfs_ctldir.h	Fri Feb 22 10:55:34 2008 -0800
+++ b/usr/src/uts/common/fs/zfs/sys/zfs_ctldir.h	Fri Feb 22 11:48:10 2008 -0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -55,7 +55,6 @@
 int zfsctl_rename_snapshot(const char *from, const char *to);
 int zfsctl_destroy_snapshot(const char *snapname, int force);
 int zfsctl_umount_snapshots(vfs_t *, int, cred_t *);
-int zfsctl_unmount_snap(vnode_t *dvp, const char *name, int force, cred_t *cr);
 
 int zfsctl_root_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, pathname_t *pnp,
     int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct,
--- a/usr/src/uts/common/fs/zfs/zfs_ctldir.c	Fri Feb 22 10:55:34 2008 -0800
+++ b/usr/src/uts/common/fs/zfs/zfs_ctldir.c	Fri Feb 22 11:48:10 2008 -0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -62,7 +62,8 @@
  * (ie: snapshots) are ZFS nodes and have their own unique vfs_t.
  * However, vnodes within these mounted on file systems have their v_vfsp
  * fields set to the head filesystem to make NFS happy (see
- * zfsctl_snapdir_lookup()).
+ * zfsctl_snapdir_lookup()). We VFS_HOLD the head filesystem's vfs_t
+ * so that it cannot be freed until all snapshots have been unmounted.
  */
 
 #include <fs/fs_subr.h>
@@ -76,6 +77,18 @@
 #include <sys/dsl_deleg.h>
 #include <sys/mount.h>
 
+typedef struct zfsctl_node {
+	gfs_dir_t	zc_gfs_private;
+	uint64_t	zc_id;
+	timestruc_t	zc_cmtime;	/* ctime and mtime, always the same */
+} zfsctl_node_t;
+
+typedef struct zfsctl_snapdir {
+	zfsctl_node_t	sd_node;
+	kmutex_t	sd_lock;
+	avl_tree_t	sd_snaps;
+} zfsctl_snapdir_t;
+
 typedef struct {
 	char		*se_name;
 	vnode_t		*se_root;
@@ -107,6 +120,7 @@
 
 static vnode_t *zfsctl_mknode_snapdir(vnode_t *);
 static vnode_t *zfsctl_snapshot_mknode(vnode_t *, uint64_t objset);
+static int zfsctl_unmount_snap(zfs_snapentry_t *, int, cred_t *);
 
 static gfs_opsvec_t zfsctl_opsvec[] = {
 	{ ".zfs", zfsctl_tops_root, &zfsctl_ops_root },
@@ -115,18 +129,6 @@
 	{ NULL }
 };
 
-typedef struct zfsctl_node {
-	gfs_dir_t	zc_gfs_private;
-	uint64_t	zc_id;
-	timestruc_t	zc_cmtime;	/* ctime and mtime, always the same */
-} zfsctl_node_t;
-
-typedef struct zfsctl_snapdir {
-	zfsctl_node_t	sd_node;
-	kmutex_t	sd_lock;
-	avl_tree_t	sd_snaps;
-} zfsctl_snapdir_t;
-
 /*
  * Root directory elements.  We have only a single static entry, 'snapshot'.
  */
@@ -432,43 +434,39 @@
 	return (0);
 }
 
-int
-zfsctl_unmount_snap(vnode_t *dvp, const char *name, int force, cred_t *cr)
+static int
+zfsctl_unmount_snap(zfs_snapentry_t *sep, int fflags, cred_t *cr)
 {
-	zfsctl_snapdir_t *sdp = dvp->v_data;
-	zfs_snapentry_t search, *sep;
-	avl_index_t where;
-	int err;
+	vnode_t *svp = sep->se_root;
+	int error;
 
-	ASSERT(MUTEX_HELD(&sdp->sd_lock));
-
-	search.se_name = (char *)name;
-	if ((sep = avl_find(&sdp->sd_snaps, &search, &where)) == NULL)
-		return (ENOENT);
-
-	ASSERT(vn_ismntpt(sep->se_root));
+	ASSERT(vn_ismntpt(svp));
 
 	/* this will be dropped by dounmount() */
-	if ((err = vn_vfswlock(sep->se_root)) != 0)
-		return (err);
+	if ((error = vn_vfswlock(svp)) != 0)
+		return (error);
 
-	VN_HOLD(sep->se_root);
-	err = dounmount(vn_mountedvfs(sep->se_root), force, kcred);
-	if (err) {
-		VN_RELE(sep->se_root);
-		return (err);
+	VN_HOLD(svp);
+	error = dounmount(vn_mountedvfs(svp), fflags, cr);
+	if (error) {
+		VN_RELE(svp);
+		return (error);
 	}
-	ASSERT(sep->se_root->v_count == 1);
-	gfs_vop_inactive(sep->se_root, cr, NULL);
+	VFS_RELE(svp->v_vfsp);
+	/*
+	 * We can't use VN_RELE(), as that will try to invoke
+	 * zfsctl_snapdir_inactive(), which would cause us to destroy
+	 * the sd_lock mutex held by our caller.
+	 */
+	ASSERT(svp->v_count == 1);
+	gfs_vop_inactive(svp, cr, NULL);
 
-	avl_remove(&sdp->sd_snaps, sep);
 	kmem_free(sep->se_name, strlen(sep->se_name) + 1);
 	kmem_free(sep, sizeof (zfs_snapentry_t));
 
 	return (0);
 }
 
-
 static void
 zfsctl_rename_snap(zfsctl_snapdir_t *sdp, zfs_snapentry_t *sep, const char *nm)
 {
@@ -575,6 +573,8 @@
     caller_context_t *ct, int flags)
 {
 	zfsctl_snapdir_t *sdp = dvp->v_data;
+	zfs_snapentry_t *sep;
+	zfs_snapentry_t search;
 	char snapname[MAXNAMELEN];
 	int err;
 
@@ -587,14 +587,19 @@
 
 	mutex_enter(&sdp->sd_lock);
 
-	err = zfsctl_unmount_snap(dvp, name, MS_FORCE, cr);
-	if (err) {
-		mutex_exit(&sdp->sd_lock);
-		return (err);
+	search.se_name = name;
+	sep = avl_find(&sdp->sd_snaps, &search, NULL);
+	if (sep) {
+		avl_remove(&sdp->sd_snaps, sep);
+		err = zfsctl_unmount_snap(sep, MS_FORCE, cr);
+		if (err)
+			avl_add(&sdp->sd_snaps, sep);
+		else
+			err = dmu_objset_destroy(snapname);
+	} else {
+		err = ENOENT;
 	}
 
-	err = dmu_objset_destroy(snapname);
-
 	mutex_exit(&sdp->sd_lock);
 
 	return (err);
@@ -692,6 +697,13 @@
 			 * try to remount it.
 			 */
 			goto domount;
+		} else {
+			/*
+			 * VROOT was set during the traverse call.  We need
+			 * to clear it since we're pretending to be part
+			 * of our parent's vfs.
+			 */
+			(*vpp)->v_flag &= ~VROOT;
 		}
 		mutex_exit(&sdp->sd_lock);
 		ZFS_EXIT(zfsvfs);
@@ -914,6 +926,7 @@
 	    zfsctl_ops_snapshot, NULL, NULL, MAXNAMELEN, NULL, NULL);
 	zcp = vp->v_data;
 	zcp->zc_id = objset;
+	VFS_HOLD(vp->v_vfsp);
 
 	return (vp);
 }
@@ -952,6 +965,7 @@
 
 	mutex_exit(&sdp->sd_lock);
 	VN_RELE(dvp);
+	VFS_RELE(vp->v_vfsp);
 
 	/*
 	 * Dispose of the vnode for the snapshot mount point.
@@ -1037,7 +1051,7 @@
 zfsctl_umount_snapshots(vfs_t *vfsp, int fflags, cred_t *cr)
 {
 	zfsvfs_t *zfsvfs = vfsp->vfs_data;
-	vnode_t *dvp, *svp;
+	vnode_t *dvp;
 	zfsctl_snapdir_t *sdp;
 	zfs_snapentry_t *sep, *next;
 	int error;
@@ -1053,7 +1067,6 @@
 
 	sep = avl_first(&sdp->sd_snaps);
 	while (sep != NULL) {
-		svp = sep->se_root;
 		next = AVL_NEXT(&sdp->sd_snaps, sep);
 
 		/*
@@ -1061,32 +1074,17 @@
 		 * have just been unmounted by somebody else, and
 		 * will be cleaned up by zfsctl_snapdir_inactive().
 		 */
-		if (vn_ismntpt(svp)) {
-			if ((error = vn_vfswlock(svp)) != 0)
-				goto out;
-
-			VN_HOLD(svp);
-			error = dounmount(vn_mountedvfs(svp), fflags, cr);
+		if (vn_ismntpt(sep->se_root)) {
+			avl_remove(&sdp->sd_snaps, sep);
+			error = zfsctl_unmount_snap(sep, fflags, cr);
 			if (error) {
-				VN_RELE(svp);
-				goto out;
+				avl_add(&sdp->sd_snaps, sep);
+				break;
 			}
-
-			avl_remove(&sdp->sd_snaps, sep);
-			kmem_free(sep->se_name, strlen(sep->se_name) + 1);
-			kmem_free(sep, sizeof (zfs_snapentry_t));
-
-			/*
-			 * We can't use VN_RELE(), as that will try to
-			 * invoke zfsctl_snapdir_inactive(), and that
-			 * would lead to an attempt to re-grab the sd_lock.
-			 */
-			ASSERT3U(svp->v_count, ==, 1);
-			gfs_vop_inactive(svp, cr, NULL);
 		}
 		sep = next;
 	}
-out:
+
 	mutex_exit(&sdp->sd_lock);
 	VN_RELE(dvp);