svn commit: r186197 - in head/sys: kern sys

Attilio Rao attilio at FreeBSD.org
Tue Dec 16 23:16:11 UTC 2008


Author: attilio
Date: Tue Dec 16 23:16:10 2008
New Revision: 186197
URL: http://svn.freebsd.org/changeset/base/186197

Log:
  1) Fix a deadlock in the VFS:
  - threadA runs vfs_rel(mp1)
  - threadB does unmount the mp1 fs, sets MNTK_UNMOUNT and drop MNT_ILOCK()
  - threadA runs vfs_busy(mp1) and, as long as, MNTK_UNMOUNT is set, sleeps
    waiting for threadB to complete the unmount
  - threadB, in vfs_mount_destroy(), finds mnt_lock > 0 and sleeps waiting
    for the refcount to expire.
  
  Fix the deadlock by adding a flag called MNTK_REFEXPIRE which signals the
  unmounter is waiting for mnt_ref to expire.
  The vfs_busy contenders got awake, fails, and if they retry the
  MNTK_REFEXPIRE won't allow them to sleep again.
  
  2) Simplify significantly the code of vfs_mount_destroy() trimming
     unnecessary codes:
     - as long as any reference exited, it is no-more possible to have
       write-op (primarty and secondary) in progress.
     - it is no needed to drop and reacquire the mount lock.
     - filling the structures with dummy values is unuseful as long as
       it is going to be freed.
  
  Tested by:	pho, Andrea Barberio <insomniac at slackware dot it>
  Discussed with:	kib

Modified:
  head/sys/kern/vfs_mount.c
  head/sys/kern/vfs_subr.c
  head/sys/sys/mount.h

Modified: head/sys/kern/vfs_mount.c
==============================================================================
--- head/sys/kern/vfs_mount.c	Tue Dec 16 23:06:36 2008	(r186196)
+++ head/sys/kern/vfs_mount.c	Tue Dec 16 23:16:10 2008	(r186197)
@@ -507,29 +507,20 @@ vfs_mount_destroy(struct mount *mp)
 {
 
 	MNT_ILOCK(mp);
+	mp->mnt_kern_flag |= MNTK_REFEXPIRE;
+	if (mp->mnt_kern_flag & MNTK_MWAIT) {
+		mp->mnt_kern_flag &= ~MNTK_MWAIT;
+		wakeup(mp);
+	}
 	while (mp->mnt_ref)
 		msleep(mp, MNT_MTX(mp), PVFS, "mntref", 0);
-	if (mp->mnt_writeopcount > 0) {
-		printf("Waiting for mount point write ops\n");
-		while (mp->mnt_writeopcount > 0) {
-			mp->mnt_kern_flag |= MNTK_SUSPEND;
-			msleep(&mp->mnt_writeopcount,
-			       MNT_MTX(mp),
-			       PZERO, "mntdestroy2", 0);
-		}
-		printf("mount point write ops completed\n");
-	}
-	if (mp->mnt_secondary_writes > 0) {
-		printf("Waiting for mount point secondary write ops\n");
-		while (mp->mnt_secondary_writes > 0) {
-			mp->mnt_kern_flag |= MNTK_SUSPEND;
-			msleep(&mp->mnt_secondary_writes,
-			       MNT_MTX(mp),
-			       PZERO, "mntdestroy3", 0);
-		}
-		printf("mount point secondary write ops completed\n");
-	}
-	MNT_IUNLOCK(mp);
+	KASSERT(mp->mnt_ref == 0,
+	    ("%s: invalid refcount in the drain path @ %s:%d", __func__,
+	    __FILE__, __LINE__));
+	if (mp->mnt_writeopcount != 0)
+		panic("vfs_mount_destroy: nonzero writeopcount");
+	if (mp->mnt_secondary_writes != 0)
+		panic("vfs_mount_destroy: nonzero secondary_writes");
 	mp->mnt_vfc->vfc_refcount--;
 	if (!TAILQ_EMPTY(&mp->mnt_nvnodelist)) {
 		struct vnode *vp;
@@ -538,18 +529,10 @@ vfs_mount_destroy(struct mount *mp)
 			vprint("", vp);
 		panic("unmount: dangling vnode");
 	}
-	MNT_ILOCK(mp);
-	if (mp->mnt_kern_flag & MNTK_MWAIT)
-		wakeup(mp);
-	if (mp->mnt_writeopcount != 0)
-		panic("vfs_mount_destroy: nonzero writeopcount");
-	if (mp->mnt_secondary_writes != 0)
-		panic("vfs_mount_destroy: nonzero secondary_writes");
 	if (mp->mnt_nvnodelistsize != 0)
 		panic("vfs_mount_destroy: nonzero nvnodelistsize");
-	mp->mnt_writeopcount = -1000;
-	mp->mnt_nvnodelistsize = -1000;
-	mp->mnt_secondary_writes = -1000;
+	if (mp->mnt_lockref != 0)
+		panic("vfs_mount_destroy: nonzero lock refcount");
 	MNT_IUNLOCK(mp);
 #ifdef MAC
 	mac_mount_destroy(mp);

Modified: head/sys/kern/vfs_subr.c
==============================================================================
--- head/sys/kern/vfs_subr.c	Tue Dec 16 23:06:36 2008	(r186196)
+++ head/sys/kern/vfs_subr.c	Tue Dec 16 23:16:10 2008	(r186197)
@@ -345,7 +345,7 @@ vfs_busy(struct mount *mp, int flags)
 	MNT_ILOCK(mp);
 	MNT_REF(mp);
 	if (mp->mnt_kern_flag & MNTK_UNMOUNT) {
-		if (flags & MBF_NOWAIT) {
+		if (flags & MBF_NOWAIT || mp->mnt_kern_flag & MNTK_REFEXPIRE) {
 			MNT_REL(mp);
 			MNT_IUNLOCK(mp);
 			return (ENOENT);

Modified: head/sys/sys/mount.h
==============================================================================
--- head/sys/sys/mount.h	Tue Dec 16 23:06:36 2008	(r186196)
+++ head/sys/sys/mount.h	Tue Dec 16 23:16:10 2008	(r186197)
@@ -316,6 +316,7 @@ void          __mnt_vnode_markerfree(str
 #define MNTK_SOFTDEP	0x00000004	/* async disabled by softdep */
 #define MNTK_NOINSMNTQ	0x00000008	/* insmntque is not allowed */
 #define	MNTK_DRAINING	0x00000010	/* lock draining is happening */
+#define	MNTK_REFEXPIRE	0x00000020	/* refcount expiring is happening */
 #define MNTK_UNMOUNT	0x01000000	/* unmount in progress */
 #define	MNTK_MWAIT	0x02000000	/* waiting for unmount to finish */
 #define	MNTK_SUSPEND	0x08000000	/* request write suspension */


More information about the svn-src-head mailing list