svn commit: r364113 - in head/sys: fs/devfs kern sys

Mateusz Guzik mjg at FreeBSD.org
Tue Aug 11 14:27:59 UTC 2020


Author: mjg
Date: Tue Aug 11 14:27:57 2020
New Revision: 364113
URL: https://svnweb.freebsd.org/changeset/base/364113

Log:
  devfs: rework si_usecount to track opens
  
  This removes a lot of special casing from the VFS layer.
  
  Reviewed by:	kib (previous version)
  Tested by:	pho (previous version)
  Differential Revision:	https://reviews.freebsd.org/D25612

Modified:
  head/sys/fs/devfs/devfs.h
  head/sys/fs/devfs/devfs_vnops.c
  head/sys/kern/kern_proc.c
  head/sys/kern/tty.c
  head/sys/kern/vfs_subr.c
  head/sys/kern/vfs_syscalls.c
  head/sys/sys/vnode.h

Modified: head/sys/fs/devfs/devfs.h
==============================================================================
--- head/sys/fs/devfs/devfs.h	Tue Aug 11 14:19:05 2020	(r364112)
+++ head/sys/fs/devfs/devfs.h	Tue Aug 11 14:27:57 2020	(r364113)
@@ -153,6 +153,7 @@ struct devfs_dirent {
 	struct timespec 	de_ctime;
 	struct vnode 		*de_vnode;
 	char 			*de_symlink;
+	int			de_usecount;
 };
 
 struct devfs_mount {
@@ -202,6 +203,10 @@ struct devfs_dirent	*devfs_vmkdir(struct devfs_mount *
 			    struct devfs_dirent *, u_int);
 struct devfs_dirent	*devfs_find(struct devfs_dirent *, const char *, int,
 			    int);
+
+void	devfs_ctty_ref(struct vnode *);
+void	devfs_ctty_unref(struct vnode *);
+int	devfs_usecount(struct vnode *);
 
 #endif /* _KERNEL */
 

Modified: head/sys/fs/devfs/devfs_vnops.c
==============================================================================
--- head/sys/fs/devfs/devfs_vnops.c	Tue Aug 11 14:19:05 2020	(r364112)
+++ head/sys/fs/devfs/devfs_vnops.c	Tue Aug 11 14:27:57 2020	(r364113)
@@ -222,6 +222,115 @@ devfs_clear_cdevpriv(void)
 	devfs_fpdrop(fp);
 }
 
+static void
+devfs_usecount_add(struct vnode *vp)
+{
+	struct devfs_dirent *de;
+	struct cdev *dev;
+
+	mtx_lock(&devfs_de_interlock);
+	VI_LOCK(vp);
+	VNPASS(vp->v_type == VCHR || vp->v_type == VBAD, vp);
+	if (VN_IS_DOOMED(vp)) {
+		goto out_unlock;
+	}
+
+	de = vp->v_data;
+	dev = vp->v_rdev;
+	MPASS(de != NULL);
+	MPASS(dev != NULL);
+	dev->si_usecount++;
+	de->de_usecount++;
+out_unlock:
+	VI_UNLOCK(vp);
+	mtx_unlock(&devfs_de_interlock);
+}
+
+static void
+devfs_usecount_subl(struct vnode *vp)
+{
+	struct devfs_dirent *de;
+	struct cdev *dev;
+
+	mtx_assert(&devfs_de_interlock, MA_OWNED);
+	ASSERT_VI_LOCKED(vp, __func__);
+	VNPASS(vp->v_type == VCHR || vp->v_type == VBAD, vp);
+
+	de = vp->v_data;
+	dev = vp->v_rdev;
+	if (de == NULL)
+		return;
+	if (dev == NULL) {
+		MPASS(de->de_usecount == 0);
+		return;
+	}
+	if (dev->si_usecount < de->de_usecount)
+		panic("%s: si_usecount underflow for dev %p "
+		    "(has %ld, dirent has %d)\n",
+		    __func__, dev, dev->si_usecount, de->de_usecount);
+	if (VN_IS_DOOMED(vp)) {
+		dev->si_usecount -= de->de_usecount;
+		de->de_usecount = 0;
+	} else {
+		if (de->de_usecount == 0)
+			panic("%s: de_usecount underflow for dev %p\n",
+			    __func__, dev);
+		dev->si_usecount--;
+		de->de_usecount--;
+	}
+}
+
+static void
+devfs_usecount_sub(struct vnode *vp)
+{
+
+	mtx_lock(&devfs_de_interlock);
+	VI_LOCK(vp);
+	devfs_usecount_subl(vp);
+	VI_UNLOCK(vp);
+	mtx_unlock(&devfs_de_interlock);
+}
+
+static int
+devfs_usecountl(struct vnode *vp)
+{
+
+	VNPASS(vp->v_type == VCHR, vp);
+	mtx_assert(&devfs_de_interlock, MA_OWNED);
+	ASSERT_VI_LOCKED(vp, __func__);
+	return (vp->v_rdev->si_usecount);
+}
+
+int
+devfs_usecount(struct vnode *vp)
+{
+	int count;
+
+	VNPASS(vp->v_type == VCHR, vp);
+	mtx_lock(&devfs_de_interlock);
+	VI_LOCK(vp);
+	count = devfs_usecountl(vp);
+	VI_UNLOCK(vp);
+	mtx_unlock(&devfs_de_interlock);
+	return (count);
+}
+
+void
+devfs_ctty_ref(struct vnode *vp)
+{
+
+	vrefact(vp);
+	devfs_usecount_add(vp);
+}
+
+void
+devfs_ctty_unref(struct vnode *vp)
+{
+
+	devfs_usecount_sub(vp);
+	vrele(vp);
+}
+
 /*
  * On success devfs_populate_vp() returns with dmp->dm_lock held.
  */
