unionfs: remove LK_UPGRADE if falling back to the standard lock

The LK_UPGRADE operation may have temporarily dropped the upper or
lower vnode's lock.  If the unionfs vnode was reclaimed during that
window, its lock field will be reset to no longer point at the
upper/lower vnode lock, so the lock operation will use the standard
lock stored in v_lock.  Remove LK_UPGRADE from the flags in this case
to avoid a lockmgr assertion, as this lock has not been previously
owned by the calling thread.

Reported by:	pho
Tested by:	pho
Reviewed by:	kib, markj
Differential Revision:	https://reviews.freebsd.org/D39272
This commit is contained in:
Jason A. Harmening 2023-01-16 15:46:59 -06:00
parent 21ef48af5c
commit a18c403fbd

View File

@ -2010,7 +2010,7 @@ unionfs_lock(struct vop_lock1_args *ap)
vdrop(lvp);
if (uhold != 0)
vdrop(uvp);
return (vop_stdlock(ap));
goto unionfs_lock_fallback;
}
}
@ -2043,7 +2043,7 @@ unionfs_lock(struct vop_lock1_args *ap)
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(struct vop_lock1_args *ap)
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