This is a series of patches to fix locking problems with many of the
file systems. By the end, layered filesystems should work reliably, and
there will be a better starting point to fix other issues. The problems
can essentially be described by the statement, "In a virtual file system,
the files aren't real, so neither are the locks."
Short description of each patch; more details come with the patch.
They were tested not to break anything in this order. Sometimes order
won't matter, other times it will.
patch 1:
PDIRUNLOCK handling in lookup(). Make all real file systems use the new
cache_lookup() which handles ".." and things for them, instead of having a
private copy in every fs.
patch 2:
PDIRUNLOCK for the stuff in miscfs.
patch 3:
clean up kernfs and procfs and make them ready for real locks.
patch 4:
VLAYER flag, new WILLUNLOCK and WILLPUT vop descriptions, stuff to prep for
coming changes.
patch 5:
genfs_lock: real vnode locking. replace vop_generic_lock with genfs_nolock.
patch 6:
layerfs: common code from nullfs goes to layerfs, adds working locking.
patch 7:
unionfs: fix up unionfs to handle the changes.
patch 8:
genfs_lock for ufs. remove inode lock and use vnode lock.
This is patch 1. PDIRUNLOCK handling in lookup(). Make all real file
systems use the new cache_lookup() whih handles ".." and things for them,
instead of having a private copy in every fs. Affects all file systems
which have disk data, and nfs. Changes the return values for cache_lookup().
diff -ruNX nodiff orig/sys/adosfs/adlookup.c vfs1/adosfs/adlookup.c
--- orig/sys/adosfs/adlookup.c Thu Dec 18 00:59:01 1997
+++ vfs1/adosfs/adlookup.c Wed Dec 25 09:35:57 2002
@@ -90,6 +90,7 @@
*vpp = NULL;
ucp = cnp->cn_cred;
nameiop = cnp->cn_nameiop;
+ cnp->cn_flags &= ~PDIRUNLOCK;
flags = cnp->cn_flags;
last = flags & ISLASTCN;
lockp = flags & LOCKPARENT;
@@ -107,41 +108,17 @@
return (ENOTDIR);
if ((error = VOP_ACCESS(vdp, VEXEC, ucp, cnp->cn_proc)) != 0)
return (error);
+ if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) &&
+ (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
/*
- * cache lookup algorithm borrowed from ufs_lookup()
- * its not consistent with otherthings in this function..
+ * Before tediously performing a linear scan of the directory,
+ * check the name cache to see if the directory/name pair
+ * we are looking for is known already.
*/
- if ((error = cache_lookup(vdp, vpp, cnp)) != 0) {
- if (error == ENOENT)
- return (error);
-
- vpid = (*vpp)->v_id;
- if (vdp == *vpp) {
- VREF(vdp);
- error = 0;
- } else if (flags & ISDOTDOT) {
- VOP_UNLOCK(vdp, 0, p); /* race */
- error = vget(*vpp, LK_EXCLUSIVE, p);
- if (error == 0 && lockp && last)
- error =
- vn_lock(vdp, LK_EXCLUSIVE | LK_RETRY, p);
- } else {
- error = vget(*vpp, LK_EXCLUSIVE, p);
- /* if (lockp == 0 || error || last) */
- if (lockp == 0 || error || last == 0)
- VOP_UNLOCK(vdp, 0, p);
- }
- if (error == 0) {
- if (vpid == vdp->v_id)
- return (0);
- vput(*vpp);
- if (lockp && vdp != *vpp && last)
- VOP_UNLOCK(vdp, 0, p);
- }
- *vpp = NULL;
- if ((error = vn_lock(vdp, LK_EXCLUSIVE | LK_RETRY, p)) != 0)
- return (error);
- }
+ if ((error = cache_lookup(vdp, vpp, cnp)) >= 0)
+ return (error);
/*
* fake a '.'
@@ -174,12 +151,17 @@
*
*/
VOP_UNLOCK(vdp, 0, p); /* race */
+ cnp->cn_flags |= PDIRUNLOCK;
if ((error = VFS_VGET(vdp->v_mount, ABLKTOINO(adp->pblock),
- vpp)) != 0)
- vn_lock(vdp, LK_RETRY | LK_EXCLUSIVE, p);
- else if (last && lockp &&
- (error = vn_lock(vdp, LK_EXCLUSIVE | LK_RETRY, p)))
- vput(*vpp);
+ vpp)) != 0) {
+ if (vn_lock(vdp, LK_EXCLUSIVE | LK_RETRY, p) == 0)
+ cnp->cn_flags &= ~PDIRUNLOCK;
+ } else if (last && lockp) {
+ if ((error = vn_lock(vdp, LK_EXCLUSIVE | LK_RETRY, p)))
+ vput(*vpp);
+ else
+ cnp->cn_flags &= ~PDIRUNLOCK;
+ }
if (error) {
*vpp = NULL;
return (error);
@@ -238,8 +220,10 @@
#endif
return (error);
}
- if (lockp == 0)
+ if (lockp == 0) {
VOP_UNLOCK(vdp, 0, p);
+ cnp->cn_flags |= PDIRUNLOCK;
+ }
cnp->cn_nameiop |= SAVENAME;
#ifdef ADOSFS_DIAGNOSTIC
printf("EJUSTRETURN)");
@@ -278,8 +262,10 @@
}
if (vdp == *vpp)
VREF(vdp);
- else if (lockp == 0 || last == 0)
+ else if (lockp == 0 || last == 0) {
VOP_UNLOCK(vdp, 0, p);
+ cnp->cn_flags |= PDIRUNLOCK;
+ }
found_lockdone:
if ((cnp->cn_flags & MAKEENTRY) && nocache == 0)
cache_enter(vdp, *vpp, cnp);
diff -ruNX nodiff orig/sys/isofs/cd9660/cd9660_lookup.c vfs1/isofs/cd9660/cd9660_lookup.c
--- orig/sys/isofs/cd9660/cd9660_lookup.c Sat Jun 23 02:14:22 2001
+++ vfs1/isofs/cd9660/cd9660_lookup.c Wed Dec 25 09:35:57 2002
@@ -129,10 +129,13 @@
struct vnode **vpp = ap->a_vpp;
struct componentname *cnp = ap->a_cnp;
struct ucred *cred = cnp->cn_cred;
- int flags = cnp->cn_flags;
+ int flags;
int nameiop = cnp->cn_nameiop;
struct proc *p = cnp->cn_proc;
+ cnp->cn_flags &= ~PDIRUNLOCK;
+ flags = cnp->cn_flags;
+
bp = NULL;
*vpp = NULL;
vdp = ap->a_dvp;
@@ -158,55 +161,9 @@
* check the name cache to see if the directory/name pair
* we are looking for is known already.
*/
- if ((error = cache_lookup(vdp, vpp, cnp)) != 0) {
- int vpid; /* capability number of vnode */
+ if ((error = cache_lookup(vdp, vpp, cnp)) >= 0)
+ return (error);
- if (error == ENOENT)
- return (error);
-#ifdef PARANOID
- if ((vdp->v_flag & VROOT) && (flags & ISDOTDOT))
- panic("cd9660_lookup: .. through root");
-#endif
- /*
- * Get the next vnode in the path.
- * See comment below starting `Step through' for
- * an explaination of the locking protocol.
- */
- pdp = vdp;
- dp = VTOI(*vpp);
- vdp = *vpp;
- vpid = vdp->v_id;
- if (pdp == vdp) {
- VREF(vdp);
- error = 0;
- } else if (flags & ISDOTDOT) {
- VOP_UNLOCK(pdp, 0, p);
- error = vget(vdp, LK_EXCLUSIVE, p);
- if (!error && lockparent && (flags & ISLASTCN))
- error = vn_lock(pdp, LK_EXCLUSIVE, p);
- } else {
- error = vget(vdp, LK_EXCLUSIVE, p);
- if (!lockparent || error || !(flags & ISLASTCN))
- VOP_UNLOCK(pdp, 0, p);
- }
- /*
- * Check that the capability number did not change
- * while we were waiting for the lock.
- */
- if (!error) {
- if (vpid == vdp->v_id)
- return (0);
- vput(vdp);
- if (lockparent && pdp != vdp && (flags & ISLASTCN))
- VOP_UNLOCK(pdp, 0, p);
- }
- if ((error = vn_lock(pdp, LK_EXCLUSIVE, p)) != 0)
- return (error);
- vdp = pdp;
- dp = VTOI(pdp);
- *vpp = NULL;
- }
-
len = cnp->cn_namelen;
name = cnp->cn_nameptr;
/*
@@ -426,16 +383,20 @@
if (flags & ISDOTDOT) {
brelse(bp);
VOP_UNLOCK(pdp, 0, p); /* race to get the inode */
+ cnp->cn_flags |= PDIRUNLOCK;
error = cd9660_vget_internal(vdp->v_mount, dp->i_ino, &tdp,
- dp->i_ino != ino, NULL);
+ dp->i_ino != ino, NULL);
if (error) {
- vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, p);
+ if (vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, p) == 0)
+ cnp->cn_flags &= ~PDIRUNLOCK;
return (error);
}
- if (lockparent && (flags & ISLASTCN) &&
- (error = vn_lock(pdp, LK_EXCLUSIVE, p))) {
- vput(tdp);
- return (error);
+ if (lockparent && (flags & ISLASTCN)) {
+ if ((error = vn_lock(pdp, LK_EXCLUSIVE, p))) {
+ vput(tdp);
+ return (error);
+ }
+ cnp->cn_flags &= ~PDIRUNLOCK;
}
*vpp = tdp;
} else if (dp->i_number == dp->i_ino) {
@@ -448,8 +409,10 @@
brelse(bp);
if (error)
return (error);
- if (!lockparent || !(flags & ISLASTCN))
+ if (!lockparent || !(flags & ISLASTCN)) {
VOP_UNLOCK(pdp, 0, p);
+ cnp->cn_flags |= PDIRUNLOCK;
+ }
*vpp = tdp;
}
diff -ruNX nodiff orig/sys/kern/vfs_cache.c vfs1/kern/vfs_cache.c
--- orig/sys/kern/vfs_cache.c Wed Jul 3 21:19:08 2002
+++ vfs1/kern/vfs_cache.c Wed Dec 25 09:35:57 2002
@@ -90,9 +90,11 @@
- ni_ptr pointing to the name of the entry being sought, ni_namelen
- tells the length of the name, and ni_hash contains a hash of
- the name. If the lookup succeeds, the vnode is returned in ni_vp
- * and a status of -1 is returned. If the lookup determines that
- * the name does not exist (negative cacheing), a status of ENOENT
- * is returned. If the lookup fails, a status of zero is returned.
+ * and a status of 0 is returned. If the locking fails for whatever
+ * reason, the vnode is unlocked and the error is returned to caller.
+ * If the lookup determines that the name does not exist (negative cacheing),
+ * a status of ENOENT is returned. If the lookup fails, a status of -1
+ * is returned.
*/
int
cache_lookup(dvp, vpp, cnp)
@@ -100,65 +102,137 @@
struct vnode **vpp;
struct componentname *cnp;
{
- register struct namecache *ncp;
- register struct nchashhead *ncpp;
+ struct namecache *ncp;
+ struct nchashhead *ncpp;
+ struct vnode *vp;
+ struct proc *p = curproc;
if (!doingcache) {
cnp->cn_flags &= ~MAKEENTRY;
- return (0);
+ *vpp = NULL;
+ return (-1);
}
if (cnp->cn_namelen > NCHNAMLEN) {
nchstats.ncs_long++;
cnp->cn_flags &= ~MAKEENTRY;
- return (0);
+ *vpp = NULL;
+ return (-1);
}
+
ncpp = &nchashtbl[
hash32_buf(&dvp->v_id, sizeof(dvp->v_id), cnp->cn_hash) & nchash];
- for (ncp = ncpp->lh_first; ncp != 0; ncp = ncp->nc_hash.le_next) {
+ LIST_FOREACH(ncp, ncpp, nc_hash) {
if (ncp->nc_dvp == dvp &&
ncp->nc_dvpid == dvp->v_id &&
ncp->nc_nlen == cnp->cn_namelen &&
- !bcmp(ncp->nc_name, cnp->cn_nameptr, (u_int)ncp->nc_nlen))
+ !memcmp(ncp->nc_name, cnp->cn_nameptr, (u_int)ncp->nc_nlen))
break;
}
if (ncp == 0) {
nchstats.ncs_miss++;
- return (0);
+ *vpp = NULL;
+ return (-1);
}
if ((cnp->cn_flags & MAKEENTRY) == 0) {
nchstats.ncs_badhits++;
+ goto remove;
} else if (ncp->nc_vp == NULL) {
/*
* Restore the ISWHITEOUT flag saved earlier.
*/
cnp->cn_flags |= ncp->nc_vpid;
- if (cnp->cn_nameiop != CREATE) {
+ if (cnp->cn_nameiop != CREATE ||
+ (cnp->cn_flags & ISLASTCN) == 0) {
nchstats.ncs_neghits++;
/*
* Move this slot to end of LRU chain,
* if not already there.
*/
- if (ncp->nc_lru.tqe_next != 0) {
+ if (TAILQ_NEXT(ncp, nc_lru) != NULL) {
TAILQ_REMOVE(&nclruhead, ncp, nc_lru);
TAILQ_INSERT_TAIL(&nclruhead, ncp, nc_lru);
}
return (ENOENT);
+ } else {
+ nchstats.ncs_badhits++;
+ goto remove;
}
} else if (ncp->nc_vpid != ncp->nc_vp->v_id) {
nchstats.ncs_falsehits++;
+ goto remove;
+ }
+
+ vp = ncp->nc_vp;
+ vpid = vp->v_id;
+ if (vp == dvp) { /* lookup on "." */
+ VREF(dvp);
+ error = 0;
+ } else if (cnp->cn_flags & ISDOTDOT) {
+ VOP_UNLOCK(dvp, 0, p);
+ cnp->cn_flags |= PDIRUNLOCK;
+ error = vget(vp, LK_EXCLUSIVE, p);
+ /*
+ * If the above vget() succeeded and both LOCKPARENT and
+ * ISLASTCN is set, lock the directory vnode as well.
+ */
+ if (!error && (~cnp->cn_flags & (LOCKPARENT|ISLASTCN)) == 0) {
+ if ((error = vn_lock(dvp, LK_EXCLUSIVE, p)) != 0) {
+ vput(vp);
+ return (error);
+ }
+ cnp->cn_flags &= ~PDIRUNLOCK;
+ }
} else {
- nchstats.ncs_goodhits++;
+ error = vget(vp, LK_EXCLUSIVE, p);
+ /*
+ * If the above vget() failed or either of LOCKPARENT or
+ * ISLASTCN is set, unlock the directory vnode.
+ */
+ if (error || (~cnp->cn_flags & (LOCKPARENT|ISLASTCN)) != 0) {
+ VOP_UNLOCK(dvp, 0, p);
+ cnp->cn_flags |= PDIRUNLOCK;
+ }
+ }
+
+ /*
+ * Check that the lock succeeded, and that the capability number did
+ * not change while we were waiting for the lock.
+ */
+ if (error || vpid != vp->v_id) {
+ if (!error) {
+ vput(vp);
+ nchstats.ncs_falsehits++;
+ } else
+ nchstats.ncs_badhits++;
/*
- * move this slot to end of LRU chain, if not already there
+ * The parent needs to be locked when we return to VOP_LOOKUP().
+ * The `.' case here should be extremely rare (if it can happen
+ * at all), so we don't bother optimizing out the unlock/relock.
*/
- if (ncp->nc_lru.tqe_next != 0) {
- TAILQ_REMOVE(&nclruhead, ncp, nc_lru);
- TAILQ_INSERT_TAIL(&nclruhead, ncp, nc_lru);
+ if (vp == dvp || error ||
+ (~cnp->cn_flags & (LOCKPARENT|ISLASTCN)) != 0) {
+ if ((error = vn_lock(dvp, LK_EXCLUSIVE, p)) != 0)
+ return (error);
+ cnp->cn_flags &= ~PDIRUNLOCK;
}
- *vpp = ncp->nc_vp;
+ *vpp = NULL;
return (-1);
}
+ nchstats.ncs_goodhits++;
+ /*
+ * Move this slot to end of LRU chain, if not already there.
+ */
+ if (TAILQ_NEXT(ncp, nc_lru) != NULL) {
+ TAILQ_REMOVE(&nclruhead, ncp, nc_lru);
+ TAILQ_INSERT_TAIL(&nclruhead, ncp, nc_lru);
+ }
+ *vpp = vp;
/*
* Last component and we are renaming or deleting,
* the cache entry is invalid, or otherwise don't
@@ -166,9 +240,16 @@
*/
TAILQ_REMOVE(&nclruhead, ncp, nc_lru);
LIST_REMOVE(ncp, nc_hash);
- ncp->nc_hash.le_prev = 0;
+ ncp->nc_hash.le_prev = NULL;
+#if 0
+ if (ncp->nc_vhash.le_prev != NULL) {
+ LIST_REMOVE(ncp, nc_vhash);
+ ncp->nc_vhash.le_prev = NULL;
+ }
+#endif
TAILQ_INSERT_HEAD(&nclruhead, ncp, nc_lru);
- return (0);
+ *vpp = NULL;
+ return (-1);
}
/*
diff -ruNX nodiff orig/sys/kern/vfs_lookup.c vfs1/kern/vfs_lookup.c
--- orig/sys/kern/vfs_lookup.c Tue Aug 27 16:04:42 2002
+++ vfs1/kern/vfs_lookup.c Wed Dec 25 09:35:57 2002
@@ -289,6 +289,7 @@
int wantparent; /* 1 => wantparent or lockparent flag */
int rdonly; /* lookup read-only flag bit */
int error = 0;
+ int dpunlocked = 0; /* dp has already been unlocked */
int slashes;
struct componentname *cnp = &ndp->ni_cnd;
struct proc *p = cnp->cn_proc;
@@ -439,6 +440,8 @@
unionlookup:
ndp->ni_dvp = dp;
ndp->ni_vp = NULL;
+ cnp->cn_flags &= ~PDIRUNLOCK;
+
if ((error = VOP_LOOKUP(dp, &ndp->ni_vp, cnp)) != 0) {
#ifdef DIAGNOSTIC
if (ndp->ni_vp != NULL)
@@ -452,7 +455,10 @@
(dp->v_mount->mnt_flag & MNT_UNION)) {
tdp = dp;
dp = dp->v_mount->mnt_vnodecovered;
- vput(tdp);
+ if (cnp->cn_flags & PDIRUNLOCK)
+ vrele(tdp);
+ else
+ vput(tdp);
VREF(dp);
vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, p);
goto unionlookup;
@@ -517,11 +523,14 @@
(cnp->cn_flags & NOCROSSMOUNT) == 0) {
if (vfs_busy(mp, 0, 0, p))
continue;
+ VOP_UNLOCK(dp, 0, p);
error = VFS_ROOT(mp, &tdp);
vfs_unbusy(mp, p);
- if (error)
+ if (error) {
+ dpunlocked = 1;
goto bad2;
- vput(dp);
+ }
+ vrele(dp);
ndp->ni_vp = dp = tdp;
}
@@ -585,11 +594,15 @@
return (0);
bad2:
- if ((cnp->cn_flags & LOCKPARENT) && (cnp->cn_flags & ISLASTCN))
+ if ((cnp->cn_flags & LOCKPARENT) && (cnp->cn_flags & ISLASTCN) &&
+ ((cnp->cn_flags & PDIRUNLOCK) == 0))
VOP_UNLOCK(ndp->ni_dvp, 0, p);
vrele(ndp->ni_dvp);
bad:
- vput(dp);
+ if (dpunlocked)
+ vrele(dp);
+ else
+ vput(dp);
ndp->ni_vp = NULL;
return (error);
}
diff -ruNX nodiff orig/sys/msdosfs/msdosfs_lookup.c vfs1/msdosfs/msdosfs_lookup.c
--- orig/sys/msdosfs/msdosfs_lookup.c Fri Feb 19 17:26:17 1999
+++ vfs1/msdosfs/msdosfs_lookup.c Wed Dec 25 09:35:58 2002
@@ -110,12 +110,15 @@
struct buf *bp = 0;
struct direntry *dep;
u_char dosfilename[12];
- int flags = cnp->cn_flags;
+ int flags;
int nameiop = cnp->cn_nameiop;
int wincnt = 1;
int chksum = -1;
int olddos = 1;
-
+
+ cnp->cn_flags &= ~PDIRUNLOCK; /* XXX why this ?? */
+ flags = cnp->cn_flags;
+
#ifdef MSDOSFS_DEBUG
printf("msdosfs_lookup(): looking for %s\n", cnp->cn_nameptr);
#endif
@@ -144,56 +147,8 @@
- check the name cache to see if the directory/name pair
- we are looking for is known already.
*/
- if ((error = cache_lookup(vdp, vpp, cnp)) != 0) {
- int vpid;
-
- if (error == ENOENT)
- return (error);
- /*
- * Get the next vnode in the path.
- * See comment below starting `Step through' for
- * an explaination of the locking protocol.
- */
- pdp = vdp;
- dp = VTODE(*vpp);
- vdp = *vpp;
- vpid = vdp->v_id;
- if (pdp == vdp) { /* lookup on "." */
- VREF(vdp);
- error = 0;
- } else if (flags & ISDOTDOT) {
- VOP_UNLOCK(pdp, 0, p);
- error = vget(vdp, LK_EXCLUSIVE, p);
- if (!error && lockparent && (flags & ISLASTCN))
- error =
- vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, p);
- } else {
- error = vget(vdp, LK_EXCLUSIVE, p);
- if (!lockparent || error || !(flags & ISLASTCN))
- VOP_UNLOCK(pdp, 0, p);
- }
- /*
- * Check that the capability number did not change
- * while we were waiting for the lock.
- */
- if (!error) {
- if (vpid == vdp->v_id) {
-#ifdef MSDOSFS_DEBUG
- printf("msdosfs_lookup(): cache hit, vnode %08x, file %s\n",
- vdp, dp->de_Name);
-#endif
- return (0);
- }
- vput(vdp);
- if (lockparent && pdp != vdp && (flags & ISLASTCN))
- VOP_UNLOCK(pdp, 0, p);
- }
- if ((error = vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, p)) != 0)
- return (error);
- vdp = pdp;
- dp = VTODE(vdp);
- *vpp = NULL;
- }
+ if ((error = cache_lookup(vdp, vpp, cnp)) >= 0)
+ return (error);
/*
* If they are going after the . or .. entry in the root directory,
@@ -415,8 +370,10 @@
* information cannot be used.
*/
cnp->cn_flags |= SAVENAME;
- if (!lockparent)
+ if (!lockparent) {
VOP_UNLOCK(vdp, 0, p);
+ cnp->cn_flags |= PDIRUNLOCK;
+ }
return (EJUSTRETURN);
}
/*
@@ -504,8 +461,10 @@
if ((error = deget(pmp, cluster, blkoff, &tdp)) != 0)
return (error);
*vpp = DETOV(tdp);
- if (!lockparent)
+ if (!lockparent) {
VOP_UNLOCK(vdp, 0, p);
+ cnp->cn_flags |= PDIRUNLOCK;
+ }
return (0);
}
@@ -562,14 +521,19 @@
pdp = vdp;
if (flags & ISDOTDOT) {
VOP_UNLOCK(pdp, 0, p); /* race to get the inode */
+ cnp->cn_flags |= PDIRUNLOCK;
if ((error = deget(pmp, cluster, blkoff, &tdp)) != 0) {
- vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, p);
+ if (vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, p) == 0)
+ cnp->cn_flags &= ~PDIRUNLOCK;
return (error);
}
- if (lockparent && (flags & ISLASTCN) &&
- (error = vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, p))) {
- vput(DETOV(tdp));
- return (error);
+ if (lockparent && (flags & ISLASTCN)) {
+ if ((error = vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY,
+ p))) {
+ vput(DETOV(tdp));
+ return (error);
+ }
+ cnp->cn_flags &= ~PDIRUNLOCK;
}
*vpp = DETOV(tdp);
} else if (dp->de_StartCluster == scn && isadir) {
@@ -578,8 +542,10 @@
} else {
if ((error = deget(pmp, cluster, blkoff, &tdp)) != 0)
return (error);
- if (!lockparent || !(flags & ISLASTCN))
+ if (!lockparent || !(flags & ISLASTCN)) {
VOP_UNLOCK(pdp, 0, p);
+ cnp->cn_flags |= PDIRUNLOCK;
+ }
*vpp = DETOV(tdp);
}
diff -ruNX nodiff orig/sys/nfs/nfs_vnops.c vfs1/nfs/nfs_vnops.c
--- orig/sys/nfs/nfs_vnops.c Fri Nov 29 10:48:20 2002
+++ vfs1/nfs/nfs_vnops.c Wed Dec 25 09:35:58 2002
@@ -636,7 +636,7 @@
struct vnode *dvp = ap->a_dvp;
struct vnode **vpp = ap->a_vpp;
struct proc *p = cnp->cn_proc;
- int flags = cnp->cn_flags;
+ int flags;
struct vnode *newvp;
u_int32_t *tl;
caddr_t cp;
@@ -650,6 +650,9 @@
int lockparent, wantparent, error = 0, attrflag, fhsize;
int v3 = NFS_ISV3(dvp);
+ cnp->cn_flags &= ~PDIRUNLOCK;
+ flags = cnp->cn_flags;
+
*vpp = NULLVP;
if ((flags & ISLASTCN) && (dvp->v_mount->mnt_flag & MNT_RDONLY) &&
(cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
@@ -660,38 +663,76 @@
wantparent = flags & (LOCKPARENT|WANTPARENT);
nmp = VFSTONFS(dvp->v_mount);
np = VTONFS(dvp);
- if ((error = cache_lookup(dvp, vpp, cnp)) != 0 && error != ENOENT) {
+
+ /*
+ * Before tediously performing a linear scan of the directory,
+ * check the name cache to see if the directory/name pair
+ * we are looking for is known already.
+ * If the directory/name pair is found in the name cache,
+ * we have to ensure the directory has not changed from
+ * the time the cache entry has been created. If it has,
+ * the cache entry has to be ignored.
+ */
+ if ((error = cache_lookup(dvp, vpp, cnp)) >= 0) {
struct vattr vattr;
- int vpid;
+ int err2;
- newvp = *vpp;
- vpid = newvp->v_id;
- /*
- * See the comment starting `Step through' in ufs/ufs_lookup.c
- * for an explanation of the locking protocol
- */
- if (dvp == newvp) {
- VREF(newvp);
- error = 0;
- } else
- error = vget(newvp, LK_EXCLUSIVE, p);
+ if (error && error != ENOENT) {
+ *vpp = NULLVP;
- if (!error) {
- if (vpid == newvp->v_id) {
- if (!VOP_GETATTR(newvp, &vattr, cnp->cn_cred, cnp->cn_proc)
- && vattr.va_ctime.tv_sec == VTONFS(newvp)->n_ctime) {
- nfsstats.lookupcache_hits++;
- if (cnp->cn_nameiop != LOOKUP &&
- (flags & ISLASTCN))
- cnp->cn_flags |= SAVENAME;
- return (0);
- }
- cache_purge(newvp);
+ if (cnp->cn_flags & PDIRUNLOCK) {
+ err2 = vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p);
+ if (err2 != 0) {
+ *vpp = NULLVP;
}
- vrele(newvp);
+ cnp->cn_flags &= ~PDIRUNLOCK;
}
+
+ err2 = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, cnp->cn_proc);
+ if (err2 != 0) {
+ if (error == 0) {
+ if (*vpp != dvp)
+ vput(*vpp);
+ else
+ vrele(*vpp);
+ }
+ *vpp = NULLVP;
+ return (err2);
+ }
+
+ if (error == ENOENT) {
+ if (!VOP_GETATTR(dvp, &vattr, cnp->cn_cred,
+ cnp->cn_proc) && vattr.va_mtime.tv_sec ==
+ VTONFS(dvp)->n_ctime)
+ return (ENOENT);
+ cache_purge(dvp);
+ np->n_ctime = 0;
+ goto dorpc;
+ }
+
+ newvp = *vpp;
+ if (!VOP_GETATTR(newvp, &vattr, cnp->cn_cred, cnp->cn_proc)
+ && vattr.va_ctime.tv_sec == VTONFS(newvp)->n_ctime)
+ {
+ nfsstats.lookupcache_hits++;
+ if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN))
+ cnp->cn_flags |= SAVENAME;
+ if ((!lockparent || !(flags & ISLASTCN)) &&
+ newvp != dvp)
+ VOP_UNLOCK(dvp, 0, p);
+ return (0);
+ }
+ cache_purge(newvp);
+ if (newvp != dvp)
+ vput(newvp);
+ else
+ vrele(newvp);
*vpp = NULLVP;
}
+dorpc:
error = 0;
newvp = NULLVP;
nfsstats.lookupcache_misses++;
@@ -731,25 +772,72 @@
*vpp = newvp;
m_freem(mrep);
cnp->cn_flags |= SAVENAME;
+ if (!lockparent) {
+ VOP_UNLOCK(dvp, 0, p);
+ cnp->cn_flags |= PDIRUNLOCK;
+ }
return (0);
}
+ /*
+ * The postop attr handling is duplicated for each if case,
+ * because it should be done while dvp is locked (unlocking
+ * dvp is different for each case).
+ */
+
if (NFS_CMPFH(np, fhp, fhsize)) {
VREF(dvp);
newvp = dvp;
- } else {
+ if (v3) {
+ nfsm_postop_attr(newvp, attrflag);
+ nfsm_postop_attr(dvp, attrflag);
+ } else
+ nfsm_loadattr(newvp, (struct vattr *)0);
+ } else if (flags & ISDOTDOT) {
+ VOP_UNLOCK(dvp, 0, p);
+ cnp->cn_flags |= PDIRUNLOCK;
+
error = nfs_nget(dvp->v_mount, fhp, fhsize, &np);
if (error) {
+ if (vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p) == 0)
+ cnp->cn_flags &= ~PDIRUNLOCK;
m_freem(mrep);
return (error);
}
newvp = NFSTOV(np);
+
+ if (v3) {
+ nfsm_postop_attr(newvp, attrflag);
+ nfsm_postop_attr(dvp, attrflag);
+ } else
+ nfsm_loadattr(newvp, (struct vattr *)0);
+
+ if (lockparent && (flags & ISLASTCN)) {
+ if ((error = vn_lock(dvp, LK_EXCLUSIVE, p))) {
+ m_freem(mrep);
+ vput(newvp);
+ return error;
+ }
+ cnp->cn_flags &= ~PDIRUNLOCK;
+ }
+
+ } else {
+ error = nfs_nget(dvp->v_mount, fhp, fhsize, &np);
+ if (error) {
+ m_freem(mrep);
+ return error;
+ }
+ newvp = NFSTOV(np);
+ if (v3) {
+ nfsm_postop_attr(newvp, attrflag);
+ nfsm_postop_attr(dvp, attrflag);
+ } else
+ nfsm_loadattr(newvp, (struct vattr *)0);
+ if (!lockparent || !(flags & ISLASTCN)) {
+ VOP_UNLOCK(dvp, 0, p);
+ cnp->cn_flags |= PDIRUNLOCK;
+ }
}
- if (v3) {
- nfsm_postop_attr(newvp, attrflag);
- nfsm_postop_attr(dvp, attrflag);
- } else
- nfsm_loadattr(newvp, (struct vattr *)0);
if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN))
cnp->cn_flags |= SAVENAME;
if ((cnp->cn_flags & MAKEENTRY) &&
@@ -760,8 +848,24 @@
*vpp = newvp;
nfsm_reqdone;
if (error) {
- if (newvp != NULLVP)
+ /*
+ * We get here only because of errors returned by
+ * the RPC. Otherwise we'll have returned above
+ * (the nfsm_* macros will jump to nfsm_reqdone
+ * on error).
+ */
+ if (error == ENOENT && (cnp->cn_flags & MAKEENTRY) &&
+ cnp->cn_nameiop != CREATE) {
+ if (VTONFS(dvp)->n_ctime == 0)
+ VTONFS(dvp)->n_ctime =
+ VTONFS(dvp)->n_vattr.va_mtime.tv_sec;
+ cache_enter(dvp, NULL, cnp);
+ }
+ if (newvp != NULLVP) {
vrele(newvp);
+ if (newvp != dvp)
+ VOP_UNLOCK(newvp, 0, p);
+ }
if ((cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME) &&
(flags & ISLASTCN) && error == ENOENT) {
if (dvp->v_mount->mnt_flag & MNT_RDONLY)
@@ -771,6 +875,7 @@
}
if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN))
cnp->cn_flags |= SAVENAME;
+ *vpp = NULL;
}
return (error);
}
diff -ruNX nodiff orig/sys/sys/namei.h vfs1/sys/namei.h
--- orig/sys/sys/namei.h Wed Jul 3 21:19:08 2002
+++ vfs1/sys/namei.h Wed Dec 25 09:35:58 2002
@@ -140,7 +140,8 @@
#define DOWHITEOUT 0x040000 /* do whiteouts */
#define REQUIREDIR 0x080000 /* must be a directory */
#define STRIPSLASHES 0x100000 /* strip trailing slashes */
-#define PARAMASK 0x1fff00 /* mask of parameter descriptors */
+#define PDIRUNLOCK 0x200000 /* vfs_lookup() unlocked parent dir */
+#define PARAMASK 0x2fff00 /* mask of parameter descriptors */
/*
- Initialization of an nameidata structure.
*/
diff -ruNX nodiff orig/sys/ufs/ext2fs/ext2fs_lookup.c vfs1/ufs/ext2fs/ext2fs_lookup.c
--- orig/sys/ufs/ext2fs/ext2fs_lookup.c Sat Oct 12 01:09:45 2002
+++ vfs1/ufs/ext2fs/ext2fs_lookup.c Wed Dec 25 09:35:58 2002
@@ -317,50 +317,8 @@
- check the name cache to see if the directory/name pair
- we are looking for is known already.
*/
- if ((error = cache_lookup(vdp, vpp, cnp)) != 0) {
- int vpid; /* capability number of vnode */
-
- if (error == ENOENT)
- return (error);
- /*
- * Get the next vnode in the path.
- * See comment below starting `Step through' for
- * an explaination of the locking protocol.
- */
- pdp = vdp;
- dp = VTOI(*vpp);
- vdp = *vpp;
- vpid = vdp->v_id;
- if (pdp == vdp) { /* lookup on "." */
- VREF(vdp);
- error = 0;
- } else if (flags & ISDOTDOT) {
- VOP_UNLOCK(pdp, 0, p);
- error = vget(vdp, LK_EXCLUSIVE, p);
- if (!error && lockparent && (flags & ISLASTCN))
- error = vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, p);
- } else {
- error = vget(vdp, LK_EXCLUSIVE, p);
- if (!lockparent || error || !(flags & ISLASTCN))
- VOP_UNLOCK(pdp, 0, p);
- }
- /*
- * Check that the capability number did not change
- * while we were waiting for the lock.
- */
- if (!error) {
- if (vpid == vdp->v_id)
- return (0);
- vput(vdp);
- if (lockparent && pdp != vdp && (flags & ISLASTCN))
- VOP_UNLOCK(pdp, 0, p);
- }
- if ((error = vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, p)) != 0)
- return (error);
- vdp = pdp;
- dp = VTOI(pdp);
- *vpp = NULL;
- }
+ if ((error = cache_lookup(vdp, vpp, cnp)) >= 0)
+ return (error);
/*
* Suppress search for slots unless creating
@@ -570,8 +528,10 @@
* information cannot be used.
*/
cnp->cn_flags |= SAVENAME;
- if (!lockparent)
+ if (!lockparent) {
VOP_UNLOCK(vdp, 0, p);
+ cnp->cn_flags |= PDIRUNLOCK;
+ }
return (EJUSTRETURN);
}
/*
@@ -646,8 +606,10 @@
return (EPERM);
}
*vpp = tdp;
- if (!lockparent)
+ if (!lockparent) {
VOP_UNLOCK(vdp, 0, p);
+ cnp->cn_flags |= PDIRUNLOCK;
+ }
return (0);
}
@@ -671,8 +633,10 @@
return (error);
*vpp = tdp;
cnp->cn_flags |= SAVENAME;
- if (!lockparent)
+ if (!lockparent) {
VOP_UNLOCK(vdp, 0, p);
+ cnp->cn_flags |= PDIRUNLOCK;
+ }
return (0);
}
@@ -698,14 +662,18 @@
pdp = vdp;
if (flags & ISDOTDOT) {
VOP_UNLOCK(pdp, 0, p); /* race to get the inode */
+ cnp->cn_flags |= PDIRUNLOCK;
if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0) {
- vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, p);
+ if (vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, p) == 0)
+ cnp->cn_flags &= ~PDIRUNLOCK;
return (error);
}
- if (lockparent && (flags & ISLASTCN) &&
- (error = vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, p)) != 0) {
- vput(tdp);
- return (error);
+ if (lockparent && (flags & ISLASTCN)) {
+ if ((error = vn_lock(pdp, LK_EXCLUSIVE, p)) != 0) {
+ vput(tdp);
+ return (error);
+ }
+ cnp->cn_flags &= ~PDIRUNLOCK;
}
*vpp = tdp;
} else if (dp->i_number == dp->i_ino) {
@@ -714,8 +682,10 @@
} else {
if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0)
return (error);
- if (!lockparent || !(flags & ISLASTCN))
+ if (!lockparent || !(flags & ISLASTCN)) {
VOP_UNLOCK(pdp, 0, p);
+ cnp->cn_flags |= PDIRUNLOCK;
+ }
*vpp = tdp;
}
diff -ruNX nodiff orig/sys/ufs/ufs/ufs_lookup.c vfs1/ufs/ufs/ufs_lookup.c
--- orig/sys/ufs/ufs/ufs_lookup.c Fri Feb 22 20:37:46 2002
+++ vfs1/ufs/ufs/ufs_lookup.c Wed Dec 25 09:35:58 2002
@@ -134,10 +134,13 @@
struct vnode **vpp = ap->a_vpp;
struct componentname *cnp = ap->a_cnp;
struct ucred *cred = cnp->cn_cred;
- int flags = cnp->cn_flags;
+ int flags;
int nameiop = cnp->cn_nameiop;
struct proc *p = cnp->cn_proc;
+ cnp->cn_flags &= ~PDIRUNLOCK;
+ flags = cnp->cn_flags;
+
bp = NULL;
slotoffset = -1;
*vpp = NULL;
@@ -165,51 +168,8 @@
* check the name cache to see if the directory/name pair
* we are looking for is known already.
*/
- if ((error = cache_lookup(vdp, vpp, cnp)) != 0) {
- int vpid; /* capability number of vnode */
-
- if (error == ENOENT)
- return (error);
- /*
- * Get the next vnode in the path.
- * See comment below starting `Step through' for
- * an explaination of the locking protocol.
- */
- pdp = vdp;
- dp = VTOI(*vpp);
- vdp = *vpp;
- vpid = vdp->v_id;
- if (pdp == vdp) { /* lookup on "." */
- VREF(vdp);
- error = 0;
- } else if (flags & ISDOTDOT) {
- VOP_UNLOCK(pdp, 0, p);
- error = vget(vdp, LK_EXCLUSIVE, p);
- if (!error && lockparent && (flags & ISLASTCN))
- error = vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, p);
- } else {
- error = vget(vdp, LK_EXCLUSIVE, p);
- if (!lockparent || error || !(flags & ISLASTCN))
- VOP_UNLOCK(pdp, 0, p);
- }
- /*
- * Check that the capability number did not change
- * while we were waiting for the lock.
- */
- if (!error) {
- if (vpid == vdp->v_id)
- return (0);
- vput(vdp);
- if (lockparent && pdp != vdp && (flags & ISLASTCN))
- VOP_UNLOCK(pdp, 0, p);
- }
- *vpp = NULL;
-
- if ((error = vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, p)) != 0)
- return (error);
- vdp = pdp;
- dp = VTOI(pdp);
- }
+ if ((error = cache_lookup(vdp, vpp, cnp)) >= 0)
+ return (error);
/*
* Suppress search for slots unless creating
@@ -455,8 +415,10 @@
* information cannot be used.
*/
cnp->cn_flags |= SAVENAME;
- if (!lockparent)
+ if (!lockparent) {
VOP_UNLOCK(vdp, 0, p);
+ cnp->cn_flags |= PDIRUNLOCK;
+ }
return (EJUSTRETURN);
}
/*
@@ -534,8 +496,10 @@
return (EPERM);
}
*vpp = tdp;
- if (!lockparent)
+ if (!lockparent) {
VOP_UNLOCK(vdp, 0, p);
+ cnp->cn_flags |= PDIRUNLOCK;
+ }
return (0);
}
@@ -561,8 +525,10 @@
return (error);
*vpp = tdp;
cnp->cn_flags |= SAVENAME;
- if (!lockparent)
+ if (!lockparent) {
VOP_UNLOCK(vdp, 0, p);
+ cnp->cn_flags |= PDIRUNLOCK;
+ }
return (0);
}
@@ -588,15 +554,19 @@
pdp = vdp;
if (flags & ISDOTDOT) {
VOP_UNLOCK(pdp, 0, p); /* race to get the inode */
+ cnp->cn_flags |= PDIRUNLOCK;
error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp);
if (error) {
- vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, p);
+ if (vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, p) == 0)
+ cnp->cn_flags &= ~PDIRUNLOCK;
return (error);
}
- if (lockparent && (flags & ISLASTCN) &&
- (error = vn_lock(pdp, LK_EXCLUSIVE, p))) {
- vput(tdp);
- return (error);
+ if (lockparent && (flags & ISLASTCN)) {
+ if ((error = vn_lock(pdp, LK_EXCLUSIVE, p))) {
+ vput(tdp);
+ return (error);
+ }
+ cnp->cn_flags &= ~PDIRUNLOCK;
}
*vpp = tdp;
} else if (dp->i_number == dp->i_ino) {
@@ -606,8 +576,10 @@
error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp);
if (error)
return (error);
- if (!lockparent || !(flags & ISLASTCN))
+ if (!lockparent || !(flags & ISLASTCN)) {
VOP_UNLOCK(pdp, 0, p);
+ cnp->cn_flags |= PDIRUNLOCK;
+ }
*vpp = tdp;
}
diff -ruNX nodiff orig/sys/xfs/xfs_node-bsd.c vfs1/xfs/xfs_node-bsd.c
--- orig/sys/xfs/xfs_node-bsd.c Fri Jun 7 04:10:32 2002
+++ vfs1/xfs/xfs_node-bsd.c Wed Dec 25 09:40:54 2002
@@ -533,9 +533,9 @@
- The real change is sys/kern/vfs_cache:1.20
*/
-#if __NetBSD_Version__ >= 104120000
+#if __NetBSD_Version__ >= 104120000 || defined(__OpenBSD__)
if (cache_lookup(dvp, &dummy, cnp) != -1) {
- VOP_UNLOCK(dummy, 0);
+ VOP_UNLOCK(dummy, 0, cnp->cn_proc);
printf ("XFS PANIC WARNING! xfs_dnlc_enter: %s already in cache\n",
cnp->cn_nameptr);
}
@@ -700,7 +700,7 @@
- (see the comment above for version information).
*/
-#if __NetBSD_Version__ >= 104120000
+#if __NetBSD_Version__ >= 104120000 || defined(__OpenBSD__)
int
xfs_dnlc_lookup(struct vnode *dvp,
--
"I promise you a police car on every sidewalk."
- M. Barry, Mayor of Washington, DC
Received on Sun Dec 29 22:02:18 2002