commit 472ed4b23f9d01fa08e06aabcb604062ffe8681c
Author: Richard Kojedzinszky <krichy@cflinux.hu>
Date:   Wed Dec 25 06:07:41 2013 +0100

    ZFS/GFS locking fixes

diff --git a/sys/cddl/compat/opensolaris/kern/opensolaris_lookup.c b/sys/cddl/compat/opensolaris/kern/opensolaris_lookup.c
index 6565810..74ed173 100644
--- a/sys/cddl/compat/opensolaris/kern/opensolaris_lookup.c
+++ b/sys/cddl/compat/opensolaris/kern/opensolaris_lookup.c
@@ -81,6 +81,8 @@ traverse(vnode_t **cvpp, int lktype)
 	 * progress on this vnode.
 	 */
 
+	vn_lock(cvp, lktype);
+
 	for (;;) {
 		/*
 		 * Reached the end of the mount chain?
@@ -89,13 +91,7 @@ traverse(vnode_t **cvpp, int lktype)
 		if (vfsp == NULL)
 			break;
 		error = vfs_busy(vfsp, 0);
-		/*
-		 * tvp is NULL for *cvpp vnode, which we can't unlock.
-		 */
-		if (tvp != NULL)
-			vput(cvp);
-		else
-			vrele(cvp);
+		VOP_UNLOCK(cvp, 0);
 		if (error)
 			return (error);
 
@@ -107,6 +103,9 @@ traverse(vnode_t **cvpp, int lktype)
 		vfs_unbusy(vfsp);
 		if (error != 0)
 			return (error);
+
+		VN_RELE(cvp);
+
 		cvp = tvp;
 	}
 
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/gfs.c b/sys/cddl/contrib/opensolaris/uts/common/fs/gfs.c
index 59944a1..0f8f157 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/gfs.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/gfs.c
@@ -448,7 +448,6 @@ gfs_lookup_dot(vnode_t **vpp, vnode_t *dvp, vnode_t *pvp, const char *nm)
 			VN_HOLD(pvp);
 			*vpp = pvp;
 		}
-		vn_lock(*vpp, LK_EXCLUSIVE | LK_RETRY);
 		return (0);
 	}
 
@@ -485,6 +484,7 @@ gfs_file_create(size_t size, vnode_t *pvp, vfs_t *vfsp, vnodeops_t *ops)
 	fp = kmem_zalloc(size, KM_SLEEP);
 	error = getnewvnode("zfs", vfsp, ops, &vp);
 	ASSERT(error == 0);
+	VN_LOCK_ASHARE(vp);
 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
 	vp->v_data = (caddr_t)fp;
 
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ctldir.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ctldir.c
index 28ab1fa..6806858 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ctldir.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ctldir.c
@@ -612,7 +612,7 @@ zfsctl_freebsd_root_lookup(ap)
 
 	err = zfsctl_root_lookup(dvp, nm, vpp, NULL, 0, NULL, cr, NULL, NULL, NULL);
 	if (err == 0 && (nm[0] != '.' || nm[1] != '\0'))
-		vn_lock(*vpp, LK_EXCLUSIVE | LK_RETRY);
+		vn_lock(*vpp, ap->a_cnp->cn_lkflags | LK_RETRY);
 	return (err);
 }
 
@@ -975,6 +975,8 @@ zfsctl_snapdir_lookup(ap)
 	ZFS_ENTER(zfsvfs);
 
 	if (gfs_lookup_dot(vpp, dvp, zfsvfs->z_ctldir, nm) == 0) {
+		if (nm[0] != '.' || nm[1] != '\0')
+			vn_lock(*vpp, ap->a_cnp->cn_lkflags | LK_RETRY);
 		ZFS_EXIT(zfsvfs);
 		return (0);
 	}
