aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMateusz Guzik <mjg@FreeBSD.org>2023-07-05 23:01:17 +0000
committerMateusz Guzik <mjg@FreeBSD.org>2023-07-07 01:15:04 +0000
commit80bd5ef0702562c546fa1717e8fe221058974eac (patch)
tree1ddcd3db63380cecf10d373e270c0199b81f88b4
parentebf37c3fed04f10d452b426cf83e741b6365a2ec (diff)
downloadsrc-80bd5ef0702562c546fa1717e8fe221058974eac.tar.gz
src-80bd5ef0702562c546fa1717e8fe221058974eac.zip
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
-rw-r--r--sys/kern/vfs_lookup.c142
1 files 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:
/*