Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/vfs_subr.c
Show First 20 Lines • Show All 2,624 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* Grab a particular vnode from the free list, increment its | * Grab a particular vnode from the free list, increment its | ||||
* reference count and lock it. VI_DOOMED is set if the vnode | * reference count and lock it. VI_DOOMED is set if the vnode | ||||
* is being destroyed. Only callers who specify LK_RETRY will | * is being destroyed. Only callers who specify LK_RETRY will | ||||
* see doomed vnodes. If inactive processing was delayed in | * see doomed vnodes. If inactive processing was delayed in | ||||
* vput try to do it here. | * vput try to do it here. | ||||
* | * | ||||
* Notes on lockless counter manipulation: | * Both holdcnt and usecount can be manipulated using atomics without holding any | ||||
* _vhold, vputx and other routines make various decisions based | * locks except in these cases which require the vnode interlock: | ||||
* on either holdcnt or usecount being 0. As long as either counter | * holdcnt: 1->0 and 0->1 | ||||
* is not transitioning 0->1 nor 1->0, the manipulation can be done | * usecount: 0->1 | ||||
* with atomic operations. Otherwise the interlock is taken covering | * | ||||
* both the atomic and additional actions. | * usecount is permitted to transition 1->0 without the interlock because | ||||
* vnode is kept live by holdcnt. | |||||
* | |||||
*/ | */ | ||||
static enum vgetstate | static enum vgetstate | ||||
_vget_prep(struct vnode *vp, bool interlock) | _vget_prep(struct vnode *vp, bool interlock) | ||||
{ | { | ||||
enum vgetstate vs; | enum vgetstate vs; | ||||
if (refcount_acquire_if_not_zero(&vp->v_usecount)) { | if (refcount_acquire_if_not_zero(&vp->v_usecount)) { | ||||
vs = VGET_USECOUNT; | vs = VGET_USECOUNT; | ||||
▲ Show 20 Lines • Show All 79 Lines • ▼ Show 20 Lines | #endif | ||||
* We don't guarantee that any particular close will | * We don't guarantee that any particular close will | ||||
* trigger inactive processing so just make a best effort | * trigger inactive processing so just make a best effort | ||||
* here at preventing a reference to a removed file. If | * here at preventing a reference to a removed file. If | ||||
* we don't succeed no harm is done. | * we don't succeed no harm is done. | ||||
* | * | ||||
* Upgrade our holdcnt to a usecount. | * Upgrade our holdcnt to a usecount. | ||||
*/ | */ | ||||
VI_LOCK(vp); | VI_LOCK(vp); | ||||
/* | |||||
* See the previous section. By the time we get here we may find | |||||
* ourselves in the same spot. | |||||
*/ | |||||
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__)); | |||||
#else | |||||
refcount_release(&vp->v_holdcnt); | |||||
#endif | |||||
VNODE_REFCOUNT_FENCE_ACQ(); | |||||
VNASSERT((vp->v_iflag & VI_OWEINACT) == 0, vp, | |||||
("%s: vnode with usecount and VI_OWEINACT set", __func__)); | |||||
VI_UNLOCK(vp); | |||||
return (0); | |||||
} | |||||
if ((vp->v_iflag & VI_OWEINACT) == 0) { | if ((vp->v_iflag & VI_OWEINACT) == 0) { | ||||
oweinact = 0; | oweinact = 0; | ||||
} else { | } else { | ||||
oweinact = 1; | oweinact = 1; | ||||
vp->v_iflag &= ~VI_OWEINACT; | vp->v_iflag &= ~VI_OWEINACT; | ||||
VNODE_REFCOUNT_FENCE_REL(); | VNODE_REFCOUNT_FENCE_REL(); | ||||
} | } | ||||
if (vp->v_usecount > 0) | |||||
refcount_release(&vp->v_holdcnt); | |||||
refcount_acquire(&vp->v_usecount); | refcount_acquire(&vp->v_usecount); | ||||
if (oweinact && VOP_ISLOCKED(vp) == LK_EXCLUSIVE && | if (oweinact && VOP_ISLOCKED(vp) == LK_EXCLUSIVE && | ||||
(flags & LK_NOWAIT) == 0) | (flags & LK_NOWAIT) == 0) | ||||
vinactive(vp, curthread); | vinactive(vp, curthread); | ||||
VI_UNLOCK(vp); | VI_UNLOCK(vp); | ||||
return (0); | return (0); | ||||
} | } | ||||
Show All 21 Lines | |||||
} | } | ||||
void | void | ||||
vrefl(struct vnode *vp) | 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_usecount == 0) | 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__)); | |||||
VNASSERT((vp->v_iflag & VI_OWEINACT) == 0, vp, | |||||
("%s: vnode with usecount and VI_OWEINACT set", __func__)); | |||||
return; | |||||
} | |||||
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(); | ||||
} | } | ||||
refcount_acquire(&vp->v_usecount); | refcount_acquire(&vp->v_usecount); | ||||
} | } | ||||
void | void | ||||
▲ Show 20 Lines • Show All 60 Lines • ▼ Show 20 Lines | vputx(struct vnode *vp, int func) | ||||
* proceeded to free the vnode before our unlock finished. For this | * proceeded to free the vnode before our unlock finished. For this | ||||
* reason we unlock the vnode early. This is a little bit wasteful as | * 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 | * it may be the vnode is exclusively locked and inactive processing is | ||||
* needed, in which case we are adding work. | * needed, in which case we are adding work. | ||||
*/ | */ | ||||
if (func == VPUTX_VPUT) | if (func == VPUTX_VPUT) | ||||
VOP_UNLOCK(vp, 0); | VOP_UNLOCK(vp, 0); | ||||
if (refcount_release_if_not_last(&vp->v_usecount)) | /* | ||||
* If we release the last usecount we take ownership of the hold count | |||||
* which provides liveness of the vnode, which means we have to vdrop. | |||||
*/ | |||||
if (!refcount_release(&vp->v_usecount)) | |||||
return; | return; | ||||
VI_LOCK(vp); | VI_LOCK(vp); | ||||
/* | /* | ||||
* We want to hold the vnode until the inactive finishes to | * By the time we got here someone else might have transitioned the | ||||
* prevent vgone() races. We drop the use count here and the | * count back to > 0. Or the vnode started undergoing inactive. | ||||
* hold count below when we're done. | |||||
*/ | */ | ||||
if (!refcount_release(&vp->v_usecount)) { | if (vp->v_usecount > 0 || vp->v_iflag & VI_DOINGINACT) { | ||||
VI_UNLOCK(vp); | |||||
return; | |||||
} | |||||
if (vp->v_iflag & VI_DOINGINACT) { | |||||
vdropl(vp); | vdropl(vp); | ||||
return; | return; | ||||
} | } | ||||
error = 0; | error = 0; | ||||
if (vp->v_usecount != 0) { | if (vp->v_usecount != 0) { | ||||
vn_printf(vp, "vputx: usecount not zero for vnode "); | vn_printf(vp, "vputx: usecount not zero for vnode "); | ||||
▲ Show 20 Lines • Show All 2,925 Lines • Show Last 20 Lines |