@@ -487,7 +596,6 @@ loop:
 		/* XXX: v_rdev should be protect by vnode lock */
 		vp->v_rdev = dev;
 		VNPASS(vp->v_usecount == 1, vp);
-		dev->si_usecount++;
 		/* Special casing of ttys for deadfs.  Probably redundant. */
 		dsw = dev->si_devsw;
 		if (dsw != NULL && (dsw->d_flags & D_TTY) != 0)
@@ -569,6 +677,7 @@ devfs_close(struct vop_close_args *ap)
 	struct proc *p;
 	struct cdev *dev = vp->v_rdev;
 	struct cdevsw *dsw;
+	struct devfs_dirent *de = vp->v_data;
 	int dflags, error, ref, vp_locked;
 
 	/*
@@ -587,7 +696,7 @@ devfs_close(struct vop_close_args *ap)
 	 * if the reference count is 2 (this last descriptor
 	 * plus the session), release the reference from the session.
 	 */
-	if (vp->v_usecount == 2 && td != NULL) {
+	if (de->de_usecount == 2 && td != NULL) {
 		p = td->td_proc;
 		PROC_LOCK(p);
 		if (vp == p->p_session->s_ttyvp) {
@@ -596,19 +705,20 @@ devfs_close(struct vop_close_args *ap)
 			sx_xlock(&proctree_lock);
 			if (vp == p->p_session->s_ttyvp) {
 				SESS_LOCK(p->p_session);
+				mtx_lock(&devfs_de_interlock);
 				VI_LOCK(vp);
-				if (vp->v_usecount == 2 && vcount(vp) == 1 &&
-				    !VN_IS_DOOMED(vp)) {
+				if (devfs_usecountl(vp) == 2 && !VN_IS_DOOMED(vp)) {
 					p->p_session->s_ttyvp = NULL;
 					p->p_session->s_ttydp = NULL;
 					oldvp = vp;
 				}
 				VI_UNLOCK(vp);
+				mtx_unlock(&devfs_de_interlock);
 				SESS_UNLOCK(p->p_session);
 			}
 			sx_xunlock(&proctree_lock);
 			if (oldvp != NULL)
-				vrele(oldvp);
+				devfs_ctty_unref(oldvp);
 		} else
 			PROC_UNLOCK(p);
 	}
@@ -625,9 +735,12 @@ devfs_close(struct vop_close_args *ap)
 	if (dsw == NULL)
 		return (ENXIO);
 	dflags = 0;
+	mtx_lock(&devfs_de_interlock);
 	VI_LOCK(vp);
-	if (vp->v_usecount == 1 && vcount(vp) == 1)
+	if (devfs_usecountl(vp) == 1)
 		dflags |= FLASTCLOSE;
+	devfs_usecount_subl(vp);
+	mtx_unlock(&devfs_de_interlock);
 	if (VN_IS_DOOMED(vp)) {
 		/* Forced close. */
 		dflags |= FREVOKE | FNONBLOCK;
@@ -850,7 +963,7 @@ devfs_ioctl(struct vop_ioctl_args *ap)
 			return (0);
 		}
 
-		vrefact(vp);
+		devfs_ctty_ref(vp);
 		SESS_LOCK(sess);
 		vpold = sess->s_ttyvp;
 		sess->s_ttyvp = vp;
@@ -1159,6 +1272,9 @@ devfs_open(struct vop_open_args *ap)
 		return (ENXIO);
 	}
 
+	if (vp->v_type == VCHR)
+		devfs_usecount_add(vp);
+
 	vlocked = VOP_ISLOCKED(vp);
 	VOP_UNLOCK(vp);
 
@@ -1178,6 +1294,9 @@ devfs_open(struct vop_open_args *ap)
 	td->td_fpop = fpop;
 
 	vn_lock(vp, vlocked | LK_RETRY);
+	if (error != 0 && vp->v_type == VCHR)
+		devfs_usecount_sub(vp);
+
 	dev_relthread(dev, ref);
 	if (error != 0) {
 		if (error == ERESTART)
@@ -1406,19 +1525,28 @@ devfs_readlink(struct vop_readlink_args *ap)
 	return (uiomove(de->de_symlink, strlen(de->de_symlink), ap->a_uio));
 }
 
-static int
-devfs_reclaim(struct vop_reclaim_args *ap)
+static void
+devfs_reclaiml(struct vnode *vp)
 {
-	struct vnode *vp;
 	struct devfs_dirent *de;
 
-	vp = ap->a_vp;
-	mtx_lock(&devfs_de_interlock);
+	mtx_assert(&devfs_de_interlock, MA_OWNED);
 	de = vp->v_data;
 	if (de != NULL) {
+		MPASS(de->de_usecount == 0);
 		de->de_vnode = NULL;
 		vp->v_data = NULL;
 	}
+}
+
+static int
+devfs_reclaim(struct vop_reclaim_args *ap)
+{
+	struct vnode *vp;
+
+	vp = ap->a_vp;
+	mtx_lock(&devfs_de_interlock);
+	devfs_reclaiml(vp);
 	mtx_unlock(&devfs_de_interlock);
 	return (0);
 }
@@ -1432,14 +1560,14 @@ devfs_reclaim_vchr(struct vop_reclaim_args *ap)
 	vp = ap->a_vp;
 	MPASS(vp->v_type == VCHR);
 
-	devfs_reclaim(ap);
-
+	mtx_lock(&devfs_de_interlock);
 	VI_LOCK(vp);
+	devfs_usecount_subl(vp);
+	devfs_reclaiml(vp);
+	mtx_unlock(&devfs_de_interlock);
 	dev_lock();
 	dev = vp->v_rdev;
 	vp->v_rdev = NULL;
-	if (dev != NULL)
-		dev->si_usecount -= (vp->v_usecount > 0);
 	dev_unlock();
 	VI_UNLOCK(vp);
 	if (dev != NULL)

Modified: head/sys/kern/kern_proc.c
==============================================================================
--- head/sys/kern/kern_proc.c	Tue Aug 11 14:19:05 2020	(r364112)
+++ head/sys/kern/kern_proc.c	Tue Aug 11 14:27:57 2020	(r364113)
@@ -88,6 +88,8 @@ __FBSDID("$FreeBSD$");
 #include <vm/vm_page.h>
 #include <vm/uma.h>
 
+#include <fs/devfs/devfs.h>
+
 #ifdef COMPAT_FREEBSD32
 #include <compat/freebsd32/freebsd32.h>
 #include <compat/freebsd32/freebsd32_util.h>
@@ -858,7 +860,7 @@ killjobc(void)
 				VOP_REVOKE(ttyvp, REVOKEALL);
 				VOP_UNLOCK(ttyvp);
 			}
