]> git.michaelhowe.org Git - packages/o/openafs.git/commitdiff
FBSD: close race in afs_root
authorBen Kaduk <kaduk@mit.edu>
Sat, 6 Nov 2010 04:02:31 +0000 (00:02 -0400)
committerDerrick Brashear <shadow@dementia.org>
Mon, 10 Jan 2011 04:28:15 +0000 (20:28 -0800)
Previously, we called afs_PutVCache(afs_globalVp) directly.  This
is unsafe because PutVCache acquires locks which can sleep, losing
the serialization of the GLOCK.  In rare circumstances, this can
result in two threads simultaneously making that call, and the
second one would panic in vputx() with a negative refcount.
Close the race by using a local variable for the afs_PutVCache()
calls, applying the change to afs_globalVp before dropping the GLOCK.
While here, fix up other race conditions.

Change-Id: I4733489d50d3459172ee2eb021190d013f68018a
Reviewed-on: http://gerrit.openafs.org/3275
Tested-by: BuildBot <buildbot@rampaginggeek.com>
Reviewed-by: Derrick Brashear <shadow@dementia.org>
Reviewed-on: http://gerrit.openafs.org/3631
Tested-by: Derrick Brashear <shadow@dementia.org>
src/afs/FBSD/osi_vfsops.c

index 32a8feb43a5d0d21d320986af335d956104f8578..b956b8090c313a4ef415071a0f0ebd0b693447a3 100644 (file)
@@ -242,6 +242,7 @@ afs_root(struct mount *mp, struct vnode **vpp)
     int error;
     struct vrequest treq;
     struct vcache *tvp = 0;
+    struct vcache *gvp;
 #if !defined(AFS_FBSD53_ENV) || defined(AFS_FBSD80_ENV)
     struct thread *td = curthread;
 #endif
@@ -250,22 +251,26 @@ afs_root(struct mount *mp, struct vnode **vpp)
     AFS_GLOCK();
     AFS_STATCNT(afs_root);
     crhold(cr);
+tryagain:
     if (afs_globalVp && (afs_globalVp->f.states & CStatd)) {
        tvp = afs_globalVp;
        error = 0;
     } else {
-tryagain:
-       if (afs_globalVp) {
-           afs_PutVCache(afs_globalVp);
-           /* vrele() needed here or not? */
-           afs_globalVp = NULL;
-       }
        if (!(error = afs_InitReq(&treq, cr)) && !(error = afs_CheckInit())) {
            tvp = afs_GetVCache(&afs_rootFid, &treq, NULL, NULL);
            /* we really want this to stay around */
-           if (tvp)
+           if (tvp) {
+               gvp = afs_globalVp;
                afs_globalVp = tvp;
-           else
+               if (gvp) {
+                   afs_PutVCache(gvp);
+                   if (tvp != afs_globalVp) {
+                       /* someone raced us and won */
+                       afs_PutVCache(tvp);
+                       goto tryagain;
+                   }
+               }
+           } else
                error = ENOENT;
        }
     }
@@ -274,16 +279,23 @@ tryagain:
 
        ASSERT_VI_UNLOCKED(vp, "afs_root");
        AFS_GUNLOCK();
+       error = vget(vp, LK_EXCLUSIVE | LK_RETRY, td);
+       AFS_GLOCK();
+       /* we dropped the glock, so re-check everything it had serialized */
+       if (!afs_globalVp || !(afs_globalVp->f.states & CStatd) ||
+               tvp != afs_globalVp) {
+           vput(vp);
+           afs_PutVCache(tvp);
+           goto tryagain;
+       }
+       if (error != 0)
+           goto tryagain;
        /*
         * I'm uncomfortable about this.  Shouldn't this happen at a
         * higher level, and shouldn't we busy the top-level directory
         * to prevent recycling?
         */
-       error = vget(vp, LK_EXCLUSIVE | LK_RETRY, td);
        vp->v_vflag |= VV_ROOT;
-       AFS_GLOCK();
-       if (error != 0)
-               goto tryagain;
 
        afs_globalVFS = mp;
        *vpp = vp;