Module Name:    src
Committed By:   hannken
Date:           Tue Jun 15 09:43:37 UTC 2010

Modified Files:
        src/sys/kern: vfs_syscalls.c

Log Message:
When mounting a file system re-lookup and lock the directory we mount on
after the file system is setup by VFS_MOUNT().  This way recursive vnode
locks are no longer needed here and mounts on null mounts no longer fail
as described in PR #43439 (mount_null panic: lockdebug_wantlock: locking
against myself).

Based on a proposal from  and
Reviewed by: David A. Holland <dholl...@netbsd.org>


To generate a diff of this commit:
cvs rdiff -u -r1.404 -r1.405 src/sys/kern/vfs_syscalls.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/kern/vfs_syscalls.c
diff -u src/sys/kern/vfs_syscalls.c:1.404 src/sys/kern/vfs_syscalls.c:1.405
--- src/sys/kern/vfs_syscalls.c:1.404	Wed Mar  3 00:47:31 2010
+++ src/sys/kern/vfs_syscalls.c	Tue Jun 15 09:43:36 2010
@@ -1,4 +1,4 @@
-/*	$NetBSD: vfs_syscalls.c,v 1.404 2010/03/03 00:47:31 yamt Exp $	*/
+/*	$NetBSD: vfs_syscalls.c,v 1.405 2010/06/15 09:43:36 hannken Exp $	*/
 
 /*-
  * Copyright (c) 2008, 2009 The NetBSD Foundation, Inc.
@@ -66,7 +66,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: vfs_syscalls.c,v 1.404 2010/03/03 00:47:31 yamt Exp $");
+__KERNEL_RCSID(0, "$NetBSD: vfs_syscalls.c,v 1.405 2010/06/15 09:43:36 hannken Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_fileassoc.h"
@@ -290,11 +290,12 @@
 
 static int
 mount_domount(struct lwp *l, struct vnode **vpp, struct vfsops *vfsops,
-    const char *path, int flags, void *data, size_t *data_len, u_int recurse)
+    const char *path, int flags, void *data, size_t *data_len)
 {
 	struct mount *mp;
 	struct vnode *vp = *vpp;
 	struct vattr va;
+	struct nameidata nd;
 	int error;
 
 	error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_MOUNT,
@@ -327,19 +328,6 @@
 		return EINVAL;
 	}
 
-	if ((error = vinvalbuf(vp, V_SAVE, l->l_cred, l, 0, 0)) != 0) {
-		vfs_delref(vfsops);
-		return error;
-	}
-
-	/*
-	 * Check if a file-system is not already mounted on this vnode.
-	 */
-	if (vp->v_mountedhere != NULL) {
-		vfs_delref(vfsops);
-		return EBUSY;
-	}
-
 	if ((mp = vfs_mountalloc(vfsops, vp)) == NULL) {
 		vfs_delref(vfsops);
 		return ENOMEM;
@@ -363,30 +351,55 @@
 	error = VFS_MOUNT(mp, path, data, data_len);
 	mp->mnt_flag &= ~MNT_OP_FLAGS;
 
+	if (error != 0)
+		goto err_unmounted;
+
 	/*
-	 * Put the new filesystem on the mount list after root.
+	 * Validate and prepare the mount point.
 	 */
-	cache_purge(vp);
+	NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | TRYEMULROOT,
+	    UIO_USERSPACE, path);
+	error = namei(&nd);
 	if (error != 0) {
-		vp->v_mountedhere = NULL;
-		mutex_exit(&mp->mnt_updating);
-		vfs_unbusy(mp, false, NULL);
-		vfs_destroy(mp);
-		return error;
+		goto err_mounted;
+	}
+	if (nd.ni_vp != vp) {
+		vput(nd.ni_vp);
+		error = EINVAL;
+		goto err_mounted;
+	}
+	if (vp->v_mountedhere != NULL) {
+		vput(nd.ni_vp);
+		error = EBUSY;
+		goto err_mounted;
+	}
+	error = vinvalbuf(vp, V_SAVE, l->l_cred, l, 0, 0);
+	if (error != 0) {
+		vput(nd.ni_vp);
+		goto err_mounted;
 	}
 
