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
This commit is contained in:
Mateusz Guzik 2023-07-05 23:01:17 +00:00
parent ebf37c3fed
commit 80bd5ef070

View file

@ -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:
/*