Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 19 Oct 2020 19:23:22 +0000 (UTC)
From:      Konstantin Belousov <kib@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r366849 - head/sys/fs/nullfs
Message-ID:  <202010191923.09JJNMaV035419@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: kib
Date: Mon Oct 19 19:23:22 2020
New Revision: 366849
URL: https://svnweb.freebsd.org/changeset/base/366849

Log:
  nullfs: ensure correct lock is taken after bypass.
  
  If lower VOP relocked the lower vnode, it is possible that nullfs
  vnode was reclaimed meantime.  In this case nullfs vnode no longer
  shares lock with lower vnode, which breaks locking protocol.
  
  Check for the condition and acquire nullfs vnode lock if detected.
  
  Reported and tested by:	pho
  Sponsored by:	The FreeBSD Foundation
  MFC after:	1 week

Modified:
  head/sys/fs/nullfs/null_vnops.c

Modified: head/sys/fs/nullfs/null_vnops.c
==============================================================================
--- head/sys/fs/nullfs/null_vnops.c	Mon Oct 19 19:20:23 2020	(r366848)
+++ head/sys/fs/nullfs/null_vnops.c	Mon Oct 19 19:23:22 2020	(r366849)
@@ -227,6 +227,7 @@ null_bypass(struct vop_generic_args *ap)
 	struct vnode *old_vps[VDESC_MAX_VPS];
 	struct vnode **vps_p[VDESC_MAX_VPS];
 	struct vnode ***vppp;
+	struct vnode *lvp;
 	struct vnodeop_desc *descp = ap->a_desc;
 	int reles, i;
 
@@ -295,6 +296,23 @@ null_bypass(struct vop_generic_args *ap)
 		if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET)
 			break;   /* bail out at end of list */
 		if (old_vps[i]) {
+			lvp = *(vps_p[i]);
+
+			/*
+			 * If lowervp was unlocked during VOP
+			 * operation, nullfs upper vnode could have
+			 * been reclaimed, which changes its v_vnlock
+			 * back to private v_lock.  In this case we
+			 * must move lock ownership from lower to
+			 * upper (reclaimed) vnode.
+			 */
+			if (lvp != NULLVP &&
+			    VOP_ISLOCKED(lvp) == LK_EXCLUSIVE &&
+			    old_vps[i]->v_vnlock != lvp->v_vnlock) {
+				VOP_UNLOCK(lvp);
+				VOP_LOCK(old_vps[i], LK_EXCLUSIVE | LK_RETRY);
+			}
+
 			*(vps_p[i]) = old_vps[i];
 #if 0
 			if (reles & VDESC_VP0_WILLUNLOCK)



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202010191923.09JJNMaV035419>