Index: sys/fs/devfs/devfs_devs.c =================================================================== --- sys/fs/devfs/devfs_devs.c +++ sys/fs/devfs/devfs_devs.c @@ -62,6 +62,7 @@ static MALLOC_DEFINE(M_DEVFS2, "DEVFS2", "DEVFS data 2"); static MALLOC_DEFINE(M_DEVFS3, "DEVFS3", "DEVFS data 3"); static MALLOC_DEFINE(M_CDEVP, "DEVFS1", "DEVFS cdev_priv storage"); +MALLOC_DEFINE(M_CDEV_VP, "DEVFS4", "DEVFS vnode tables"); SYSCTL_NODE(_vfs, OID_AUTO, devfs, CTLFLAG_RW, 0, "DEVFS filesystem"); @@ -144,6 +145,8 @@ LIST_INIT(&cdev->si_children); vfs_timestamp(&ts); cdev->si_atime = cdev->si_mtime = cdev->si_ctime = ts; + cdev->si_vnodes = (void *)&cdev->si_vnodes_local; + cdev->si_vnodes_size = nitems(cdev->si_vnodes_local); return (cdev); } @@ -173,6 +176,9 @@ devfs_free(struct cdev *cdev) { struct cdev_priv *cdp; +#ifdef INVARIANTS + int i; +#endif cdp = cdev2priv(cdev); if (cdev->si_cred != NULL) @@ -180,6 +186,18 @@ devfs_free_cdp_inode(cdp->cdp_inode); if (cdp->cdp_maxdirent > 0) free(cdp->cdp_dirents, M_DEVFS2); + KASSERT(cdev->si_vnodes_assigned == 0, ("%s: cdev %p still has " + "vnodes %d assigned", __func__, cdev, cdev->si_vnodes_assigned)); +#ifdef INVARIANTS + for (i = 0; i < cdev->si_vnodes_size; i++) { + if (cdev->si_vnodes[i] != NULL) { + printf("%s: cdev %p non-NULL entry at %d\n", __func__, + cdev, i); + } + } +#endif + if (cdev->si_vnodes != (void *)&cdev->si_vnodes_local) + free(cdev->si_vnodes, M_CDEV_VP); free(cdp, M_CDEVP); } Index: sys/fs/devfs/devfs_vnops.c =================================================================== --- sys/fs/devfs/devfs_vnops.c +++ sys/fs/devfs/devfs_vnops.c @@ -82,6 +82,7 @@ #include static MALLOC_DEFINE(M_CDEVPDATA, "DEVFSP", "Metainfo for cdev-fp data"); +MALLOC_DECLARE(M_CDEV_VP); struct mtx devfs_de_interlock; MTX_SYSINIT(devfs_de_interlock, &devfs_de_interlock, "devfs interlock", MTX_DEF); @@ -418,6 +419,88 @@ vput(vp); } +static void +dev_vn_growtable(struct cdev *dev) +{ + struct vnode **vt, **tofree; + int newsize; + + newsize = dev->si_vnodes_size * 2; + vt = malloc(newsize * sizeof(*vt), M_CDEV_VP, M_WAITOK | M_ZERO); + dev_lock(); + if (dev->si_vnodes_size >= newsize) { + dev_unlock(); + free(vt, M_CDEV_VP); + return; + } + memcpy(vt, dev->si_vnodes, dev->si_vnodes_size * sizeof(*vt)); + if (dev->si_vnodes != (void *)&dev->si_vnodes_local) + tofree = dev->si_vnodes; + else + tofree = NULL; + dev->si_vnodes = vt; + dev->si_vnodes_size = newsize; + dev_unlock(); + free(tofree, M_CDEV_VP); +} + +static int +dev_vn_add_vp(struct cdev *dev, struct vnode *vp) +{ + + if (dev->si_vnodes_size == dev->si_vnodes_assigned) + return (EAGAIN); + + vp->v_rdev = dev; + KASSERT(vp->v_usecount == 1, + ("%s %d (%d)\n", __func__, __LINE__, vp->v_usecount)); + dev->si_vnodes_assigned++; + dev_vn_table_add(dev, vp); + return (0); +} + +static void +dev_vn_remove_vp(struct cdev *dev, struct vnode *vp) +{ + mtx_assert(&devmtx, MA_OWNED); + + if (dev == NULL) + return; + dev_vn_table_remove(dev, vp); + dev->si_vnodes_assigned--; + vp->v_rdev = NULL; +} + +void +dev_vn_table_add(struct cdev *dev, struct vnode *vp) +{ + mtx_assert(&devmtx, MA_OWNED); + + if (dev->si_vnodes_size == dev->si_vnodes_index) + panic("%s: no room for vnode %p in cdev %p table\n", + __func__, vp, dev); + + dev->si_vnodes[dev->si_vnodes_index++] = vp; +} + +void +dev_vn_table_remove(struct cdev *dev, struct vnode *vp) +{ + int i; + + mtx_assert(&devmtx, MA_OWNED); + + for (i = 0; i < dev->si_vnodes_index; i++) { + if (dev->si_vnodes[i] == vp) { + dev->si_vnodes[i] = dev->si_vnodes[dev->si_vnodes_index - 1]; + dev->si_vnodes[dev->si_vnodes_index - 1] = NULL; + dev->si_vnodes_index--; + return; + } + } + panic("%s: vnode %p not found in cdev %p table\n", __func__, vp, dev); +} + /* * devfs_allocv shall be entered with dmp->dm_lock held, and it drops * it on return. @@ -485,14 +568,16 @@ if (de->de_dirent->d_type == DT_CHR) { vp->v_type = VCHR; - VI_LOCK(vp); - dev_lock(); + for (;;) { + VI_LOCK(vp); + dev_lock(); + if (dev_vn_add_vp(dev, vp) == 0) + break; + dev_unlock(); + VI_UNLOCK(vp); + dev_vn_growtable(dev); + } dev_refl(dev); - /* XXX: v_rdev should be protect by vnode lock */ - vp->v_rdev = dev; - KASSERT(vp->v_usecount == 1, - ("%s %d (%d)\n", __func__, __LINE__, vp->v_usecount)); - dev->si_usecount += vp->v_usecount; /* Special casing of ttys for deadfs. Probably redundant. */ dsw = dev->si_devsw; if (dsw != NULL && (dsw->d_flags & D_TTY) != 0) @@ -602,7 +687,7 @@ if (vp == p->p_session->s_ttyvp) { SESS_LOCK(p->p_session); VI_LOCK(vp); - if (count_dev(dev) == 2 && + if (count_dev_cmp(dev, 2) == 0 && (vp->v_iflag & VI_DOOMED) == 0) { p->p_session->s_ttyvp = NULL; p->p_session->s_ttydp = NULL; @@ -636,12 +721,12 @@ dflags |= FREVOKE | FNONBLOCK; } else if (dsw->d_flags & D_TRACKCLOSE) { /* Keep device updated on status. */ - } else if (count_dev(dev) > 1) { + } else if (count_dev_cmp(dev, 1) > 0) { VI_UNLOCK(vp); dev_relthread(dev, ref); return (0); } - if (count_dev(dev) == 1) + if (count_dev_cmp(dev, 1) == 0) dflags |= FLASTCLOSE; vholdl(vp); VI_UNLOCK(vp); @@ -1445,9 +1530,7 @@ VI_LOCK(vp); dev_lock(); dev = vp->v_rdev; - vp->v_rdev = NULL; - if (dev != NULL) - dev->si_usecount -= vp->v_usecount; + dev_vn_remove_vp(dev, vp); dev_unlock(); VI_UNLOCK(vp); if (dev != NULL) Index: sys/kern/kern_conf.c =================================================================== --- sys/kern/kern_conf.c +++ sys/kern/kern_conf.c @@ -1526,8 +1526,8 @@ dev = (struct cdev *)addr; cdp = cdev2priv(dev); - db_printf("dev %s ref %d use %ld thr %ld inuse %u fdpriv %p\n", - dev->si_name, dev->si_refcount, dev->si_usecount, + db_printf("dev %s ref %d use %d thr %ld inuse %u fdpriv %p\n", + dev->si_name, dev->si_refcount, count_dev(dev), dev->si_threadcount, cdp->cdp_inuse, cdp->cdp_fdpriv.lh_first); db_printf("devsw %p si_drv0 %d si_drv1 %p si_drv2 %p\n", dev->si_devsw, dev->si_drv0, dev->si_drv1, dev->si_drv2); @@ -1563,5 +1563,8 @@ CDP_FLAG(CDP_ACTIVE); CDP_FLAG(CDP_SCHED_DTR); db_printf("cdp_flags %s\n", buf); + db_printf("si_vnodes %p si_vnodes_assigned %d si_vnodes_index %d" + "si_vnodes_size %d\n", dev->si_vnodes, dev->si_vnodes_assigned, + dev->si_vnodes_index, dev->si_vnodes_size); } #endif Index: sys/kern/vfs_subr.c =================================================================== --- sys/kern/vfs_subr.c +++ sys/kern/vfs_subr.c @@ -107,8 +107,6 @@ 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); @@ -2630,36 +2628,6 @@ refcount_init(&vp->v_usecount, 1); } -/* - * 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. - */ -static void -v_decr_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(); - } -} - /* * Grab a particular vnode from the free list, increment its * reference count and lock it. VI_DOOMED is set if the vnode @@ -2679,26 +2647,11 @@ { enum vgetstate vs; - if (__predict_true(vp->v_type != VCHR)) { - if (refcount_acquire_if_not_zero(&vp->v_usecount)) { - vs = VGET_USECOUNT; - } else { - _vhold(vp, interlock); - vs = VGET_HOLDCNT; - } + if (refcount_acquire_if_not_zero(&vp->v_usecount)) { + vs = VGET_USECOUNT; } else { - if (!interlock) - VI_LOCK(vp); - if (vp->v_usecount == 0) { - vholdl(vp); - vs = VGET_HOLDCNT; - } else { - v_incr_devcount(vp); - refcount_acquire(&vp->v_usecount); - vs = VGET_USECOUNT; - } - if (!interlock) - VI_UNLOCK(vp); + _vhold(vp, interlock); + vs = VGET_HOLDCNT; } return (vs); } @@ -2761,8 +2714,7 @@ * the vnode around. Otherwise someone else lended their hold count and * we have to drop ours. */ - if (vp->v_type != VCHR && - refcount_acquire_if_not_zero(&vp->v_usecount)) { + if (refcount_acquire_if_not_zero(&vp->v_usecount)) { #ifdef INVARIANTS int old = atomic_fetchadd_int(&vp->v_holdcnt, -1) - 1; VNASSERT(old > 0, vp, ("%s: wrong hold count", __func__)); @@ -2793,7 +2745,6 @@ } if (vp->v_usecount > 0) refcount_release(&vp->v_holdcnt); - v_incr_devcount(vp); refcount_acquire(&vp->v_usecount); if (oweinact && VOP_ISLOCKED(vp) == LK_EXCLUSIVE && (flags & LK_NOWAIT) == 0) @@ -2812,8 +2763,7 @@ ASSERT_VI_UNLOCKED(vp, __func__); CTR2(KTR_VFS, "%s: vp %p", __func__, vp); - if (vp->v_type != VCHR && - refcount_acquire_if_not_zero(&vp->v_usecount)) { + 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__)); @@ -2838,7 +2788,6 @@ vp->v_iflag &= ~VI_OWEINACT; VNODE_REFCOUNT_FENCE_REL(); } - v_incr_devcount(vp); refcount_acquire(&vp->v_usecount); } @@ -2847,12 +2796,6 @@ { CTR2(KTR_VFS, "%s: vp %p", __func__, vp); - if (__predict_false(vp->v_type == VCHR)) { - VNASSERT(vp->v_holdcnt > 0 && vp->v_usecount > 0, vp, - ("%s: wrong ref counts", __func__)); - vref(vp); - return; - } #ifdef INVARIANTS int old = atomic_fetchadd_int(&vp->v_usecount, 1); VNASSERT(old > 0, vp, ("%s: wrong use count", __func__)); @@ -2917,8 +2860,7 @@ if (func == VPUTX_VPUT) VOP_UNLOCK(vp, 0); - if (vp->v_type != VCHR && - refcount_release_if_not_last(&vp->v_usecount)) + if (refcount_release_if_not_last(&vp->v_usecount)) return; VI_LOCK(vp); @@ -2928,7 +2870,6 @@ * prevent vgone() races. We drop the use count here and the * hold count below when we're done. */ - v_decr_devcount(vp); if (!refcount_release(&vp->v_usecount)) { VI_UNLOCK(vp); return; @@ -3083,6 +3024,13 @@ mp->mnt_activevnodelistsize++; mtx_unlock(&mp->mnt_listmtx); refcount_acquire(&vp->v_holdcnt); + if (vp->v_rdev != NULL) { + VNASSERT(vp->v_type == VCHR, vp, + ("vdropl: non-VCHR vnode with v_rdev")); + dev_lock(); + dev_vn_table_add(vp->v_rdev, vp); + dev_unlock(); + } if (!locked) VI_UNLOCK(vp); } @@ -3145,6 +3093,13 @@ ("vnode already free")); VNASSERT(vp->v_holdcnt == 0, vp, ("vdropl: freeing when we shouldn't")); + if (vp->v_rdev != NULL) { + VNASSERT(vp->v_type == VCHR, vp, + ("vdropl: non-VCHR vnode with v_rdev")); + dev_lock(); + dev_vn_table_remove(vp->v_rdev, vp); + dev_unlock(); + } active = vp->v_iflag & VI_ACTIVE; if ((vp->v_iflag & VI_OWEINACT) == 0) { vp->v_iflag &= ~VI_ACTIVE; @@ -3652,12 +3607,8 @@ int vcount(struct vnode *vp) { - int count; - dev_lock(); - count = vp->v_rdev->si_usecount; - dev_unlock(); - return (count); + return (count_dev(vp->v_rdev)); } /* @@ -3666,12 +3617,38 @@ int count_dev(struct cdev *dev) { - int count; + int count, i; + + count = 0; + dev_lock(); + for (i = 0; i < dev->si_vnodes_index; i++) { + count += dev->si_vnodes[i]->v_usecount; + } + dev_unlock(); + return (count); +} + +int +count_dev_cmp(struct cdev *dev, int n) +{ + int count, i, ret; + count = 0; dev_lock(); - count = dev->si_usecount; + for (i = 0; i < dev->si_vnodes_index; i++) { + count += dev->si_vnodes[i]->v_usecount; + if (count > n) { + ret = 1; + goto out; + } + } + if (count == n) + ret = 0; + else + ret = -1; +out: dev_unlock(); - return(count); + return (ret); } /* Index: sys/sys/conf.h =================================================================== --- sys/sys/conf.h +++ sys/sys/conf.h @@ -83,12 +83,16 @@ void *si_drv1, *si_drv2; struct cdevsw *si_devsw; int si_iosize_max; /* maximum I/O size (for physio &al) */ - u_long si_usecount; u_long si_threadcount; + struct vnode **si_vnodes; + int si_vnodes_index; + int si_vnodes_size; + int si_vnodes_assigned; union { struct snapdata *__sid_snapdata; } __si_u; char si_name[SPECNAMELEN + 1]; + struct vnode *si_vnodes_local[2]; }; #define si_snapdata __si_u.__sid_snapdata @@ -256,7 +260,10 @@ #define make_dev_args_init(a) \ make_dev_args_init_impl((a), sizeof(struct make_dev_args)) +void dev_vn_table_add(struct cdev *dev, struct vnode *vp); +void dev_vn_table_remove(struct cdev *dev, struct vnode *vp); int count_dev(struct cdev *_dev); +int count_dev_cmp(struct cdev *_dev, int n); void delist_dev(struct cdev *_dev); void destroy_dev(struct cdev *_dev); int destroy_dev_sched(struct cdev *dev);