vfs: fix device count leak on vrele racing with vgone

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.

Note this can only happen if the vnode lock is not held.

Reviewed by:	kib
Tested by:	pho
Differential Revision:	https://reviews.freebsd.org/D23529
This commit is contained in:
Mateusz Guzik 2020-02-10 22:28:54 +00:00
parent 37d4ece7c5
commit 2e57c8fde7

View file

@ -3192,12 +3192,22 @@ vputx(struct vnode *vp, enum vputx_op func)
* count which provides liveness of the vnode, in which case we
* have to vdrop.
*/
if (!refcount_release(&vp->v_usecount)) {
if (func == VPUTX_VPUT)
VOP_UNLOCK(vp);
return;
if (__predict_false(vp->v_type == VCHR && func == VPUTX_VRELE)) {
if (refcount_release_if_not_last(&vp->v_usecount))
return;
VI_LOCK(vp);
if (!refcount_release(&vp->v_usecount)) {
VI_UNLOCK(vp);
return;
}
} else {
if (!refcount_release(&vp->v_usecount)) {
if (func == VPUTX_VPUT)
VOP_UNLOCK(vp);
return;
}
VI_LOCK(vp);
}
VI_LOCK(vp);
v_decr_devcount(vp);
/*
* By the time we got here someone else might have transitioned