Module Name: src
Committed By: ad
Date: Thu Jan 16 16:45:31 UTC 2020
Modified Files:
src/sys/kern [ad-namecache]: vfs_lookup.c
Log Message:
Push the vnode locking in namei() about as far back as it will go.
To generate a diff of this commit:
cvs rdiff -u -r1.212 -r1.212.4.1 src/sys/kern/vfs_lookup.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/kern/vfs_lookup.c
diff -u src/sys/kern/vfs_lookup.c:1.212 src/sys/kern/vfs_lookup.c:1.212.4.1
--- src/sys/kern/vfs_lookup.c:1.212 Thu Jul 18 09:39:40 2019
+++ src/sys/kern/vfs_lookup.c Thu Jan 16 16:45:30 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: vfs_lookup.c,v 1.212 2019/07/18 09:39:40 hannken Exp $ */
+/* $NetBSD: vfs_lookup.c,v 1.212.4.1 2020/01/16 16:45:30 ad Exp $ */
/*
* Copyright (c) 1982, 1986, 1989, 1993
@@ -37,7 +37,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: vfs_lookup.c,v 1.212 2019/07/18 09:39:40 hannken Exp $");
+__KERNEL_RCSID(0, "$NetBSD: vfs_lookup.c,v 1.212.4.1 2020/01/16 16:45:30 ad Exp $");
#ifdef _KERNEL_OPT
#include "opt_magiclinks.h"
@@ -710,8 +710,6 @@ namei_start(struct namei_state *state, i
return ENOTDIR;
}
- vn_lock(startdir, LK_EXCLUSIVE | LK_RETRY);
-
*startdir_ret = startdir;
return 0;
}
@@ -749,15 +747,17 @@ namei_follow(struct namei_state *state,
size_t linklen;
int error;
- KASSERT(VOP_ISLOCKED(searchdir) == LK_EXCLUSIVE);
- KASSERT(VOP_ISLOCKED(foundobj) == LK_EXCLUSIVE);
if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {
return ELOOP;
}
+
+ vn_lock(foundobj, LK_EXCLUSIVE | LK_RETRY);
if (foundobj->v_mount->mnt_flag & MNT_SYMPERM) {
error = VOP_ACCESS(foundobj, VEXEC, cnp->cn_cred);
- if (error != 0)
+ if (error != 0) {
+ VOP_UNLOCK(foundobj);
return error;
+ }
}
/* FUTURE: fix this to not use a second buffer */
@@ -771,6 +771,7 @@ namei_follow(struct namei_state *state,
auio.uio_resid = MAXPATHLEN;
UIO_SETUP_SYSSPACE(&auio);
error = VOP_READLINK(foundobj, &auio, cnp->cn_cred);
+ VOP_UNLOCK(foundobj);
if (error) {
PNBUF_PUT(cp);
return error;
@@ -807,14 +808,11 @@ namei_follow(struct namei_state *state,
/* we're now starting from the beginning of the buffer again */
cnp->cn_nameptr = ndp->ni_pnbuf;
- /* must unlock this before relocking searchdir */
- VOP_UNLOCK(foundobj);
-
/*
* Check if root directory should replace current directory.
*/
if (ndp->ni_pnbuf[0] == '/') {
- vput(searchdir);
+ vrele(searchdir);
/* Keep absolute symbolic links inside emulation root */
searchdir = ndp->ni_erootdir;
if (searchdir == NULL ||
@@ -825,7 +823,6 @@ namei_follow(struct namei_state *state,
searchdir = ndp->ni_rootdir;
}
vref(searchdir);
- vn_lock(searchdir, LK_EXCLUSIVE | LK_RETRY);
while (cnp->cn_nameptr[0] == '/') {
cnp->cn_nameptr++;
ndp->ni_pathlen--;
@@ -833,7 +830,6 @@ namei_follow(struct namei_state *state,
}
*newsearchdir_ret = searchdir;
- KASSERT(VOP_ISLOCKED(searchdir) == LK_EXCLUSIVE);
return 0;
}
@@ -933,19 +929,20 @@ static int
lookup_once(struct namei_state *state,
struct vnode *searchdir,
struct vnode **newsearchdir_ret,
- struct vnode **foundobj_ret)
+ struct vnode **foundobj_ret,
+ bool *newsearchdir_locked_ret)
{
struct vnode *tmpvn; /* scratch vnode */
struct vnode *foundobj; /* result */
struct mount *mp; /* mount table entry */
struct lwp *l = curlwp;
+ bool searchdir_locked = false;
int error;
struct componentname *cnp = state->cnp;
struct nameidata *ndp = state->ndp;
KASSERT(cnp == &ndp->ni_cnd);
- KASSERT(VOP_ISLOCKED(searchdir) == LK_EXCLUSIVE);
*newsearchdir_ret = searchdir;
/*
@@ -977,9 +974,7 @@ lookup_once(struct namei_state *state,
if (ndp->ni_rootdir != rootvnode) {
int retval;
- VOP_UNLOCK(searchdir);
retval = vn_isunder(searchdir, ndp->ni_rootdir, l);
- vn_lock(searchdir, LK_EXCLUSIVE | LK_RETRY);
if (!retval) {
/* Oops! We got out of jail! */
log(LOG_WARNING,
@@ -988,12 +983,11 @@ lookup_once(struct namei_state *state,
p->p_pid, kauth_cred_geteuid(l->l_cred),
p->p_comm);
/* Put us at the jail root. */
- vput(searchdir);
+ vrele(searchdir);
searchdir = NULL;
foundobj = ndp->ni_rootdir;
vref(foundobj);
vref(foundobj);
- vn_lock(foundobj, LK_EXCLUSIVE | LK_RETRY);
*newsearchdir_ret = foundobj;
*foundobj_ret = foundobj;
error = 0;
@@ -1006,18 +1000,19 @@ lookup_once(struct namei_state *state,
tmpvn = searchdir;
searchdir = searchdir->v_mount->mnt_vnodecovered;
vref(searchdir);
- vput(tmpvn);
- vn_lock(searchdir, LK_EXCLUSIVE | LK_RETRY);
+ vrele(tmpvn);
*newsearchdir_ret = searchdir;
}
}
/*
* We now have a segment name to search for, and a directory to search.
- * Our vnode state here is that "searchdir" is held and locked.
+ * Our vnode state here is that "searchdir" is held.
*/
unionlookup:
foundobj = NULL;
+ vn_lock(searchdir, LK_EXCLUSIVE | LK_RETRY);
+ searchdir_locked = true;
error = VOP_LOOKUP(searchdir, &foundobj, cnp);
if (error != 0) {
@@ -1034,7 +1029,7 @@ unionlookup:
searchdir = searchdir->v_mount->mnt_vnodecovered;
vref(searchdir);
vput(tmpvn);
- vn_lock(searchdir, LK_EXCLUSIVE | LK_RETRY);
+ searchdir_locked = false;
*newsearchdir_ret = searchdir;
goto unionlookup;
}
@@ -1089,84 +1084,99 @@ unionlookup:
}
/*
- * "searchdir" is locked and held, "foundobj" is held,
- * they may be the same vnode.
- */
- if (searchdir != foundobj) {
- if (cnp->cn_flags & ISDOTDOT)
- VOP_UNLOCK(searchdir);
- error = vn_lock(foundobj, LK_EXCLUSIVE);
- if (cnp->cn_flags & ISDOTDOT)
- vn_lock(searchdir, LK_EXCLUSIVE | LK_RETRY);
- if (error != 0) {
- vrele(foundobj);
- goto done;
- }
- }
-
- /*
- * Check to see if the vnode has been mounted on;
- * if so find the root of the mounted file system.
+ * Do an unlocked check to see if the vnode has been mounted on; if
+ * so find the root of the mounted file system.
*/
KASSERT(searchdir != NULL);
- while (foundobj->v_type == VDIR &&
- (mp = foundobj->v_mountedhere) != NULL &&
- (cnp->cn_flags & NOCROSSMOUNT) == 0) {
+ if (foundobj->v_type == VDIR && foundobj->v_mountedhere != NULL &&
+ (cnp->cn_flags & NOCROSSMOUNT) == 0) {
+ /*
+ * "searchdir" is locked and held, "foundobj" is held,
+ * they may be the same vnode.
+ */
+ if (searchdir != foundobj) {
+ if (vn_lock(foundobj, LK_EXCLUSIVE | LK_NOWAIT) != 0) {
+ if (cnp->cn_flags & ISDOTDOT)
+ VOP_UNLOCK(searchdir);
+ error = vn_lock(foundobj, LK_EXCLUSIVE);
+ if (cnp->cn_flags & ISDOTDOT)
+ vn_lock(searchdir, LK_EXCLUSIVE | LK_RETRY);
+ if (error != 0) {
+ vrele(foundobj);
+ goto done;
+ }
+ }
+ }
+ while (foundobj->v_type == VDIR &&
+ (mp = foundobj->v_mountedhere) != NULL &&
+ (cnp->cn_flags & NOCROSSMOUNT) == 0) {
- KASSERT(searchdir != foundobj);
+ KASSERT(searchdir != foundobj);
- error = vfs_busy(mp);
- if (error != 0) {
- vput(foundobj);
- goto done;
- }
- if (searchdir != NULL) {
- VOP_UNLOCK(searchdir);
- }
- vput(foundobj);
- error = VFS_ROOT(mp, &foundobj);
- vfs_unbusy(mp);
- if (error) {
+ error = vfs_busy(mp);
+ if (error != 0) {
+ vput(foundobj);
+ goto done;
+ }
if (searchdir != NULL) {
+ VOP_UNLOCK(searchdir);
+ searchdir_locked = false;
+ }
+ vput(foundobj);
+ error = VFS_ROOT(mp, &foundobj);
+ vfs_unbusy(mp);
+ if (error) {
+ if (searchdir != NULL) {
+ vn_lock(searchdir, LK_EXCLUSIVE | LK_RETRY);
+ searchdir_locked = true;
+ }
+ goto done;
+ }
+ /*
+ * Avoid locking vnodes from two filesystems because
+ * it's prone to deadlock, e.g. when using puffs.
+ * Also, it isn't a good idea to propagate slowness of
+ * a filesystem up to the root directory. For now,
+ * only handle the common case, where foundobj is
+ * VDIR.
+ *
+ * In this case set searchdir to null to avoid using
+ * it again. It is not correct to set searchdir ==
+ * foundobj here as that will confuse the caller.
+ * (See PR 47040.)
+ */
+ if (searchdir == NULL) {
+ /* already been here once; do nothing further */
+ } else if (foundobj->v_type == VDIR) {
+ vrele(searchdir);
+ *newsearchdir_ret = searchdir = NULL;
+ } else {
+ VOP_UNLOCK(foundobj);
vn_lock(searchdir, LK_EXCLUSIVE | LK_RETRY);
+ vn_lock(foundobj, LK_EXCLUSIVE | LK_RETRY);
+ searchdir_locked = true;
}
- goto done;
}
- /*
- * Avoid locking vnodes from two filesystems because
- * it's prone to deadlock, e.g. when using puffs.
- * Also, it isn't a good idea to propagate slowness of
- * a filesystem up to the root directory. For now,
- * only handle the common case, where foundobj is
- * VDIR.
- *
- * In this case set searchdir to null to avoid using
- * it again. It is not correct to set searchdir ==
- * foundobj here as that will confuse the caller.
- * (See PR 40740.)
- */
- if (searchdir == NULL) {
- /* already been here once; do nothing further */
- } else if (foundobj->v_type == VDIR) {
- vrele(searchdir);
- *newsearchdir_ret = searchdir = NULL;
- } else {
- VOP_UNLOCK(foundobj);
- vn_lock(searchdir, LK_EXCLUSIVE | LK_RETRY);
- vn_lock(foundobj, LK_EXCLUSIVE | LK_RETRY);
+ KASSERT(searchdir != foundobj);
+ VOP_UNLOCK(foundobj);
+ }
+
+ /* Unlock, unless the caller needs the parent locked. */
+ if (searchdir != NULL) {
+ KASSERT(searchdir_locked);
+ if ((cnp->cn_flags & (ISLASTCN | LOCKPARENT)) !=
+ (ISLASTCN | LOCKPARENT)) {
+ VOP_UNLOCK(searchdir);
+ searchdir_locked = false;
}
+ } else {
+ KASSERT(!searchdir_locked);
}
*foundobj_ret = foundobj;
error = 0;
done:
- KASSERT(*newsearchdir_ret == NULL ||
- VOP_ISLOCKED(*newsearchdir_ret) == LK_EXCLUSIVE);
- /*
- * *foundobj_ret is valid only if error == 0.
- */
- KASSERT(error != 0 || *foundobj_ret == NULL ||
- VOP_ISLOCKED(*foundobj_ret) == LK_EXCLUSIVE);
+ *newsearchdir_locked_ret = searchdir_locked;
return error;
}
@@ -1183,6 +1193,7 @@ namei_oneroot(struct namei_state *state,
struct nameidata *ndp = state->ndp;
struct componentname *cnp = state->cnp;
struct vnode *searchdir, *foundobj;
+ bool searchdir_locked = false;
int error;
error = namei_start(state, isnfsd, &searchdir);
@@ -1223,7 +1234,7 @@ namei_oneroot(struct namei_state *state,
for (;;) {
KASSERT(searchdir != NULL);
- KASSERT(VOP_ISLOCKED(searchdir) == LK_EXCLUSIVE);
+ KASSERT(!searchdir_locked);
/*
* If the directory we're on is unmounted, bail out.
@@ -1231,7 +1242,7 @@ namei_oneroot(struct namei_state *state,
* XXX: yes it should... but how?
*/
if (searchdir->v_mount == NULL) {
- vput(searchdir);
+ vrele(searchdir);
ndp->ni_dvp = NULL;
ndp->ni_vp = NULL;
return (ENOENT);
@@ -1250,17 +1261,23 @@ namei_oneroot(struct namei_state *state,
error = lookup_parsepath(state);
if (error) {
- vput(searchdir);
+ vrele(searchdir);
ndp->ni_dvp = NULL;
ndp->ni_vp = NULL;
state->attempt_retry = 1;
return (error);
}
- error = lookup_once(state, searchdir, &searchdir, &foundobj);
+ error = lookup_once(state, searchdir, &searchdir, &foundobj,
+ &searchdir_locked);
if (error) {
if (searchdir != NULL) {
- vput(searchdir);
+ if (searchdir_locked) {
+ searchdir_locked = false;
+ vput(searchdir);
+ } else {
+ vrele(searchdir);
+ }
}
ndp->ni_dvp = NULL;
ndp->ni_vp = NULL;
@@ -1297,6 +1314,11 @@ namei_oneroot(struct namei_state *state,
* them again.
*/
if (namei_atsymlink(state, foundobj)) {
+ /* Don't need searchdir locked any more. */
+ if (searchdir_locked) {
+ searchdir_locked = false;
+ VOP_UNLOCK(searchdir);
+ }
ndp->ni_pathlen += state->slashes;
ndp->ni_next -= state->slashes;
if (neverfollow) {
@@ -1338,14 +1360,13 @@ namei_oneroot(struct namei_state *state,
if (error) {
KASSERT(searchdir != foundobj);
if (searchdir != NULL) {
- vput(searchdir);
+ vrele(searchdir);
}
- vput(foundobj);
+ vrele(foundobj);
ndp->ni_dvp = NULL;
ndp->ni_vp = NULL;
return error;
}
- /* namei_follow unlocks it (ugh) so rele, not put */
vrele(foundobj);
foundobj = NULL;
@@ -1376,9 +1397,16 @@ namei_oneroot(struct namei_state *state,
(cnp->cn_flags & REQUIREDIR)) {
KASSERT(foundobj != searchdir);
if (searchdir) {
- vput(searchdir);
+ if (searchdir_locked) {
+ searchdir_locked = false;
+ vput(searchdir);
+ } else {
+ vrele(searchdir);
+ }
+ } else {
+ KASSERT(!searchdir_locked);
}
- vput(foundobj);
+ vrele(foundobj);
ndp->ni_dvp = NULL;
ndp->ni_vp = NULL;
state->attempt_retry = 1;
@@ -1396,15 +1424,21 @@ namei_oneroot(struct namei_state *state,
* Continue with the next component.
*/
cnp->cn_nameptr = ndp->ni_next;
- if (searchdir == foundobj) {
- vrele(searchdir);
- } else if (searchdir != NULL) {
- vput(searchdir);
+ if (searchdir != NULL) {
+ if (searchdir_locked) {
+ searchdir_locked = false;
+ vput(searchdir);
+ } else {
+ vrele(searchdir);
+ }
}
searchdir = foundobj;
foundobj = NULL;
}
+ KASSERT((cnp->cn_flags & LOCKPARENT) == 0 || searchdir == NULL ||
+ VOP_ISLOCKED(searchdir) == LK_EXCLUSIVE);
+
skiploop:
if (foundobj != NULL) {
@@ -1417,16 +1451,15 @@ namei_oneroot(struct namei_state *state,
* forever. So convert it to the real root.
*/
if (searchdir != NULL) {
- if (searchdir == foundobj)
- vrele(searchdir);
- else
- vput(searchdir);
+ if ((cnp->cn_flags & LOCKPARENT) != 0) {
+ VOP_UNLOCK(searchdir);
+ }
+ vrele(searchdir);
searchdir = NULL;
}
- vput(foundobj);
+ vrele(foundobj);
foundobj = ndp->ni_rootdir;
vref(foundobj);
- vn_lock(foundobj, LK_EXCLUSIVE | LK_RETRY);
}
/*
@@ -1439,9 +1472,12 @@ namei_oneroot(struct namei_state *state,
(searchdir == NULL ||
searchdir->v_mount != foundobj->v_mount)) {
if (searchdir) {
- vput(searchdir);
+ if ((cnp->cn_flags & LOCKPARENT) != 0) {
+ VOP_UNLOCK(searchdir);
+ }
+ vrele(searchdir);
}
- vput(foundobj);
+ vrele(foundobj);
foundobj = NULL;
ndp->ni_dvp = NULL;
ndp->ni_vp = NULL;
@@ -1466,21 +1502,23 @@ namei_oneroot(struct namei_state *state,
if (state->rdonly &&
(cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) {
if (searchdir) {
- if (foundobj != searchdir) {
- vput(searchdir);
- } else {
- vrele(searchdir);
+ if ((cnp->cn_flags & LOCKPARENT) != 0) {
+ VOP_UNLOCK(searchdir);
}
+ vrele(searchdir);
searchdir = NULL;
}
- vput(foundobj);
+ vrele(foundobj);
foundobj = NULL;
ndp->ni_dvp = NULL;
ndp->ni_vp = NULL;
state->attempt_retry = 1;
return EROFS;
}
- if ((cnp->cn_flags & LOCKLEAF) == 0) {
+
+ /* Lock the leaf node if requested. */
+ if ((cnp->cn_flags & (LOCKLEAF | LOCKPARENT)) == LOCKPARENT &&
+ searchdir == foundobj) {
/*
* Note: if LOCKPARENT but not LOCKLEAF is
* set, and searchdir == foundobj, this code
@@ -1492,7 +1530,11 @@ namei_oneroot(struct namei_state *state,
* that uses this combination "knows" this, so
* it can't be safely changed. Feh. XXX
*/
- VOP_UNLOCK(foundobj);
+ VOP_UNLOCK(searchdir);
+ } else if ((cnp->cn_flags & LOCKLEAF) != 0 &&
+ (searchdir != foundobj ||
+ (cnp->cn_flags & LOCKPARENT) == 0)) {
+ vn_lock(foundobj, LK_EXCLUSIVE | LK_RETRY);
}
}
@@ -1504,11 +1546,7 @@ namei_oneroot(struct namei_state *state,
* If LOCKPARENT is not set, the parent directory isn't returned.
*/
if ((cnp->cn_flags & LOCKPARENT) == 0 && searchdir != NULL) {
- if (searchdir == foundobj) {
- vrele(searchdir);
- } else {
- vput(searchdir);
- }
+ vrele(searchdir);
searchdir = NULL;
}
@@ -1645,11 +1683,11 @@ static int
do_lookup_for_nfsd_index(struct namei_state *state)
{
int error = 0;
-
struct componentname *cnp = state->cnp;
struct nameidata *ndp = state->ndp;
struct vnode *startdir;
struct vnode *foundobj;
+ bool startdir_locked;
const char *cp; /* pointer into pathname argument */
KASSERT(cnp == &ndp->ni_cnd);
@@ -1682,12 +1720,17 @@ do_lookup_for_nfsd_index(struct namei_st
* own reference to it to avoid consuming the caller's.
*/
vref(startdir);
- vn_lock(startdir, LK_EXCLUSIVE | LK_RETRY);
- error = lookup_once(state, startdir, &startdir, &foundobj);
+ error = lookup_once(state, startdir, &startdir, &foundobj,
+ &startdir_locked);
+ KASSERT(!startdir_locked);
if (error == 0 && startdir == foundobj) {
vrele(startdir);
} else if (startdir != NULL) {
- vput(startdir);
+ if (startdir_locked) {
+ vput(startdir);
+ } else {
+ vrele(startdir);
+ }
}
if (error) {
goto bad;
@@ -1698,9 +1741,8 @@ do_lookup_for_nfsd_index(struct namei_st
return 0;
}
- KASSERT((cnp->cn_flags & LOCKPARENT) == 0);
- if ((cnp->cn_flags & LOCKLEAF) == 0) {
- VOP_UNLOCK(foundobj);
+ if ((cnp->cn_flags & LOCKLEAF) != 0) {
+ vn_lock(foundobj, LK_EXCLUSIVE | LK_RETRY);
}
return (0);