@@ -1004,7 +1006,7 @@ zfsctl_snapdir_lookup(ap)
 	if ((sep = avl_find(&sdp->sd_snaps, &search, &where)) != NULL) {
 		*vpp = sep->se_root;
 		VN_HOLD(*vpp);
-		err = traverse(vpp, LK_EXCLUSIVE | LK_RETRY);
+		err = traverse(vpp, ap->a_cnp->cn_lkflags | LK_RETRY);
 		if (err != 0) {
 			VN_RELE(*vpp);
 			*vpp = NULL;
@@ -1013,6 +1015,8 @@ zfsctl_snapdir_lookup(ap)
 			 * The snapshot was unmounted behind our backs,
 			 * try to remount it.
 			 */
+			VOP_UNLOCK(*vpp, 0);
+			VN_HOLD(*vpp);
 			VERIFY(zfsctl_snapshot_zname(dvp, nm, MAXNAMELEN, snapname) == 0);
 			goto domount;
 		} else {
@@ -1064,7 +1068,6 @@ zfsctl_snapdir_lookup(ap)
 	sep->se_name = kmem_alloc(strlen(nm) + 1, KM_SLEEP);
 	(void) strcpy(sep->se_name, nm);
 	*vpp = sep->se_root = zfsctl_snapshot_mknode(dvp, dmu_objset_id(snap));
-	VN_HOLD(*vpp);
 	avl_insert(&sdp->sd_snaps, sep, where);
 
 	dmu_objset_rele(snap, FTAG);
@@ -1075,6 +1078,7 @@ domount:
 	(void) snprintf(mountpoint, mountpoint_len,
 	    "%s/" ZFS_CTLDIR_NAME "/snapshot/%s",
 	    dvp->v_vfsp->mnt_stat.f_mntonname, nm);
+	VN_HOLD(*vpp);
 	err = mount_snapshot(curthread, vpp, "zfs", mountpoint, snapname, 0);
 	kmem_free(mountpoint, mountpoint_len);
 	if (err == 0) {
@@ -1130,6 +1134,8 @@ zfsctl_shares_lookup(ap)
 	strlcpy(nm, cnp->cn_nameptr, cnp->cn_namelen + 1);
 
 	if (gfs_lookup_dot(vpp, dvp, zfsvfs->z_ctldir, nm) == 0) {
+		if (nm[0] != '.' || nm[1] != '\0')
+			vn_lock(*vpp, ap->a_cnp->cn_lkflags | LK_RETRY);
 		ZFS_EXIT(zfsvfs);
 		return (0);
 	}
@@ -1462,18 +1468,25 @@ zfsctl_snapshot_inactive(ap)
 	zfsctl_snapdir_t *sdp;
 	zfs_snapentry_t *sep, *next;
 	int locked;
-	vnode_t *dvp;
-
-	if (vp->v_count > 0)
-		goto end;
+	gfs_dir_t *dp = vp->v_data;
+	vnode_t *dvp = dp->gfsd_file.gfs_parent;
 
-	VERIFY(gfs_dir_lookup(vp, "..", &dvp, cr, 0, NULL, NULL) == 0);
+	VN_HOLD(dvp);
+	VOP_UNLOCK(vp, 0);
 	sdp = dvp->v_data;
-	VOP_UNLOCK(dvp, 0);
 
 	if (!(locked = MUTEX_HELD(&sdp->sd_lock)))
 		mutex_enter(&sdp->sd_lock);
 
+	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
+
+	if (vp->v_count > 0) {
+		if (!locked)
+			mutex_exit(&sdp->sd_lock);
+		VN_RELE(dvp);
+		return(0);
+	}
+
 	ASSERT(!vn_ismntpt(vp));
 
 	sep = avl_first(&sdp->sd_snaps);
@@ -1494,7 +1507,6 @@ zfsctl_snapshot_inactive(ap)
 		mutex_exit(&sdp->sd_lock);
 	VN_RELE(dvp);
 
-end:
 	/*
 	 * Dispose of the vnode for the snapshot mount point.
 	 * This is safe to do because once this entry has been removed
@@ -1588,7 +1600,7 @@ zfsctl_snapshot_lookup(ap)
 	error = zfsctl_root_lookup(zfsvfs->z_ctldir, "snapshot", vpp,
 	    NULL, 0, NULL, cr, NULL, NULL, NULL);
 	if (error == 0)
-		vn_lock(*vpp, LK_EXCLUSIVE | LK_RETRY);
+		vn_lock(*vpp, ap->a_cnp->cn_lkflags | LK_RETRY);
 	return (error);
 }
 
