syncer vnode leak because of nmount() race

Jaakko Heinonen jh at FreeBSD.org
Thu Jun 3 14:35:07 UTC 2010


Hi,

I have been playing with devfs and stress2 recently and I was able to
trigger a syncer vnode leak with devfs.sh. As far as I can tell it is a
nmount(2) (initial) mount race against update mount. The code in
vfs_domount() is protected with vfs_busy()/vfs_unbusy() but it doesn't
protect against update mounts. The protection by Giant is defeated
because devfs_root() may sleep due to sx_xlock(). vfs_domount() calls
VFS_ROOT() before allocating the syncer vnode. VI_MOUNT vnode flag
doesn't provide sufficient protection against update mounts either.

I created following patch to work around the problem. I am unsure if the
approach is good however.

%%%
Index: sys/kern/vfs_mount.c
===================================================================
--- sys/kern/vfs_mount.c	(revision 208667)
+++ sys/kern/vfs_mount.c	(working copy)
@@ -906,6 +906,16 @@ vfs_domount(
 			vput(vp);
 			return (EBUSY);
 		}
+		if (mp->mnt_vnodecovered != NULL) {
+			VI_LOCK(mp->mnt_vnodecovered);
+			if ((mp->mnt_vnodecovered->v_iflag & VI_MOUNT) != 0) {
+				VI_UNLOCK(mp->mnt_vnodecovered);
+				vfs_unbusy(mp);
+				vput(vp);
+				return (EBUSY);
+			}
+			VI_UNLOCK(mp->mnt_vnodecovered);
+		}
 		VI_LOCK(vp);
 		if ((vp->v_iflag & VI_MOUNT) != 0 ||
 		    vp->v_mountedhere != NULL) {
@@ -1060,17 +1070,10 @@ vfs_domount(
 	 * Put the new filesystem on the mount list after root.
 	 */
 	cache_purge(vp);
-	VI_LOCK(vp);
-	vp->v_iflag &= ~VI_MOUNT;
-	VI_UNLOCK(vp);
 	if (!error) {
 		struct vnode *newdp;
 
 		vp->v_mountedhere = mp;
-		mtx_lock(&mountlist_mtx);
-		TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list);
-		mtx_unlock(&mountlist_mtx);
-		vfs_event_signal(NULL, VQ_MOUNT, 0);
 		if (VFS_ROOT(mp, LK_EXCLUSIVE, &newdp))
 			panic("mount: lost mount");
 		VOP_UNLOCK(newdp, 0);
@@ -1079,10 +1082,20 @@ vfs_domount(
 		vrele(newdp);
 		if ((mp->mnt_flag & MNT_RDONLY) == 0)
 			error = vfs_allocate_syncvnode(mp);
+		VI_LOCK(vp);
+		vp->v_iflag &= ~VI_MOUNT;
+		VI_UNLOCK(vp);
+		mtx_lock(&mountlist_mtx);
+		TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list);
+		mtx_unlock(&mountlist_mtx);
+		vfs_event_signal(NULL, VQ_MOUNT, 0);
 		vfs_unbusy(mp);
 		if (error)
 			vrele(vp);
 	} else {
+		VI_LOCK(vp);
+		vp->v_iflag &= ~VI_MOUNT;
+		VI_UNLOCK(vp);
 		vfs_unbusy(mp);
 		vfs_mount_destroy(mp);
 		vput(vp);
%%%

Comments?

PS. vfs_unbusy(9) manual page is out of date after r184554 and IMO
    vfs_busy(9) manual page is misleading because it talks about
    synchronizing access to a mount point.

-- 
Jaakko


More information about the freebsd-fs mailing list