diff --git a/sys/fs/unionfs/union_vnops.c b/sys/fs/unionfs/union_vnops.c --- a/sys/fs/unionfs/union_vnops.c +++ b/sys/fs/unionfs/union_vnops.c @@ -2010,7 +2010,7 @@ vdrop(lvp); if (uhold != 0) vdrop(uvp); - return (vop_stdlock(ap)); + goto unionfs_lock_fallback; } } @@ -2043,7 +2043,7 @@ VOP_UNLOCK(lvp); vdrop(lvp); } - return (vop_stdlock(ap)); + goto unionfs_lock_fallback; } if (error != 0 && lvp != NULLVP) { /* rollback */ @@ -2065,6 +2065,22 @@ unionfs_lock_null_vnode: ap->a_flags |= LK_INTERLOCK; return (vop_stdlock(ap)); + +unionfs_lock_fallback: + /* + * If we reach this point, we've discovered the unionfs vnode + * has been reclaimed while the upper/lower vnode locks were + * temporarily dropped. Such temporary droppage may happen + * during the course of an LK_UPGRADE operation itself, and in + * that case LK_UPGRADE must be cleared as the unionfs vnode's + * lock has been reset to point to the standard v_lock field, + * which has not previously been held. + */ + if (flags & LK_UPGRADE) { + ap->a_flags &= ~LK_TYPE_MASK; + ap->a_flags |= LK_EXCLUSIVE; + } + return (vop_stdlock(ap)); } static int