git: 80bd5ef07025 - main - vfs: factor out mount point traversal to a dedicated routine

From: Mateusz Guzik <mjg_at_FreeBSD.org>
Date: Fri, 07 Jul 2023 01:15:47 UTC
The branch main has been updated by mjg:

URL: https://cgit.FreeBSD.org/src/commit/?id=80bd5ef0702562c546fa1717e8fe221058974eac

commit 80bd5ef0702562c546fa1717e8fe221058974eac
Author:     Mateusz Guzik <mjg@FreeBSD.org>
AuthorDate: 2023-07-05 23:01:17 +0000
Commit:     Mateusz Guzik <mjg@FreeBSD.org>
CommitDate: 2023-07-07 01:15:04 +0000

    vfs: factor out mount point traversal to a dedicated routine
    
    While here tidy up asserts in the area.
    
    Reviewed by:    kib
    Differential Revision:  https://reviews.freebsd.org/D40883
---
 sys/kern/vfs_lookup.c | 142 +++++++++++++++++++++++++++++---------------------
 1 file changed, 82 insertions(+), 60 deletions(-)

diff --git a/sys/kern/vfs_lookup.c b/sys/kern/vfs_lookup.c
index 5a5560a4c26f..1f62cf3e6652 100644
--- a/sys/kern/vfs_lookup.c
+++ b/sys/kern/vfs_lookup.c
@@ -871,6 +871,79 @@ vfs_lookup_failifexists(struct nameidata *ndp)
 	return (EEXIST);
 }
 
+static int __noinline
+vfs_lookup_cross_mount(struct nameidata *ndp)
+{
+	struct componentname *cnp;
+	struct mount *mp;
+	struct vnode *dp, *tdp;
+	int error, crosslkflags;
+	bool crosslock;
+
+	cnp = &ndp->ni_cnd;
+	dp = ndp->ni_vp;
+
+	/*
+	 * The vnode has been mounted on, find the root of the mounted
+	 * filesystem.
+	 */
+	for (;;) {
+		mp = dp->v_mountedhere;
+		ASSERT_VOP_LOCKED(dp, __func__);
+		VNPASS((vn_irflag_read(dp) & VIRF_MOUNTPOINT) != 0 && mp != NULL, dp);
+
+		crosslock = (dp->v_vflag & VV_CROSSLOCK) != 0;
+		crosslkflags = enforce_lkflags(mp, cnp->cn_lkflags);
+		if (__predict_false(crosslock)) {
+			/*
+			 * We are going to be holding the vnode lock, which
+			 * in this case is shared by the root vnode of the
+			 * filesystem mounted at mp, across the call to
+			 * VFS_ROOT().  Make the situation clear to the
+			 * filesystem by passing LK_CANRECURSE if the
+			 * lock is held exclusive, or by clearinng
+			 * LK_NODDLKTREAT to allow recursion on the shared
+			 * lock in the presence of an exclusive waiter.
+			 */
+			if (VOP_ISLOCKED(dp) == LK_EXCLUSIVE) {
+				crosslkflags &= ~LK_SHARED;
+				crosslkflags |= LK_EXCLUSIVE | LK_CANRECURSE;
+			} else if ((crosslkflags & LK_EXCLUSIVE) != 0) {
+				error = vn_lock(dp, LK_UPGRADE);
+				if (error != 0)
+					break;
+				if (dp->v_mountedhere != mp) {
+					continue;
+				}
+			} else
+				crosslkflags &= ~LK_NODDLKTREAT;
+		}
+		if (vfs_busy(mp, 0) != 0)
+			continue;
+		if (__predict_true(!crosslock))
+			vput(dp);
+		if (dp != ndp->ni_dvp)
+			vput(ndp->ni_dvp);
+		else
+			vrele(ndp->ni_dvp);
+		vrefact(vp_crossmp);
+		ndp->ni_dvp = vp_crossmp;
+		error = VFS_ROOT(mp, crosslkflags, &tdp);
+		vfs_unbusy(mp);
+		if (__predict_false(crosslock))
+			vput(dp);
+		if (vn_lock(vp_crossmp, LK_SHARED | LK_NOWAIT))
+			panic("vp_crossmp exclusively locked or reclaimed");
+		if (error != 0)
+			break;
+		ndp->ni_vp = dp = tdp;
+		if ((vn_irflag_read(dp) & VIRF_MOUNTPOINT) == 0)
+			break;
+	}
+
+	return (error);
+}
+
 /*
  * Search a pathname.
  * This is a very central and rather complicated routine.
@@ -919,7 +992,6 @@ vfs_lookup(struct nameidata *ndp)
 	char *lastchar;			/* location of the last character */
 	struct vnode *dp = NULL;	/* the directory we are searching */
 	struct vnode *tdp;		/* saved dp */
