From 72e2b1119136315a5845c7af00aa73456985d6fa Mon Sep 17 00:00:00 2001 From: Ben Kaduk Date: Sat, 6 Nov 2010 00:02:31 -0400 Subject: [PATCH] FBSD: close race in afs_root 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 Reviewed-by: Derrick Brashear Reviewed-on: http://gerrit.openafs.org/3631 Tested-by: Derrick Brashear --- src/afs/FBSD/osi_vfsops.c | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/afs/FBSD/osi_vfsops.c b/src/afs/FBSD/osi_vfsops.c index 32a8feb43..b956b8090 100644 --- a/src/afs/FBSD/osi_vfsops.c +++ b/src/afs/FBSD/osi_vfsops.c @@ -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; -- 2.39.5