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 *);
_______________________________________________
[email protected] mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "[email protected]"

Reply via email to