git: a18c403fbddb - main - unionfs: remove LK_UPGRADE if falling back to the standard lock

From: Jason A. Harmening <jah_at_FreeBSD.org>
Date: Tue, 18 Apr 2023 01:35:41 UTC
The branch main has been updated by jah:

URL: https://cgit.FreeBSD.org/src/commit/?id=a18c403fbddb0fa9650f133a026f40ad33751134

commit a18c403fbddb0fa9650f133a026f40ad33751134
Author:     Jason A. Harmening <jah@FreeBSD.org>
AuthorDate: 2023-01-16 21:46:59 +0000
Commit:     Jason A. Harmening <jah@FreeBSD.org>
CommitDate: 2023-04-18 01:31:40 +0000

    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
---
 sys/fs/unionfs/union_vnops.c | 20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)

diff --git a/sys/fs/unionfs/union_vnops.c b/sys/fs/unionfs/union_vnops.c
index 04d788959892..f78e653280a4 100644
--- a/sys/fs/unionfs/union_vnops.c
+++ b/sys/fs/unionfs/union_vnops.c
@@ -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