svn commit: r367631 - in head/sys: kern sys

Konstantin Belousov kib at FreeBSD.org
Fri Nov 13 09:31:58 UTC 2020


Author: kib
Date: Fri Nov 13 09:31:57 2020
New Revision: 367631
URL: https://svnweb.freebsd.org/changeset/base/367631

Log:
  Implement vn_lock_pair().
  
  In collaboration with:	pho
  Reviewed by:	mckusick (previous version), markj (previous version)
  Tested by:	markj (syzkaller), pho
  Sponsored by:	The FreeBSD Foundation
  Differential revision:	https://reviews.freebsd.org/D26136

Modified:
  head/sys/kern/vfs_vnops.c
  head/sys/sys/vnode.h

Modified: head/sys/kern/vfs_vnops.c
==============================================================================
--- head/sys/kern/vfs_vnops.c	Fri Nov 13 02:05:45 2020	(r367630)
+++ head/sys/kern/vfs_vnops.c	Fri Nov 13 09:31:57 2020	(r367631)
@@ -70,6 +70,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/filio.h>
 #include <sys/resourcevar.h>
 #include <sys/rwlock.h>
+#include <sys/prng.h>
 #include <sys/sx.h>
 #include <sys/sleepqueue.h>
 #include <sys/sysctl.h>
@@ -275,6 +276,10 @@ restart:
 			vn_finished_write(mp);
 			if (error) {
 				NDFREE(ndp, NDF_ONLY_PNBUF);
+				if (error == ERELOOKUP) {
+					NDREINIT(ndp);
+					goto restart;
+				}
 				return (error);
 			}
 			fmode &= ~O_TRUNC;
@@ -1524,6 +1529,7 @@ vn_truncate(struct file *fp, off_t length, struct ucre
 
 	vp = fp->f_vnode;
 
+retry:
 	/*
 	 * Lock the whole range for truncation.  Otherwise split i/o
 	 * might happen partly before and partly after the truncation.
@@ -1550,6 +1556,8 @@ out:
 	vn_finished_write(mp);
 out1:
 	vn_rangelock_unlock(vp, rl_cookie);
+	if (error == ERELOOKUP)
+		goto retry;
 	return (error);
 }
 
@@ -3317,4 +3325,92 @@ vn_fallocate(struct file *fp, off_t offset, off_t len,
 	}
 
 	return (error);
+}
+
+static u_long vn_lock_pair_pause_cnt;
+SYSCTL_ULONG(_debug, OID_AUTO, vn_lock_pair_pause, CTLFLAG_RD,
+    &vn_lock_pair_pause_cnt, 0,
+    "Count of vn_lock_pair deadlocks");
+
+static void
+vn_lock_pair_pause(const char *wmesg)
+{
+	atomic_add_long(&vn_lock_pair_pause_cnt, 1);
+	pause(wmesg, prng32_bounded(hz / 10));
+}
+
+/*
+ * Lock pair of vnodes vp1, vp2, avoiding lock order reversal.
+ * vp1_locked indicates whether vp1 is exclusively locked; if not, vp1
+ * must be unlocked.  Same for vp2 and vp2_locked.  One of the vnodes
+ * can be NULL.
+ *
+ * The function returns with both vnodes exclusively locked, and
+ * guarantees that it does not create lock order reversal with other
+ * threads during its execution.  Both vnodes could be unlocked
+ * temporary (and reclaimed).
+ */
+void
+vn_lock_pair(struct vnode *vp1, bool vp1_locked, struct vnode *vp2,
+    bool vp2_locked)
+{
+	int error;
+
+	if (vp1 == NULL && vp2 == NULL)
+		return;
+	if (vp1 != NULL) {
+		if (vp1_locked)
+			ASSERT_VOP_ELOCKED(vp1, "vp1");
+		else
+			ASSERT_VOP_UNLOCKED(vp1, "vp1");
+	} else {
+		vp1_locked = true;
+	}
+	if (vp2 != NULL) {
+		if (vp2_locked)
+			ASSERT_VOP_ELOCKED(vp2, "vp2");
+		else
+			ASSERT_VOP_UNLOCKED(vp2, "vp2");
+	} else {
+		vp2_locked = true;
+	}
+	if (!vp1_locked && !vp2_locked) {
+		vn_lock(vp1, LK_EXCLUSIVE | LK_RETRY);
+		vp1_locked = true;
+	}
+
+	for (;;) {
+		if (vp1_locked && vp2_locked)
+			break;
+		if (vp1_locked && vp2 != NULL) {
+			if (vp1 != NULL) {
+				error = VOP_LOCK1(vp2, LK_EXCLUSIVE | LK_NOWAIT,
+				    __FILE__, __LINE__);
+				if (error == 0)
+					break;
+				VOP_UNLOCK(vp1);
+				vp1_locked = false;
+				vn_lock_pair_pause("vlp1");
+			}
+			vn_lock(vp2, LK_EXCLUSIVE | LK_RETRY);
+			vp2_locked = true;
+		}
+		if (vp2_locked && vp1 != NULL) {
+			if (vp2 != NULL) {
+				error = VOP_LOCK1(vp1, LK_EXCLUSIVE | LK_NOWAIT,
+				    __FILE__, __LINE__);
+				if (error == 0)
+					break;
+				VOP_UNLOCK(vp2);
+				vp2_locked = false;
+				vn_lock_pair_pause("vlp2");
+			}
+			vn_lock(vp1, LK_EXCLUSIVE | LK_RETRY);
+			vp1_locked = true;
+		}
+	}
+	if (vp1 != NULL)
+		ASSERT_VOP_ELOCKED(vp1, "vp1 ret");
+	if (vp2 != NULL)
+		ASSERT_VOP_ELOCKED(vp2, "vp2 ret");
 }

Modified: head/sys/sys/vnode.h
==============================================================================
--- head/sys/sys/vnode.h	Fri Nov 13 02:05:45 2020	(r367630)
+++ head/sys/sys/vnode.h	Fri Nov 13 09:31:57 2020	(r367631)
@@ -729,6 +729,8 @@ bool	vn_isdisk_error(struct vnode *vp, int *errp);
 bool	vn_isdisk(struct vnode *vp);
 int	_vn_lock(struct vnode *vp, int flags, const char *file, int line);
 #define vn_lock(vp, flags) _vn_lock(vp, flags, __FILE__, __LINE__)
+void	vn_lock_pair(struct vnode *vp1, bool vp1_locked, struct vnode *vp2,
+	    bool vp2_locked);
 int	vn_open(struct nameidata *ndp, int *flagp, int cmode, struct file *fp);
 int	vn_open_cred(struct nameidata *ndp, int *flagp, int cmode,
 	    u_int vn_open_flags, struct ucred *cred, struct file *fp);


More information about the svn-src-head mailing list