Changeset View
Changeset View
Standalone View
Standalone View
head/sys/kern/vfs_subr.c
Show First 20 Lines • Show All 2,962 Lines • ▼ Show 20 Lines | |||||
*/ | */ | ||||
int | int | ||||
vrefcnt(struct vnode *vp) | vrefcnt(struct vnode *vp) | ||||
{ | { | ||||
return (vp->v_usecount); | return (vp->v_usecount); | ||||
} | } | ||||
#define VPUTX_VRELE 1 | enum vputx_op { VPUTX_VRELE, VPUTX_VPUT, VPUTX_VUNREF }; | ||||
#define VPUTX_VPUT 2 | |||||
#define VPUTX_VUNREF 3 | |||||
/* | /* | ||||
* Decrement the use and hold counts for a vnode. | * Decrement the use and hold counts for a vnode. | ||||
* | * | ||||
* See an explanation near vget() as to why atomic operation is safe. | * See an explanation near vget() as to why atomic operation is safe. | ||||
*/ | */ | ||||
static void | static void | ||||
vputx(struct vnode *vp, int func) | vputx(struct vnode *vp, enum vputx_op func) | ||||
{ | { | ||||
int error; | int error; | ||||
KASSERT(vp != NULL, ("vputx: null vp")); | KASSERT(vp != NULL, ("vputx: null vp")); | ||||
if (func == VPUTX_VUNREF) | if (func == VPUTX_VUNREF) | ||||
ASSERT_VOP_LOCKED(vp, "vunref"); | ASSERT_VOP_LOCKED(vp, "vunref"); | ||||
else if (func == VPUTX_VPUT) | |||||
ASSERT_VOP_LOCKED(vp, "vput"); | |||||
else | |||||
KASSERT(func == VPUTX_VRELE, ("vputx: wrong func")); | |||||
ASSERT_VI_UNLOCKED(vp, __func__); | ASSERT_VI_UNLOCKED(vp, __func__); | ||||
VNASSERT(vp->v_holdcnt > 0 && vp->v_usecount > 0, vp, | VNASSERT(vp->v_holdcnt > 0 && vp->v_usecount > 0, vp, | ||||
("%s: wrong ref counts", __func__)); | ("%s: wrong ref counts", __func__)); | ||||
CTR2(KTR_VFS, "%s: vp %p", __func__, vp); | CTR2(KTR_VFS, "%s: vp %p", __func__, vp); | ||||
/* | /* | ||||
* It is an invariant that all VOP_* calls operate on a held vnode. | |||||
* We may be only having an implicit hold stemming from our usecount, | |||||
* which we are about to release. If we unlock the vnode afterwards we | |||||
* open a time window where someone else dropped the last usecount and | |||||
* proceeded to free the vnode before our unlock finished. For this | |||||
* reason we unlock the vnode early. This is a little bit wasteful as | |||||
* it may be the vnode is exclusively locked and inactive processing is | |||||
* needed, in which case we are adding work. | |||||
*/ | |||||
if (func == VPUTX_VPUT) | |||||
VOP_UNLOCK(vp, 0); | |||||
/* | |||||
* 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 | * If we release the last usecount we take ownership of the hold | ||||
* count which provides liveness of the vnode, in which case we | * count which provides liveness of the vnode, in which case we | ||||
* have to vdrop. | * have to vdrop. | ||||
*/ | */ | ||||
Show All 9 Lines | if (vp->v_usecount > 0) { | ||||
vdropl(vp); | vdropl(vp); | ||||
return; | return; | ||||
} | } | ||||
if (vp->v_iflag & VI_DOINGINACT) { | if (vp->v_iflag & VI_DOINGINACT) { | ||||
vdropl(vp); | vdropl(vp); | ||||
return; | return; | ||||
} | } | ||||
error = 0; | |||||
if (vp->v_usecount != 0) { | |||||
vn_printf(vp, "vputx: usecount not zero for vnode "); | |||||
panic("vputx: usecount not zero"); | |||||
} | |||||
CTR2(KTR_VFS, "%s: return vnode %p to the freelist", __func__, vp); | |||||
/* | /* | ||||
* Check if the fs wants to perform inactive processing. Note we | * Check if the fs wants to perform inactive processing. Note we | ||||
* may be only holding the interlock, in which case it is possible | * may be only holding the interlock, in which case it is possible | ||||
* someone else called vgone on the vnode and ->v_data is now NULL. | * someone else called vgone on the vnode and ->v_data is now NULL. | ||||
* Since vgone performs inactive on its own there is nothing to do | * Since vgone performs inactive on its own there is nothing to do | ||||
* here but to drop our hold count. | * here but to drop our hold count. | ||||
*/ | */ | ||||
if (__predict_false(vp->v_iflag & VI_DOOMED) || | if (__predict_false(vp->v_iflag & VI_DOOMED) || | ||||
Show All 12 Lines | case VPUTX_VRELE: | ||||
error = vn_lock(vp, LK_EXCLUSIVE | LK_INTERLOCK); | error = vn_lock(vp, LK_EXCLUSIVE | LK_INTERLOCK); | ||||
VI_LOCK(vp); | VI_LOCK(vp); | ||||
break; | break; | ||||
case VPUTX_VPUT: | case VPUTX_VPUT: | ||||
error = VOP_LOCK(vp, LK_EXCLUSIVE | LK_INTERLOCK | LK_NOWAIT); | error = VOP_LOCK(vp, LK_EXCLUSIVE | LK_INTERLOCK | LK_NOWAIT); | ||||
VI_LOCK(vp); | VI_LOCK(vp); | ||||
break; | break; | ||||
case VPUTX_VUNREF: | case VPUTX_VUNREF: | ||||
error = 0; | |||||
if (VOP_ISLOCKED(vp) != LK_EXCLUSIVE) { | if (VOP_ISLOCKED(vp) != LK_EXCLUSIVE) { | ||||
error = VOP_LOCK(vp, LK_TRYUPGRADE | LK_INTERLOCK); | error = VOP_LOCK(vp, LK_TRYUPGRADE | LK_INTERLOCK); | ||||
VI_LOCK(vp); | VI_LOCK(vp); | ||||
} | } | ||||
break; | break; | ||||
} | } | ||||
VNASSERT(vp->v_usecount == 0 || (vp->v_iflag & VI_OWEINACT) == 0, vp, | VNASSERT(vp->v_usecount == 0 || (vp->v_iflag & VI_OWEINACT) == 0, vp, | ||||
("vnode with usecount and VI_OWEINACT set")); | ("vnode with usecount and VI_OWEINACT set")); | ||||
Show All 16 Lines | vrele(struct vnode *vp) | ||||
vputx(vp, VPUTX_VRELE); | vputx(vp, VPUTX_VRELE); | ||||
} | } | ||||
/* | /* | ||||
* Release an already locked vnode. This give the same effects as | * Release an already locked vnode. This give the same effects as | ||||
* unlock+vrele(), but takes less time and avoids releasing and | * unlock+vrele(), but takes less time and avoids releasing and | ||||
* re-aquiring the lock (as vrele() acquires the lock internally.) | * re-aquiring the lock (as vrele() acquires the lock internally.) | ||||
* | |||||
* It is an invariant that all VOP_* calls operate on a held vnode. | |||||
* We may be only having an implicit hold stemming from our usecount, | |||||
* which we are about to release. If we unlock the vnode afterwards we | |||||
* open a time window where someone else dropped the last usecount and | |||||
* proceeded to free the vnode before our unlock finished. For this | |||||
* reason we unlock the vnode early. This is a little bit wasteful as | |||||
* it may be the vnode is exclusively locked and inactive processing is | |||||
* needed, in which case we are adding work. | |||||
*/ | */ | ||||
void | void | ||||
vput(struct vnode *vp) | vput(struct vnode *vp) | ||||
{ | { | ||||
VOP_UNLOCK(vp, 0); | |||||
vputx(vp, VPUTX_VPUT); | vputx(vp, VPUTX_VPUT); | ||||
} | } | ||||
/* | /* | ||||
* Release an exclusively locked vnode. Do not unlock the vnode lock. | * Release an exclusively locked vnode. Do not unlock the vnode lock. | ||||
*/ | */ | ||||
void | void | ||||
vunref(struct vnode *vp) | vunref(struct vnode *vp) | ||||
▲ Show 20 Lines • Show All 2,949 Lines • Show Last 20 Lines |