Author: kib
Date: Sun Jan  1 18:45:59 2012
New Revision: 229185
URL: http://svn.freebsd.org/changeset/base/229185

Log:
  Avoid double-unlock or double unreference for ndp->ni_dvp when the vnode dp
  lock upgrade right after the 'success' label fails.
  
  In collaboration with:        pho
  MFC after:    1 week

Modified:
  head/sys/kern/vfs_lookup.c

Modified: head/sys/kern/vfs_lookup.c
==============================================================================
--- head/sys/kern/vfs_lookup.c  Sun Jan  1 18:42:00 2012        (r229184)
+++ head/sys/kern/vfs_lookup.c  Sun Jan  1 18:45:59 2012        (r229185)
@@ -508,12 +508,14 @@ lookup(struct nameidata *ndp)
        int dvfslocked;                 /* VFS Giant state for parent */
        int tvfslocked;
        int lkflags_save;
+       int ni_dvp_unlocked;
        
        /*
         * Setup: break out flag bits into variables.
         */
        dvfslocked = (ndp->ni_cnd.cn_flags & GIANTHELD) != 0;
        vfslocked = 0;
+       ni_dvp_unlocked = 0;
        ndp->ni_cnd.cn_flags &= ~GIANTHELD;
        wantparent = cnp->cn_flags & (LOCKPARENT | WANTPARENT);
        KASSERT(cnp->cn_nameiop == LOOKUP || wantparent,
@@ -861,8 +863,10 @@ unionlookup:
                /*
                 * Symlink code always expects an unlocked dvp.
                 */
-               if (ndp->ni_dvp != ndp->ni_vp)
+               if (ndp->ni_dvp != ndp->ni_vp) {
                        VOP_UNLOCK(ndp->ni_dvp, 0);
+                       ni_dvp_unlocked = 1;
+               }
                goto success;
        }
 
@@ -909,14 +913,17 @@ nextname:
                VREF(ndp->ni_startdir);
        }
        if (!wantparent) {
+               ni_dvp_unlocked = 2;
                if (ndp->ni_dvp != dp)
                        vput(ndp->ni_dvp);
                else
                        vrele(ndp->ni_dvp);
                VFS_UNLOCK_GIANT(dvfslocked);
                dvfslocked = 0;
-       } else if ((cnp->cn_flags & LOCKPARENT) == 0 && ndp->ni_dvp != dp)
+       } else if ((cnp->cn_flags & LOCKPARENT) == 0 && ndp->ni_dvp != dp) {
                VOP_UNLOCK(ndp->ni_dvp, 0);
+               ni_dvp_unlocked = 1;
+       }
 
        if (cnp->cn_flags & AUDITVNODE1)
                AUDIT_ARG_VNODE1(dp);
@@ -945,10 +952,12 @@ success:
        return (0);
 
 bad2:
-       if (dp != ndp->ni_dvp)
-               vput(ndp->ni_dvp);
-       else
-               vrele(ndp->ni_dvp);
+       if (ni_dvp_unlocked != 2) {
+               if (dp != ndp->ni_dvp && !ni_dvp_unlocked)
+                       vput(ndp->ni_dvp);
+               else
+                       vrele(ndp->ni_dvp);
+       }
 bad:
        if (!dpunlocked)
                vput(dp);
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to