diff options
| author | Robert Wing <rew@FreeBSD.org> | 2026-07-03 18:55:37 +0000 |
|---|---|---|
| committer | Robert Wing <rew@FreeBSD.org> | 2026-07-03 18:55:37 +0000 |
| commit | 2de20c5c77cf8d5b2059054cff0e0c1fc124739d (patch) | |
| tree | d2eaaf7f0d5295d31b9671c23a91c828ffa2d38e | |
| parent | e89a398c108ff4db2b7985ad591e62b9e1ea7222 (diff) | |
autofs: enable witness for autofs node lock
Previously, an_vnode_lock was initialized with SX_NOWITNESS to silence
lock order reversals. The reversals would occur when autofs_node_vn()
was called with the directory vnode lock held, then lock an_vnode_lock,
then lock the vnode attached to the autofs node. It looked like:
directory vnode -> an_vnode_lock -> vnode attached to autofs node
The established lock order is now vnode -> an_vnode_lock
Currently, we don't have to worry about losing an autofs node during the
unlock/lock as autofs nodes are only removed during an unmount() after
vflush(). When autofs_node_vn() is called, the mountpoint has either
been busied (preventing unmount) or a directory vnode is locked which
prevents vflush() from finishing until the directory vnode is unlocked.
Reviewed by: kib
Differential Revision: https://reviews.freebsd.org/D57857
| -rw-r--r-- | sys/fs/autofs/autofs_vnops.c | 44 |
1 files changed, 18 insertions, 26 deletions
diff --git a/sys/fs/autofs/autofs_vnops.c b/sys/fs/autofs/autofs_vnops.c index e8e47f20166a..b6424fc2ff4c 100644 --- a/sys/fs/autofs/autofs_vnops.c +++ b/sys/fs/autofs/autofs_vnops.c @@ -572,16 +572,7 @@ autofs_node_new(struct autofs_node *parent, struct autofs_mount *amp, anp->an_name = strdup(name, M_AUTOFS); anp->an_fileno = atomic_fetchadd_int(&->am_last_fileno, 1); callout_init(&anp->an_callout, 1); - /* - * The reason for SX_NOWITNESS here is that witness(4) - * cannot tell vnodes apart, so the following perfectly - * valid lock order... - * - * vnode lock A -> autofsvlk B -> vnode lock B - * - * ... gets reported as a LOR. - */ - sx_init_flags(&anp->an_vnode_lock, "autofsvlk", SX_NOWITNESS); + sx_init(&anp->an_vnode_lock, "autofsvlk"); getnanotime(&anp->an_ctime); anp->an_parent = parent; anp->an_mount = amp; @@ -645,6 +636,7 @@ autofs_node_vn(struct autofs_node *anp, struct mount *mp, int flags, { struct vnode *vp; int error; + enum vgetstate vs; AUTOFS_ASSERT_UNLOCKED(anp->an_mount); @@ -652,53 +644,53 @@ autofs_node_vn(struct autofs_node *anp, struct mount *mp, int flags, vp = anp->an_vnode; if (vp != NULL) { - error = vget(vp, flags | LK_RETRY); - if (error != 0) { - AUTOFS_WARN("vget failed with error %d", error); - sx_xunlock(&anp->an_vnode_lock); - return (error); - } +lockvp: + vs = vget_prep(vp); + sx_xunlock(&anp->an_vnode_lock); + vget_finish(vp, flags | LK_RETRY, vs); if (VN_IS_DOOMED(vp)) { /* * We got forcibly unmounted. */ AUTOFS_DEBUG("doomed vnode"); - sx_xunlock(&anp->an_vnode_lock); vput(vp); return (ENOENT); } *vpp = vp; - sx_xunlock(&anp->an_vnode_lock); return (0); } + sx_xunlock(&anp->an_vnode_lock); error = getnewvnode("autofs", mp, &autofs_vnodeops, &vp); - if (error != 0) { - sx_xunlock(&anp->an_vnode_lock); + if (error != 0) return (error); - } vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); vp->v_type = VDIR; if (anp->an_parent == NULL) vp->v_vflag |= VV_ROOT; - vp->v_data = anp; - VN_LOCK_ASHARE(vp); error = insmntque(vp, mp); if (error != 0) { AUTOFS_DEBUG("insmntque() failed with error %d", error); - sx_xunlock(&anp->an_vnode_lock); return (error); } - KASSERT(anp->an_vnode == NULL, ("lost race")); + sx_xlock(&anp->an_vnode_lock); + if (anp->an_vnode != NULL) { + vp->v_op = &dead_vnodeops; + vp->v_data = NULL; + vgone(vp); + vput(vp); + vp = anp->an_vnode; + goto lockvp; + } + vp->v_data = anp; anp->an_vnode = vp; - sx_xunlock(&anp->an_vnode_lock); vn_set_state(vp, VSTATE_CONSTRUCTED); |