-	struct mount *mp;		/* mount table entry */
 	struct prison *pr;
 	size_t prev_ni_pathlen;		/* saved ndp->ni_pathlen */
 	int docache;			/* == 0 do not cache last component */
@@ -930,8 +1002,6 @@ vfs_lookup(struct nameidata *ndp)
 	struct componentname *cnp = &ndp->ni_cnd;
 	int lkflags_save;
 	int ni_dvp_unlocked;
-	int crosslkflags;
-	bool crosslock;
 
 	/*
 	 * Setup: break out flag bits into variables.
@@ -1283,66 +1353,18 @@ good:
 			ni_dvp_unlocked = 1;
 		}
 		goto success;
-	} else if ((vn_irflag_read(dp) & VIRF_MOUNTPOINT) != 0) {
-		if ((cnp->cn_flags & NOCROSSMOUNT) != 0)
-			goto nextname;
-	} else
-		goto nextname;
+	}
 
-	/*
-	 * Check to see if the vnode has been mounted on;
-	 * if so find the root of the mounted filesystem.
-	 */
-	do {
-		mp = dp->v_mountedhere;
-		KASSERT(mp != NULL,
-		    ("%s: NULL mountpoint for VIRF_MOUNTPOINT vnode", __func__));
-		crosslock = (dp->v_vflag & VV_CROSSLOCK) != 0;
-		crosslkflags = enforce_lkflags(mp, cnp->cn_lkflags);
-		if (__predict_false(crosslock)) {
-			/*
-			 * We are going to be holding the vnode lock, which
-			 * in this case is shared by the root vnode of the
-			 * filesystem mounted at mp, across the call to
-			 * VFS_ROOT().  Make the situation clear to the
-			 * filesystem by passing LK_CANRECURSE if the
-			 * lock is held exclusive, or by clearinng
-			 * LK_NODDLKTREAT to allow recursion on the shared
-			 * lock in the presence of an exclusive waiter.
-			 */
-			if (VOP_ISLOCKED(dp) == LK_EXCLUSIVE) {
-				crosslkflags &= ~LK_SHARED;
-				crosslkflags |= LK_EXCLUSIVE | LK_CANRECURSE;
-			} else if ((crosslkflags & LK_EXCLUSIVE) != 0) {
-				error = vn_lock(dp, LK_UPGRADE);
-				if (error != 0)
-					goto bad_unlocked;
-				if (dp->v_mountedhere != mp) {
-					continue;
-				}
-			} else
-				crosslkflags &= ~LK_NODDLKTREAT;
-		}
-		if (vfs_busy(mp, 0) != 0)
-			continue;
-		if (__predict_true(!crosslock))
-			vput(dp);
-		if (dp != ndp->ni_dvp)
-			vput(ndp->ni_dvp);
-		else
-			vrele(ndp->ni_dvp);
-		vrefact(vp_crossmp);
-		ndp->ni_dvp = vp_crossmp;
-		error = VFS_ROOT(mp, crosslkflags, &tdp);
-		vfs_unbusy(mp);
-		if (__predict_false(crosslock))
-			vput(dp);
-		if (vn_lock(vp_crossmp, LK_SHARED | LK_NOWAIT))
-			panic("vp_crossmp exclusively locked or reclaimed");
+	if ((vn_irflag_read(dp) & VIRF_MOUNTPOINT) != 0 &&
+	    (cnp->cn_flags & NOCROSSMOUNT) == 0) {
+		error = vfs_lookup_cross_mount(ndp);
 		if (error != 0)
 			goto bad_unlocked;
-		ndp->ni_vp = dp = tdp;
-	} while ((vn_irflag_read(dp) & VIRF_MOUNTPOINT) != 0);
+		/*
+		 * FALLTHROUGH to nextname
+		 */
+		dp = ndp->ni_vp;
+	}
 
 nextname:
 	/*