svn commit: r357729 - head/sys/kern

Mateusz Guzik mjg at FreeBSD.org
Mon Feb 10 13:54:34 UTC 2020


Author: mjg
Date: Mon Feb 10 13:54:34 2020
New Revision: 357729
URL: https://svnweb.freebsd.org/changeset/base/357729

Log:
  vfs: fix lock recursion in vrele
  
  vrele is supposed to be called with an unlocked vnode, but this was never
  asserted for if v_usecount was > 0. For such counts the lock is never touched
  by the routine. As a result the kernel has several consumers which expect
  vunref semantics and get away with calling vrele since they happen to never do
  it when this is the last reference (and for some of them this may happen to be
  a guarantee).
  
  Work around the problem by changing vrele semantics to tolerate being called
  with a lock. This eliminates a possible bug where the lock is already held and
  vputx takes it anyway.
  
  Reviewed by:	kib
  Tested by:	pho
  Differential Revision:	https://reviews.freebsd.org/D23528

Modified:
  head/sys/kern/vfs_subr.c

Modified: head/sys/kern/vfs_subr.c
==============================================================================
--- head/sys/kern/vfs_subr.c	Mon Feb 10 13:52:25 2020	(r357728)
+++ head/sys/kern/vfs_subr.c	Mon Feb 10 13:54:34 2020	(r357729)
@@ -3170,6 +3170,7 @@ static void
 vputx(struct vnode *vp, enum vputx_op func)
 {
 	int error;
+	bool want_unlock;
 
 	KASSERT(vp != NULL, ("vputx: null vp"));
 	if (func == VPUTX_VUNREF)
@@ -3221,13 +3222,31 @@ vputx(struct vnode *vp, enum vputx_op func)
 	 * as VI_DOINGINACT to avoid recursion.
 	 */
 	vp->v_iflag |= VI_OWEINACT;
+	want_unlock = false;
+	error = 0;
 	switch (func) {
 	case VPUTX_VRELE:
-		error = vn_lock(vp, LK_EXCLUSIVE | LK_INTERLOCK);
-		VI_LOCK(vp);
+		switch (VOP_ISLOCKED(vp)) {
+		case LK_EXCLUSIVE:
+			break;
+		case LK_EXCLOTHER:
+		case 0:
+			want_unlock = true;
+			error = vn_lock(vp, LK_EXCLUSIVE | LK_INTERLOCK);
+			VI_LOCK(vp);
+			break;
+		default:
+			/*
+			 * The lock has at least one sharer, but we have no way
+			 * to conclude whether this is us. Play it safe and
+			 * defer processing.
+			 */
+			error = EAGAIN;
+			break;
+		}
 		break;
 	case VPUTX_VPUT:
-		error = 0;
+		want_unlock = true;
 		if (VOP_ISLOCKED(vp) != LK_EXCLUSIVE) {
 			error = VOP_LOCK(vp, LK_UPGRADE | LK_INTERLOCK |
 			    LK_NOWAIT);
@@ -3235,7 +3254,6 @@ vputx(struct vnode *vp, enum vputx_op func)
 		}
 		break;
 	case VPUTX_VUNREF:
-		error = 0;
 		if (VOP_ISLOCKED(vp) != LK_EXCLUSIVE) {
 			error = VOP_LOCK(vp, LK_TRYUPGRADE | LK_INTERLOCK);
 			VI_LOCK(vp);
@@ -3244,7 +3262,7 @@ vputx(struct vnode *vp, enum vputx_op func)
 	}
 	if (error == 0) {
 		vinactive(vp);
-		if (func != VPUTX_VUNREF)
+		if (want_unlock)
 			VOP_UNLOCK(vp);
 		vdropl(vp);
 	} else {


More information about the svn-src-all mailing list