vfs: change si_usecount management to count used vnodes

Currently si_usecount is effectively a sum of usecounts from all associated
vnodes. This is maintained by special-casing for VCHR every time usecount is
modified. Apart from complicating the code a little bit, it has a scalability
impact since it forces a read from a cacheline shared with said count.

There are no consumers of the feature in the ports tree. In head there are only
2: revoke and devfs_close. Both can get away with a weaker requirement than the
exact usecount, namely just the count of active vnodes. Changing the meaning to
the latter means we only need to modify it on 0<->1 transitions, avoiding the
check plenty of times (and entirely in something like vrefact).

Reviewed by:	kib, jeff
Tested by:	pho
Differential Revision:	https://reviews.freebsd.org/D22202
This commit is contained in:
Mateusz Guzik 2019-11-20 12:05:59 +00:00
parent da248a69aa
commit 1fccb43c39
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=354890
4 changed files with 42 additions and 96 deletions

View File

@ -481,7 +481,7 @@ devfs_allocv(struct devfs_dirent *de, struct mount *mp, int lockmode,
vp->v_rdev = dev; vp->v_rdev = dev;
KASSERT(vp->v_usecount == 1, KASSERT(vp->v_usecount == 1,
("%s %d (%d)\n", __func__, __LINE__, vp->v_usecount)); ("%s %d (%d)\n", __func__, __LINE__, vp->v_usecount));
dev->si_usecount += vp->v_usecount; dev->si_usecount++;
/* Special casing of ttys for deadfs. Probably redundant. */ /* Special casing of ttys for deadfs. Probably redundant. */
dsw = dev->si_devsw; dsw = dev->si_devsw;
if (dsw != NULL && (dsw->d_flags & D_TTY) != 0) if (dsw != NULL && (dsw->d_flags & D_TTY) != 0)
@ -581,7 +581,7 @@ devfs_close(struct vop_close_args *ap)
* if the reference count is 2 (this last descriptor * if the reference count is 2 (this last descriptor
* plus the session), release the reference from the session. * plus the session), release the reference from the session.
*/ */
if (td != NULL) { if (vp->v_usecount == 2 && td != NULL) {
p = td->td_proc; p = td->td_proc;
PROC_LOCK(p); PROC_LOCK(p);
if (vp == p->p_session->s_ttyvp) { if (vp == p->p_session->s_ttyvp) {
@ -591,7 +591,7 @@ devfs_close(struct vop_close_args *ap)
if (vp == p->p_session->s_ttyvp) { if (vp == p->p_session->s_ttyvp) {
SESS_LOCK(p->p_session); SESS_LOCK(p->p_session);
VI_LOCK(vp); VI_LOCK(vp);
if (count_dev(dev) == 2 && if (vp->v_usecount == 2 && vcount(vp) == 1 &&
(vp->v_iflag & VI_DOOMED) == 0) { (vp->v_iflag & VI_DOOMED) == 0) {
p->p_session->s_ttyvp = NULL; p->p_session->s_ttyvp = NULL;
p->p_session->s_ttydp = NULL; p->p_session->s_ttydp = NULL;
@ -620,19 +620,19 @@ devfs_close(struct vop_close_args *ap)
return (ENXIO); return (ENXIO);
dflags = 0; dflags = 0;
VI_LOCK(vp); VI_LOCK(vp);
if (vp->v_usecount == 1 && vcount(vp) == 1)
dflags |= FLASTCLOSE;
if (vp->v_iflag & VI_DOOMED) { if (vp->v_iflag & VI_DOOMED) {
/* Forced close. */ /* Forced close. */
dflags |= FREVOKE | FNONBLOCK; dflags |= FREVOKE | FNONBLOCK;
} else if (dsw->d_flags & D_TRACKCLOSE) { } else if (dsw->d_flags & D_TRACKCLOSE) {
/* Keep device updated on status. */ /* Keep device updated on status. */
} else if (count_dev(dev) > 1) { } else if ((dflags & FLASTCLOSE) == 0) {
VI_UNLOCK(vp); VI_UNLOCK(vp);
dev_relthread(dev, ref); dev_relthread(dev, ref);
return (0); return (0);
} }
if (count_dev(dev) == 1) vholdnz(vp);
dflags |= FLASTCLOSE;
vholdl(vp);
VI_UNLOCK(vp); VI_UNLOCK(vp);
vp_locked = VOP_ISLOCKED(vp); vp_locked = VOP_ISLOCKED(vp);
VOP_UNLOCK(vp, 0); VOP_UNLOCK(vp, 0);
@ -1425,7 +1425,7 @@ devfs_reclaim_vchr(struct vop_reclaim_args *ap)
dev = vp->v_rdev; dev = vp->v_rdev;
vp->v_rdev = NULL; vp->v_rdev = NULL;
if (dev != NULL) if (dev != NULL)
dev->si_usecount -= vp->v_usecount; dev->si_usecount -= (vp->v_usecount > 0);
dev_unlock(); dev_unlock();
VI_UNLOCK(vp); VI_UNLOCK(vp);
if (dev != NULL) if (dev != NULL)

View File

@ -2714,26 +2714,11 @@ _vget_prep(struct vnode *vp, bool interlock)
{ {
enum vgetstate vs; enum vgetstate vs;
if (__predict_true(vp->v_type != VCHR)) { if (refcount_acquire_if_not_zero(&vp->v_usecount)) {
if (refcount_acquire_if_not_zero(&vp->v_usecount)) { vs = VGET_USECOUNT;
vs = VGET_USECOUNT;
} else {
_vhold(vp, interlock);
vs = VGET_HOLDCNT;
}
} else { } else {
if (!interlock) _vhold(vp, interlock);
VI_LOCK(vp); vs = VGET_HOLDCNT;
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);
} }
return (vs); return (vs);
} }
@ -2796,8 +2781,7 @@ vget_finish(struct vnode *vp, int flags, enum vgetstate vs)
* the vnode around. Otherwise someone else lended their hold count and * the vnode around. Otherwise someone else lended their hold count and
* we have to drop ours. * we have to drop ours.
*/ */
if (vp->v_type != VCHR && if (refcount_acquire_if_not_zero(&vp->v_usecount)) {
refcount_acquire_if_not_zero(&vp->v_usecount)) {
#ifdef INVARIANTS #ifdef INVARIANTS
int old = atomic_fetchadd_int(&vp->v_holdcnt, -1) - 1; int old = atomic_fetchadd_int(&vp->v_holdcnt, -1) - 1;
VNASSERT(old > 0, vp, ("%s: wrong hold count", __func__)); VNASSERT(old > 0, vp, ("%s: wrong hold count", __func__));
@ -2823,24 +2807,19 @@ vget_finish(struct vnode *vp, int flags, enum vgetstate vs)
* See the previous section. By the time we get here we may find * See the previous section. By the time we get here we may find
* ourselves in the same spot. * ourselves in the same spot.
*/ */
if (vp->v_type != VCHR) { if (refcount_acquire_if_not_zero(&vp->v_usecount)) {
if (refcount_acquire_if_not_zero(&vp->v_usecount)) {
#ifdef INVARIANTS #ifdef INVARIANTS
int old = atomic_fetchadd_int(&vp->v_holdcnt, -1) - 1; int old = atomic_fetchadd_int(&vp->v_holdcnt, -1) - 1;
VNASSERT(old > 0, vp, ("%s: wrong hold count", __func__)); VNASSERT(old > 0, vp, ("%s: wrong hold count", __func__));
#else #else
refcount_release(&vp->v_holdcnt); refcount_release(&vp->v_holdcnt);
#endif #endif
VNODE_REFCOUNT_FENCE_ACQ(); VNODE_REFCOUNT_FENCE_ACQ();
VNASSERT((vp->v_iflag & VI_OWEINACT) == 0, vp, VNASSERT((vp->v_iflag & VI_OWEINACT) == 0, vp,
("%s: vnode with usecount and VI_OWEINACT set", ("%s: vnode with usecount and VI_OWEINACT set",
__func__)); __func__));
VI_UNLOCK(vp); VI_UNLOCK(vp);
return (0); return (0);
}
} else {
if (vp->v_usecount > 0)
refcount_release(&vp->v_holdcnt);
} }
if ((vp->v_iflag & VI_OWEINACT) == 0) { if ((vp->v_iflag & VI_OWEINACT) == 0) {
oweinact = 0; oweinact = 0;
@ -2868,8 +2847,7 @@ vref(struct vnode *vp)
ASSERT_VI_UNLOCKED(vp, __func__); ASSERT_VI_UNLOCKED(vp, __func__);
CTR2(KTR_VFS, "%s: vp %p", __func__, vp); CTR2(KTR_VFS, "%s: vp %p", __func__, vp);
if (vp->v_type != VCHR && if (refcount_acquire_if_not_zero(&vp->v_usecount)) {
refcount_acquire_if_not_zero(&vp->v_usecount)) {
VNODE_REFCOUNT_FENCE_ACQ(); VNODE_REFCOUNT_FENCE_ACQ();
VNASSERT(vp->v_holdcnt > 0, vp, VNASSERT(vp->v_holdcnt > 0, vp,
("%s: active vnode not held", __func__)); ("%s: active vnode not held", __func__));
@ -2888,8 +2866,7 @@ vrefl(struct vnode *vp)
ASSERT_VI_LOCKED(vp, __func__); ASSERT_VI_LOCKED(vp, __func__);
CTR2(KTR_VFS, "%s: vp %p", __func__, vp); CTR2(KTR_VFS, "%s: vp %p", __func__, vp);
if (vp->v_type != VCHR && if (refcount_acquire_if_not_zero(&vp->v_usecount)) {
refcount_acquire_if_not_zero(&vp->v_usecount)) {
VNODE_REFCOUNT_FENCE_ACQ(); VNODE_REFCOUNT_FENCE_ACQ();
VNASSERT(vp->v_holdcnt > 0, vp, VNASSERT(vp->v_holdcnt > 0, vp,
("%s: active vnode not held", __func__)); ("%s: active vnode not held", __func__));
@ -2897,8 +2874,7 @@ vrefl(struct vnode *vp)
("%s: vnode with usecount and VI_OWEINACT set", __func__)); ("%s: vnode with usecount and VI_OWEINACT set", __func__));
return; return;
} }
if (vp->v_usecount == 0) vholdl(vp);
vholdl(vp);
if ((vp->v_iflag & VI_OWEINACT) != 0) { if ((vp->v_iflag & VI_OWEINACT) != 0) {
vp->v_iflag &= ~VI_OWEINACT; vp->v_iflag &= ~VI_OWEINACT;
VNODE_REFCOUNT_FENCE_REL(); VNODE_REFCOUNT_FENCE_REL();
@ -2912,12 +2888,6 @@ vrefact(struct vnode *vp)
{ {
CTR2(KTR_VFS, "%s: vp %p", __func__, vp); 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 #ifdef INVARIANTS
int old = atomic_fetchadd_int(&vp->v_usecount, 1); int old = atomic_fetchadd_int(&vp->v_usecount, 1);
VNASSERT(old > 0, vp, ("%s: wrong use count", __func__)); VNASSERT(old > 0, vp, ("%s: wrong use count", __func__));
@ -2986,31 +2956,22 @@ vputx(struct vnode *vp, int func)
* We want to hold the vnode until the inactive finishes to * We want to hold the vnode until the inactive finishes to
* prevent vgone() races. We drop the use count here and the * prevent vgone() races. We drop the use count here and the
* hold count below when we're done. * hold count below when we're done.
*
* If we release the last usecount we take ownership of the hold
* count which provides liveness of the vnode, in which case we
* have to vdrop.
*/ */
if (vp->v_type != VCHR) { if (!refcount_release(&vp->v_usecount))
/* return;
* If we release the last usecount we take ownership of the hold VI_LOCK(vp);
* count which provides liveness of the vnode, in which case we v_decr_devcount(vp);
* have to vdrop. /*
*/ * By the time we got here someone else might have transitioned
if (!refcount_release(&vp->v_usecount)) * the count back to > 0.
return; */
VI_LOCK(vp); if (vp->v_usecount > 0) {
/* vdropl(vp);
* By the time we got here someone else might have transitioned return;
* the count back to > 0.
*/
if (vp->v_usecount > 0) {
vdropl(vp);
return;
}
} else {
VI_LOCK(vp);
v_decr_devcount(vp);
if (!refcount_release(&vp->v_usecount)) {
VI_UNLOCK(vp);
return;
}
} }
if (vp->v_iflag & VI_DOINGINACT) { if (vp->v_iflag & VI_DOINGINACT) {
vdropl(vp); vdropl(vp);
@ -3739,20 +3700,6 @@ vcount(struct vnode *vp)
return (count); return (count);
} }
/*
* Same as above, but using the struct cdev *as argument
*/
int
count_dev(struct cdev *dev)
{
int count;
dev_lock();
count = dev->si_usecount;
dev_unlock();
return(count);
}
/* /*
* Print out a description of a vnode. * Print out a description of a vnode.
*/ */

View File

@ -4148,7 +4148,7 @@ sys_revoke(struct thread *td, struct revoke_args *uap)
if (error != 0) if (error != 0)
goto out; goto out;
} }
if (vcount(vp) > 1) if (vp->v_usecount > 1 || vcount(vp) > 1)
VOP_REVOKE(vp, REVOKEALL); VOP_REVOKE(vp, REVOKEALL);
out: out:
vput(vp); vput(vp);

View File

@ -256,7 +256,6 @@ void make_dev_args_init_impl(struct make_dev_args *_args, size_t _sz);
#define make_dev_args_init(a) \ #define make_dev_args_init(a) \
make_dev_args_init_impl((a), sizeof(struct make_dev_args)) make_dev_args_init_impl((a), sizeof(struct make_dev_args))
int count_dev(struct cdev *_dev);
void delist_dev(struct cdev *_dev); void delist_dev(struct cdev *_dev);
void destroy_dev(struct cdev *_dev); void destroy_dev(struct cdev *_dev);
int destroy_dev_sched(struct cdev *dev); int destroy_dev_sched(struct cdev *dev);