Module Name:    src
Committed By:   ad
Date:           Wed Jan 22 12:10:46 UTC 2020

Modified Files:
        src/sys/kern [ad-namecache]: vfs_lookup.c

Log Message:
Fast-forward through the namecache was stopping one component too soon when
there was an obstacle, e.g. a mountpoint.  The obstacle should be returned
not the parent directory.


To generate a diff of this commit:
cvs rdiff -u -r1.212.4.4 -r1.212.4.5 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.4.4 src/sys/kern/vfs_lookup.c:1.212.4.5
--- src/sys/kern/vfs_lookup.c:1.212.4.4	Sun Jan 19 21:19:25 2020
+++ src/sys/kern/vfs_lookup.c	Wed Jan 22 12:10:46 2020
@@ -1,4 +1,4 @@
-/*	$NetBSD: vfs_lookup.c,v 1.212.4.4 2020/01/19 21:19:25 ad Exp $	*/
+/*	$NetBSD: vfs_lookup.c,v 1.212.4.5 2020/01/22 12:10:46 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.4.4 2020/01/19 21:19:25 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: vfs_lookup.c,v 1.212.4.5 2020/01/22 12:10:46 ad Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_magiclinks.h"
@@ -954,7 +954,11 @@ lookup_crossmount(struct namei_state *st
 	    (mp = foundobj->v_mountedhere) != NULL &&
 	    (cnp->cn_flags & NOCROSSMOUNT) == 0) {
 		KASSERTMSG(searchdir != foundobj, "same vn %p", searchdir);
-		/* First get the vnode stable. */
+		/*
+		 * First get the vnode stable.  LK_SHARED works brilliantly
+		 * here because almost nothing else wants to lock the
+		 * covered vnode.
+		 */
 		error = vn_lock(foundobj, LK_SHARED);
 		if (error != 0) {
 			vrele(foundobj);
@@ -962,7 +966,7 @@ lookup_crossmount(struct namei_state *st
 			break;
 		}
 
-		/* Then check to see if something is still mounted there. */
+		/* Then check to see if something is still mounted on it. */
 		if ((mp = foundobj->v_mountedhere) == NULL) {
 			VOP_UNLOCK(foundobj);
 			break;
@@ -1109,7 +1113,9 @@ lookup_once(struct namei_state *state,
 	/*
 	 * If the file system supports VOP_LOOKUP() with a shared lock, and
 	 * we are not making any modifications (nameiop LOOKUP) or this is
-	 * not the last component then get a shared lock LK_SHARED.
+	 * not the last component then get a shared lock LK_SHARED.  Where
+	 * we can't do fast-forwarded lookups (for example with layered file
+	 * systems) then this is the fallback for reducing lock contention.
 	 */
 	if ((searchdir->v_mount->mnt_iflag & IMNT_SHRLOOKUP) != 0 &&
 	    (cnp->cn_nameiop == LOOKUP || (cnp->cn_flags & ISLASTCN) == 0)) {
@@ -1237,8 +1243,8 @@ done:
 /*
  * Parse out the first path name component that we need to to consider. 
  *
- * While doing this, attempt to use the name cache to fastforward through as
- * many "easy" to find components of the path as possible.
+ * While doing this, attempt to use the name cache to fast-forward through
+ * as many "easy" to find components of the path as possible.
  *
  * We use the namecache's node locks to form a chain, and avoid as many
  * vnode references and locks as possible.  In the ideal case, only the
@@ -1280,8 +1286,7 @@ lookup_fastforward(struct namei_state *s
 		/*
 		 * Can't deal with dotdot lookups, because it means lock
 		 * order reversal, and there are checks in lookup_once()
-		 * that need to be made.  Also check for missing mountpoints
-		 * (XXX racy).
+		 * that need to be made.  Also check for missing mountpoints.
 		 */
 		if ((cnp->cn_flags & ISDOTDOT) != 0 ||
 		    (*searchdir)->v_mount == NULL) {
@@ -1322,8 +1327,16 @@ lookup_fastforward(struct namei_state *s
 			break;
 		}
 
-		/* Stop if we've reached the last component: get vnode. */
-		if (cnp->cn_flags & ISLASTCN) {
+		/*
+		 * Stop and get a hold on the vnode if there's something
+		 * that can't be handled here:
+		 *
+		 * - we've reached the last component.
+		 * - or encountered a mount point that needs to be crossed.
+		 * - or encountered something other than a directory.
+		 */
+		if ((cnp->cn_flags & ISLASTCN) != 0 || vp->v_type != VDIR ||
+		    (vp->v_type == VDIR && vp->v_mountedhere != NULL)) {
 			mutex_enter(vp->v_interlock);
 			error = vcache_tryvget(vp);
 			/* v_interlock now released */
@@ -1335,17 +1348,6 @@ lookup_fastforward(struct namei_state *s
 		}
 
 		/*
-		 * Not the last component.  If we found something other than
-		 * a directory, or it's a directory with a filesystem
-		 * mounted on it, bail out.
-		 */
-		if (vp->v_type != VDIR || vp->v_mountedhere != NULL) {
-			error = EOPNOTSUPP;
-			vp = NULL;
-			break;
-		}
-
-		/*
 		 * Otherwise, we're still in business.  Set the found VDIR
 		 * vnode as the search dir for the next component and
 		 * continue on to it.
@@ -1365,9 +1367,8 @@ lookup_fastforward(struct namei_state *s
 		mutex_enter((*searchdir)->v_interlock);
 		error2 = vcache_tryvget(*searchdir);
 		/* v_interlock now unheld */
-		if (plock != NULL) {
-			rw_exit(plock);
-		}
+		KASSERT(plock != NULL);
+		rw_exit(plock);
 		if (__predict_true(error2 == 0)) {
 			vrele(origsearchdir);
 		} else {
@@ -1447,7 +1448,7 @@ namei_oneroot(struct namei_state *state,
 		/*
 		 * Parse out the first path name component that we need to
 		 * to consider.  While doing this, attempt to use the name
-		 * cache to fastforward through as many "easy" to find
+		 * cache to fast-forward through as many "easy" to find
 		 * components of the path as possible.
 		 */
 		error = lookup_fastforward(state, &searchdir, &foundobj);

Reply via email to