+	/*
+	 * Put the new filesystem on the mount list after root.
+	 */
+	cache_purge(vp);
 	mp->mnt_iflag &= ~IMNT_WANTRDWR;
+
 	mutex_enter(&mountlist_lock);
-	vp->v_mountedhere = mp;
 	CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list);
 	mutex_exit(&mountlist_lock);
-    	vn_restorerecurse(vp, recurse);
-	VOP_UNLOCK(vp, 0);
-	checkdirs(vp);
 	if ((mp->mnt_flag & (MNT_RDONLY | MNT_ASYNC)) == 0)
 		error = vfs_allocate_syncvnode(mp);
-	/* Hold an additional reference to the mount across VFS_START(). */
+	if (error == 0)
+		vp->v_mountedhere = mp;
+	vput(nd.ni_vp);
+	if (error != 0)
+		goto err_onmountlist;
+
+	checkdirs(vp);
 	mutex_exit(&mp->mnt_updating);
+
+	/* Hold an additional reference to the mount across VFS_START(). */
 	vfs_unbusy(mp, true, NULL);
 	(void) VFS_STATVFS(mp, &mp->mnt_stat);
 	error = VFS_START(mp, 0);
@@ -396,6 +409,24 @@
 	vfs_destroy(mp);
 	*vpp = NULL;
 	return error;
+
+err_onmountlist:
+	mutex_enter(&mountlist_lock);
+	CIRCLEQ_REMOVE(&mountlist, mp, mnt_list);
+	mp->mnt_iflag |= IMNT_GONE;
+	mutex_exit(&mountlist_lock);
+
+err_mounted:
+	if (VFS_UNMOUNT(mp, MNT_FORCE) != 0)
+		panic("Unmounting fresh file system failed");
+
+err_unmounted:
+	vp->v_mountedhere = NULL;
+	mutex_exit(&mp->mnt_updating);
+	vfs_unbusy(mp, false, NULL);
+	vfs_destroy(mp);
+
+	return error;
 }
 
 static int
@@ -457,7 +488,6 @@
 {
 	struct vnode *vp;
 	void *data_buf = data;
-	u_int recurse;
 	bool vfsopsrele = false;
 	int error;
 
@@ -470,33 +500,20 @@
 	 */
 	error = namei_simple_user(path, NSM_FOLLOW_TRYEMULROOT, &vp);
 	if (error != 0) {
-		/* XXXgcc */
 		vp = NULL;
-		recurse = 0;
 		goto done;
 	}
 
-	/*
-	 * A lookup in VFS_MOUNT might result in an attempt to
-	 * lock this vnode again, so make the lock recursive.
-	 */
 	if (vfsops == NULL) {
 		if (flags & (MNT_GETARGS | MNT_UPDATE)) {
-			vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
-			recurse = vn_setrecurse(vp);
 			vfsops = vp->v_mount->mnt_op;
 		} else {
 			/* 'type' is userspace */
 			error = mount_get_vfsops(type, &vfsops);
-			vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
-			recurse = vn_setrecurse(vp);
 			if (error != 0)
 				goto done;
 			vfsopsrele = true;
 		}
-	} else {
-		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
-		recurse = vn_setrecurse(vp);
 	}
 
 	if (data != NULL && data_seg == UIO_USERSPACE) {
@@ -540,7 +557,7 @@
 		/* Locking is handled internally in mount_domount(). */
 		KASSERT(vfsopsrele == true);
 		error = mount_domount(l, &vp, vfsops, path, flags, data_buf,
-		    &data_len, recurse);
+		    &data_len);
 		vfsopsrele = false;
 	}
 
@@ -548,8 +565,7 @@
 	if (vfsopsrele)
 		vfs_delref(vfsops);
     	if (vp != NULL) {
-	    	vn_restorerecurse(vp, recurse);
-	    	vput(vp);
+	    	vrele(vp);
 	}
 	if (data_buf != data)
 		kmem_free(data_buf, data_len);

Reply via email to