-			vrele(ttyvp);
+			devfs_ctty_unref(ttyvp);
 			sx_xlock(&proctree_lock);
 		}
 	}

Modified: head/sys/kern/tty.c
==============================================================================
--- head/sys/kern/tty.c	Tue Aug 11 14:19:05 2020	(r364112)
+++ head/sys/kern/tty.c	Tue Aug 11 14:27:57 2020	(r364113)
@@ -67,6 +67,8 @@ __FBSDID("$FreeBSD$");
 #include <sys/ucred.h>
 #include <sys/vnode.h>
 
+#include <fs/devfs/devfs.h>
+
 #include <machine/stdarg.h>
 
 static MALLOC_DEFINE(M_TTY, "tty", "tty device");
@@ -1256,7 +1258,7 @@ tty_drop_ctty(struct tty *tp, struct proc *p)
 	 * is either changed or released.
 	 */
 	if (vp != NULL)
-		vrele(vp);
+		devfs_ctty_unref(vp);
 	return (0);
 }
 

Modified: head/sys/kern/vfs_subr.c
==============================================================================
--- head/sys/kern/vfs_subr.c	Tue Aug 11 14:19:05 2020	(r364112)
+++ head/sys/kern/vfs_subr.c	Tue Aug 11 14:27:57 2020	(r364113)
@@ -108,8 +108,6 @@ static int	flushbuflist(struct bufv *bufv, int flags, 
 static void	syncer_shutdown(void *arg, int howto);
 static int	vtryrecycle(struct vnode *vp);
 static void	v_init_counters(struct vnode *);
-static void	v_incr_devcount(struct vnode *);
-static void	v_decr_devcount(struct vnode *);
 static void	vgonel(struct vnode *);
 static void	vfs_knllock(void *arg);
 static void	vfs_knlunlock(void *arg);
@@ -2794,59 +2792,6 @@ v_init_counters(struct vnode *vp)
 }
 
 /*
- * Increment si_usecount of the associated device, if any.
- */
-static void
-v_incr_devcount(struct vnode *vp)
-{
-
-	ASSERT_VI_LOCKED(vp, __FUNCTION__);
-	if (vp->v_type == VCHR && vp->v_rdev != NULL) {
-		dev_lock();
-		vp->v_rdev->si_usecount++;
-		dev_unlock();
-	}
-}
-
-/*
- * Decrement si_usecount of the associated device, if any.
- *
- * The caller is required to hold the interlock when transitioning a VCHR use
- * count to zero. This prevents a race with devfs_reclaim_vchr() that would
- * leak a si_usecount reference. The vnode lock will also prevent this race
- * if it is held while dropping the last ref.
- *
- * The race is:
- *
- * CPU1					CPU2
- *				  	devfs_reclaim_vchr
- * make v_usecount == 0
- * 				    	  VI_LOCK
- * 				    	  sees v_usecount == 0, no updates
- * 				    	  vp->v_rdev = NULL;
- * 				    	  ...
- * 				    	  VI_UNLOCK
- * VI_LOCK
- * v_decr_devcount
- *   sees v_rdev == NULL, no updates
- *
- * In this scenario si_devcount decrement is not performed.
- */
-static void
-v_decr_devcount(struct vnode *vp)
-{
-
-	ASSERT_VOP_LOCKED(vp, __func__);
-	ASSERT_VI_LOCKED(vp, __FUNCTION__);
-	if (vp->v_type == VCHR && vp->v_rdev != NULL) {
-		dev_lock();
-		VNPASS(vp->v_rdev->si_usecount > 0, vp);
-		vp->v_rdev->si_usecount--;
-		dev_unlock();
-	}
-}
-
-/*
  * Grab a particular vnode from the free list, increment its
  * reference count and lock it.  VIRF_DOOMED is set if the vnode
  * is being destroyed.  Only callers who specify LK_RETRY will
@@ -2921,41 +2866,6 @@ vget(struct vnode *vp, int flags, struct thread *td)
 	return (vget_finish(vp, flags, vs));
 }
 
-static void __noinline
-vget_finish_vchr(struct vnode *vp)
-{
-
-	VNASSERT(vp->v_type == VCHR, vp, ("type != VCHR)"));
-
-	/*
-	 * See the comment in vget_finish before usecount bump.
-	 */
-	if (refcount_acquire_if_not_zero(&vp->v_usecount)) {
-#ifdef INVARIANTS
-		int old = atomic_fetchadd_int(&vp->v_holdcnt, -1);
-		VNASSERT(old > 0, vp, ("%s: wrong hold count %d", __func__, old));
-#else
-		refcount_release(&vp->v_holdcnt);
-#endif
-		return;
-	}
-
-	VI_LOCK(vp);
-	if (refcount_acquire_if_not_zero(&vp->v_usecount)) {
-#ifdef INVARIANTS
-		int old = atomic_fetchadd_int(&vp->v_holdcnt, -1);
-		VNASSERT(old > 1, vp, ("%s: wrong hold count %d", __func__, old));
-#else
-		refcount_release(&vp->v_holdcnt);
-#endif
-		VI_UNLOCK(vp);
-		return;
-	}
-	v_incr_devcount(vp);
-	refcount_acquire(&vp->v_usecount);
-	VI_UNLOCK(vp);
-}
-
 int
 vget_finish(struct vnode *vp, int flags, enum vgetstate vs)
 {
@@ -2993,11 +2903,6 @@ vget_finish_ref(struct vnode *vp, enum vgetstate vs)
 	if (vs == VGET_USECOUNT)
 		return;
 
-	if (__predict_false(vp->v_type == VCHR)) {
-		vget_finish_vchr(vp);
-		return;
-	}
-
 	/*
 	 * We hold the vnode. If the usecount is 0 it will be utilized to keep
 	 * the vnode around. Otherwise someone else lended their hold count and
@@ -3019,61 +2924,12 @@ vget_finish_ref(struct vnode *vp, enum vgetstate vs)
  * Increase the reference (use) and hold count of a vnode.
  * This will also remove the vnode from the free list if it is presently free.
  */
-static void __noinline
-vref_vchr(struct vnode *vp, bool interlock)
-{
-
-	/*
-	 * See the comment in vget_finish before usecount bump.
-	 */
-	if (!interlock) {
-		if (refcount_acquire_if_not_zero(&vp->v_usecount)) {
-			VNODE_REFCOUNT_FENCE_ACQ();
-			VNASSERT(vp->v_holdcnt > 0, vp,
-			    ("%s: active vnode not held", __func__));
-			return;
-		}
-		VI_LOCK(vp);
-		/*
-		 * By the time we get here the vnode might have been doomed, at
-		 * which point the 0->1 use count transition is no longer
-		 * protected by the interlock. Since it can't bounce back to
-		 * VCHR and requires vref semantics, punt it back
-		 */
-		if (__predict_false(vp->v_type == VBAD)) {
-			VI_UNLOCK(vp);
-			vref(vp);
-			return;
-		}
-	}
-	VNASSERT(vp->v_type == VCHR, vp, ("type != VCHR)"));
-	if (refcount_acquire_if_not_zero(&vp->v_usecount)) {
-		VNODE_REFCOUNT_FENCE_ACQ();
-		VNASSERT(vp->v_holdcnt > 0, vp,
-		    ("%s: active vnode not held", __func__));
-		if (!interlock)
-			VI_UNLOCK(vp);
-		return;
-	}
-	vhold(vp);
-	v_incr_devcount(vp);
-	refcount_acquire(&vp->v_usecount);
-	if (!interlock)
-		VI_UNLOCK(vp);
-	return;
-}
-
 void
 vref(struct vnode *vp)
 {
 	int old;
 
 	CTR2(KTR_VFS, "%s: vp %p", __func__, vp);
-	if (__predict_false(vp->v_type == VCHR)) {
-		 vref_vchr(vp, false);
-		 return;
-	}
-
 	if (refcount_acquire_if_not_zero(&vp->v_usecount)) {
 		VNODE_REFCOUNT_FENCE_ACQ();
 		VNASSERT(vp->v_holdcnt > 0, vp,
@@ -3102,10 +2958,6 @@ vrefl(struct vnode *vp)
 
 	ASSERT_VI_LOCKED(vp, __func__);
 	CTR2(KTR_VFS, "%s: vp %p", __func__, vp);
-	if (__predict_false(vp->v_type == VCHR)) {
-		vref_vchr(vp, true);
-		return;
-	}
 	vref(vp);
 }
 
@@ -3246,9 +3098,6 @@ enum vput_op { VRELE, VPUT, VUNREF };
  * By releasing the last usecount we take ownership of the hold count which
  * provides liveness of the vnode, meaning we have to vdrop.
  *
- * If the vnode is of type VCHR we may need to decrement si_usecount, see
- * v_decr_devcount for details.
- *
  * For all vnodes we may need to perform inactive processing. It requires an
  * exclusive lock on the vnode, while it is legal to call here with only a
  * shared lock (or no locks). If locking the vnode in an expected manner fails,
@@ -3269,8 +3118,6 @@ vput_final(struct vnode *vp, enum vput_op func)
 	VNPASS(vp->v_holdcnt > 0, vp);
 
 	VI_LOCK(vp);
-	if (__predict_false(vp->v_type == VCHR && func != VRELE))
-		v_decr_devcount(vp);
 
 	/*
 	 * By the time we got here someone else might have transitioned
@@ -3358,28 +3205,9 @@ out:
  * Releasing the last use count requires additional processing, see vput_final
  * above for details.
  *
- * Note that releasing use count without the vnode lock requires special casing
- * for VCHR, see v_decr_devcount for details.
- *
  * Comment above each variant denotes lock state on entry and exit.
  */
 
-static void __noinline
-vrele_vchr(struct vnode *vp)
-{
-
-	if (refcount_release_if_not_last(&vp->v_usecount))
-		return;
-	VI_LOCK(vp);
-	if (!refcount_release(&vp->v_usecount)) {
-		VI_UNLOCK(vp);
-		return;
-	}
-	v_decr_devcount(vp);
-	VI_UNLOCK(vp);
-	vput_final(vp, VRELE);
-}
-
 /*
  * in: any
  * out: same as passed in
@@ -3389,10 +3217,6 @@ vrele(struct vnode *vp)
 {
 
 	ASSERT_VI_UNLOCKED(vp, __func__);
-	if (__predict_false(vp->v_type == VCHR)) {
-		vrele_vchr(vp);
-		return;
-	}
 	if (!refcount_release(&vp->v_usecount))
 		return;
 	vput_final(vp, VRELE);
@@ -4141,20 +3965,6 @@ vgonel(struct vnode *vp)
 	vp->v_vnlock = &vp->v_lock;
 	vp->v_op = &dead_vnodeops;
 	vp->v_type = VBAD;
-}
-
-/*
- * Calculate the total number of references to a special device.
- */
-int
-vcount(struct vnode *vp)
-{
-	int count;
-
-	dev_lock();
-	count = vp->v_rdev->si_usecount;
-	dev_unlock();
-	return (count);
 }
 
 /*

Modified: head/sys/kern/vfs_syscalls.c
==============================================================================
--- head/sys/kern/vfs_syscalls.c	Tue Aug 11 14:19:05 2020	(r364112)
+++ head/sys/kern/vfs_syscalls.c	Tue Aug 11 14:27:57 2020	(r364113)
@@ -87,6 +87,8 @@ __FBSDID("$FreeBSD$");
 #include <vm/vm_page.h>
 #include <vm/uma.h>
 
+#include <fs/devfs/devfs.h>
+
 #include <ufs/ufs/quota.h>
 
 MALLOC_DEFINE(M_FADVISE, "fadvise", "posix_fadvise(2) information");
@@ -4213,7 +4215,7 @@ sys_revoke(struct thread *td, struct revoke_args *uap)
 		if (error != 0)
 			goto out;
 	}
-	if (vp->v_usecount > 1 || vcount(vp) > 1)
+	if (devfs_usecount(vp) > 0)
 		VOP_REVOKE(vp, REVOKEALL);
 out:
 	vput(vp);

Modified: head/sys/sys/vnode.h
==============================================================================
--- head/sys/sys/vnode.h	Tue Aug 11 14:19:05 2020	(r364112)
+++ head/sys/sys/vnode.h	Tue Aug 11 14:27:57 2020	(r364113)
@@ -676,7 +676,6 @@ int	vaccess_acl_posix1e(enum vtype type, uid_t file_ui
 	    gid_t file_gid, struct acl *acl, accmode_t accmode,
 	    struct ucred *cred);
 void	vattr_null(struct vattr *vap);
-int	vcount(struct vnode *vp);
 void	vlazy(struct vnode *);
 void	vdrop(struct vnode *);
 void	vdropl(struct vnode *);


More information about the svn-src-head mailing list