Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys Fix kern/7906: race between unmount and getnewvnode()



details:   https://anonhg.NetBSD.org/src/rev/4ec7de3047da
branches:  trunk
changeset: 474375:4ec7de3047da
user:      sommerfeld <sommerfeld%NetBSD.org@localhost>
date:      Sun Jul 04 16:20:12 1999 +0000

description:
Fix kern/7906: race between unmount and getnewvnode()

mp->mnt_flags & MNT_MWAIT is replaced by mp->mnt_wcnt, and a new mount
flag MNT_GONE is created (reusing the same bit).

In insmntque(), add DIAGNOSTIC check to fail if the filesystem vnode
is being moved to is in the process of being unmounted.

getnewvnode() now protects the list of vnodes active on mp with
vfs_busy()/vfs_unbusy().

To avoid generating spurious errors during a doomed unmount, change
the "wait for unmount to finish" protocol between dounmount() and
vfs_busy().  In vfs_busy(), instead of only sleeping once, sleep until
either MNT_UNMOUNT is clear or MNT_GONE is set; also, maintain a count
of waiters in mp->mnt_wcnt so that dounmount() knows when it's safe to
free mp.

tested by running a "while :; do mount /d1; umount -f /d1; done" loop
against multiple find(1) processes.

diffstat:

 sys/kern/vfs_subr.c     |  40 ++++++++++++++++++++++++++++++++++++----
 sys/kern/vfs_syscalls.c |  13 +++++++++----
 sys/sys/mount.h         |   5 +++--
 3 files changed, 48 insertions(+), 10 deletions(-)

diffs (161 lines):

diff -r ac076e04adf6 -r 4ec7de3047da sys/kern/vfs_subr.c
--- a/sys/kern/vfs_subr.c       Sun Jul 04 16:07:00 1999 +0000
+++ b/sys/kern/vfs_subr.c       Sun Jul 04 16:20:12 1999 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: vfs_subr.c,v 1.102 1999/04/21 02:37:07 mrg Exp $       */
+/*     $NetBSD: vfs_subr.c,v 1.103 1999/07/04 16:20:13 sommerfeld Exp $        */
 
 /*-
  * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
@@ -198,10 +198,11 @@
 {
        int lkflags;
 
-       if (mp->mnt_flag & MNT_UNMOUNT) {
+       while (mp->mnt_flag & MNT_UNMOUNT) {
+               int gone;
+               
                if (flags & LK_NOWAIT)
                        return (ENOENT);
-               mp->mnt_flag |= MNT_MWAIT;
                if (interlkp)
                        simple_unlock(interlkp);
                /*
@@ -209,11 +210,20 @@
                 * lock granted when unmounting, the only place that a
                 * wakeup needs to be done is at the release of the
                 * exclusive lock at the end of dounmount.
+                *
+                * XXX MP: add spinlock protecting mnt_wcnt here.
                 */
+               mp->mnt_wcnt++;
                sleep((caddr_t)mp, PVFS);
+               mp->mnt_wcnt--;
+               gone = mp->mnt_flag & MNT_GONE;
+               
+               if (mp->mnt_wcnt == 0)
+                       wakeup(&mp->mnt_wcnt);
                if (interlkp)
                        simple_lock(interlkp);
-               return (ENOENT);
+               if (gone)
+                       return (ENOENT);
        }
        lkflags = LK_SHARED;
        if (interlkp)
@@ -394,9 +404,22 @@
 {
        struct proc *p = curproc;       /* XXX */
        struct vnode *vp;
+       int error;
 #ifdef DIAGNOSTIC
        int s;
 #endif
+       if (mp) {
+               /*
+                * XXX
+                * calling vfs_busy here (either with or without LK_NOWAIT)
+                * means that syscalls taking place during an
+                * unsuccessful unmount attempt will fail (spuriously).
+                * We should be able to wait for the unmount to finish.
+                */
+               error = vfs_busy(mp, LK_NOWAIT, 0);
+               if (error)
+                       return error;
+       }
 
        simple_lock(&vnode_free_list_slock);
        if ((vnode_free_list.tqh_first == NULL &&
@@ -419,6 +442,7 @@
                 */
                if (vp == NULLVP) {
                        simple_unlock(&vnode_free_list_slock);
+                       if (mp) vfs_unbusy(mp);
                        tablefull("vnode");
                        *vpp = 0;
                        return (ENFILE);
@@ -461,6 +485,7 @@
        vp->v_usecount = 1;
        vp->v_data = 0;
        simple_lock_init(&vp->v_uvm.u_obj.vmobjlock);
+       if (mp) vfs_unbusy(mp);
        return (0);
 }
 
@@ -473,6 +498,13 @@
        register struct mount *mp;
 {
 
+#ifdef DIAGNOSTIC
+       if ((mp != NULL) &&
+           (mp->mnt_flag & MNT_UNMOUNT)) {
+               panic("insmntque into dying filesystem");
+       }
+#endif
+       
        simple_lock(&mntvnode_slock);
        /*
         * Delete from old mount point vnode list, if on one.
diff -r ac076e04adf6 -r 4ec7de3047da sys/kern/vfs_syscalls.c
--- a/sys/kern/vfs_syscalls.c   Sun Jul 04 16:07:00 1999 +0000
+++ b/sys/kern/vfs_syscalls.c   Sun Jul 04 16:20:12 1999 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: vfs_syscalls.c,v 1.141 1999/07/04 06:17:52 sommerfeld Exp $    */
+/*     $NetBSD: vfs_syscalls.c,v 1.142 1999/07/04 16:20:13 sommerfeld Exp $    */
 
 /*
  * Copyright (c) 1989, 1993
@@ -478,9 +478,11 @@
                mp->mnt_flag |= async;
                lockmgr(&mp->mnt_lock, LK_RELEASE | LK_INTERLOCK | LK_REENABLE,
                    &mountlist_slock);
-               if (mp->mnt_flag & MNT_MWAIT)
+               while(mp->mnt_wcnt > 0) {
                        wakeup((caddr_t)mp);
-                return (error);
+                       sleep(&mp->mnt_wcnt, PVFS);
+               }
+               return (error);
        }
        CIRCLEQ_REMOVE(&mountlist, mp, mnt_list);
        if ((coveredvp = mp->mnt_vnodecovered) != NULLVP) {
@@ -490,9 +492,12 @@
        mp->mnt_op->vfs_refcount--;
        if (mp->mnt_vnodelist.lh_first != NULL)
                panic("unmount: dangling vnode");
+       mp->mnt_flag |= MNT_GONE;
        lockmgr(&mp->mnt_lock, LK_RELEASE | LK_INTERLOCK, &mountlist_slock);
-       if (mp->mnt_flag & MNT_MWAIT)
+       while(mp->mnt_wcnt > 0) {
                wakeup((caddr_t)mp);
+               sleep(&mp->mnt_wcnt, PVFS);
+       }
        free((caddr_t)mp, M_MOUNT);
        return (0);
 }
diff -r ac076e04adf6 -r 4ec7de3047da sys/sys/mount.h
--- a/sys/sys/mount.h   Sun Jul 04 16:07:00 1999 +0000
+++ b/sys/sys/mount.h   Sun Jul 04 16:20:12 1999 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: mount.h,v 1.76 1999/06/29 22:18:48 wrstuden Exp $      */
+/*     $NetBSD: mount.h,v 1.77 1999/07/04 16:20:12 sommerfeld Exp $    */
 
 /*
  * Copyright (c) 1989, 1991, 1993
@@ -128,6 +128,7 @@
        int             mnt_maxsymlinklen;      /* max size of short symlink */
        struct statfs   mnt_stat;               /* cache of filesystem stats */
        qaddr_t         mnt_data;               /* private data */
+       int             mnt_wcnt;               /* count of vfs_busy waiters */
 };
 
 /*
@@ -200,7 +201,7 @@
  * past the mount point.  This keeps the subtree stable during mounts
  * and unmounts.
  */
-#define        MNT_MWAIT       0x00200000      /* waiting for unmount to finish */
+#define        MNT_GONE        0x00200000      /* filesystem is gone.. */
 #define MNT_UNMOUNT    0x01000000      /* unmount in progress */
 #define MNT_WANTRDWR   0x02000000      /* upgrade to read/write requested */
 



Home | Main Index | Thread Index | Old Index