Changeset View
Changeset View
Standalone View
Standalone View
head/sys/kern/vfs_subr.c
Show First 20 Lines • Show All 211 Lines • ▼ Show 20 Lines | |||||
* Various variables used for debugging the new implementation of | * Various variables used for debugging the new implementation of | ||||
* reassignbuf(). | * reassignbuf(). | ||||
* XXX these are probably of (very) limited utility now. | * XXX these are probably of (very) limited utility now. | ||||
*/ | */ | ||||
static int reassignbufcalls; | static int reassignbufcalls; | ||||
SYSCTL_INT(_vfs, OID_AUTO, reassignbufcalls, CTLFLAG_RW | CTLFLAG_STATS, | SYSCTL_INT(_vfs, OID_AUTO, reassignbufcalls, CTLFLAG_RW | CTLFLAG_STATS, | ||||
&reassignbufcalls, 0, "Number of calls to reassignbuf"); | &reassignbufcalls, 0, "Number of calls to reassignbuf"); | ||||
static counter_u64_t free_owe_inact; | static counter_u64_t deferred_inact; | ||||
SYSCTL_COUNTER_U64(_vfs, OID_AUTO, free_owe_inact, CTLFLAG_RD, &free_owe_inact, | SYSCTL_COUNTER_U64(_vfs, OID_AUTO, deferred_inact, CTLFLAG_RD, &deferred_inact, | ||||
"Number of times free vnodes kept on active list due to VFS " | "Number of times inactive processing was deferred"); | ||||
"owing inactivation"); | |||||
/* To keep more than one thread at a time from running vfs_getnewfsid */ | /* To keep more than one thread at a time from running vfs_getnewfsid */ | ||||
static struct mtx mntid_mtx; | static struct mtx mntid_mtx; | ||||
/* | /* | ||||
* Lock for any access to the following: | * Lock for any access to the following: | ||||
* vnode_free_list | * vnode_free_list | ||||
* numvnodes | * numvnodes | ||||
▲ Show 20 Lines • Show All 371 Lines • ▼ Show 20 Lines | vntblinit(void *dummy __unused) | ||||
buf_trie_zone = uma_zcreate("BUF TRIE", pctrie_node_size(), | buf_trie_zone = uma_zcreate("BUF TRIE", pctrie_node_size(), | ||||
NULL, NULL, pctrie_zone_init, NULL, UMA_ALIGN_PTR, | NULL, NULL, pctrie_zone_init, NULL, UMA_ALIGN_PTR, | ||||
UMA_ZONE_NOFREE | UMA_ZONE_VM); | UMA_ZONE_NOFREE | UMA_ZONE_VM); | ||||
uma_prealloc(buf_trie_zone, nbuf); | uma_prealloc(buf_trie_zone, nbuf); | ||||
vnodes_created = counter_u64_alloc(M_WAITOK); | vnodes_created = counter_u64_alloc(M_WAITOK); | ||||
recycles_count = counter_u64_alloc(M_WAITOK); | recycles_count = counter_u64_alloc(M_WAITOK); | ||||
recycles_free_count = counter_u64_alloc(M_WAITOK); | recycles_free_count = counter_u64_alloc(M_WAITOK); | ||||
free_owe_inact = counter_u64_alloc(M_WAITOK); | deferred_inact = counter_u64_alloc(M_WAITOK); | ||||
/* | /* | ||||
* Initialize the filesystem syncer. | * Initialize the filesystem syncer. | ||||
*/ | */ | ||||
syncer_workitem_pending = hashinit(syncer_maxdelay, M_VNODE, | syncer_workitem_pending = hashinit(syncer_maxdelay, M_VNODE, | ||||
&syncer_mask); | &syncer_mask); | ||||
syncer_maxdelay = syncer_mask + 1; | syncer_maxdelay = syncer_mask + 1; | ||||
mtx_init(&sync_mtx, "Syncer mtx", NULL, MTX_DEF); | mtx_init(&sync_mtx, "Syncer mtx", NULL, MTX_DEF); | ||||
▲ Show 20 Lines • Show All 2,387 Lines • ▼ Show 20 Lines | |||||
*/ | */ | ||||
int | int | ||||
vrefcnt(struct vnode *vp) | vrefcnt(struct vnode *vp) | ||||
{ | { | ||||
return (vp->v_usecount); | return (vp->v_usecount); | ||||
} | } | ||||
static void | |||||
vdefer_inactive(struct vnode *vp) | |||||
{ | |||||
ASSERT_VI_LOCKED(vp, __func__); | |||||
VNASSERT(vp->v_iflag & VI_OWEINACT, vp, | |||||
("%s: vnode without VI_OWEINACT", __func__)); | |||||
VNASSERT(!VN_IS_DOOMED(vp), vp, | |||||
("%s: doomed vnode", __func__)); | |||||
if (vp->v_iflag & VI_DEFINACT) { | |||||
VNASSERT(vp->v_holdcnt > 1, vp, ("lost hold count")); | |||||
vdropl(vp); | |||||
return; | |||||
} | |||||
vp->v_iflag |= VI_DEFINACT; | |||||
VI_UNLOCK(vp); | |||||
counter_u64_add(deferred_inact, 1); | |||||
} | |||||
static void | |||||
vdefer_inactive_cond(struct vnode *vp) | |||||
{ | |||||
VI_LOCK(vp); | |||||
VNASSERT(vp->v_holdcnt > 0, vp, ("vnode without hold count")); | |||||
if (VN_IS_DOOMED(vp) || | |||||
(vp->v_iflag & VI_OWEINACT) == 0) { | |||||
vdropl(vp); | |||||
return; | |||||
} | |||||
vdefer_inactive(vp); | |||||
} | |||||
enum vputx_op { VPUTX_VRELE, VPUTX_VPUT, VPUTX_VUNREF }; | enum vputx_op { VPUTX_VRELE, VPUTX_VPUT, VPUTX_VUNREF }; | ||||
/* | /* | ||||
* 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 | ||||
▲ Show 20 Lines • Show All 73 Lines • ▼ Show 20 Lines | vputx(struct vnode *vp, enum vputx_op func) | ||||
} | } | ||||
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")); | ||||
if (error == 0) { | if (error == 0) { | ||||
if (vp->v_iflag & VI_OWEINACT) | if (vp->v_iflag & VI_OWEINACT) | ||||
vinactive(vp); | vinactive(vp); | ||||
if (func != VPUTX_VUNREF) | if (func != VPUTX_VUNREF) | ||||
VOP_UNLOCK(vp); | VOP_UNLOCK(vp); | ||||
} | |||||
vdropl(vp); | vdropl(vp); | ||||
} else if (vp->v_iflag & VI_OWEINACT) { | |||||
vdefer_inactive(vp); | |||||
} else { | |||||
vdropl(vp); | |||||
} | } | ||||
} | |||||
/* | /* | ||||
* Vnode put/release. | * Vnode put/release. | ||||
* If count drops to zero, call inactive routine and return to freelist. | * If count drops to zero, call inactive routine and return to freelist. | ||||
*/ | */ | ||||
void | void | ||||
vrele(struct vnode *vp) | vrele(struct vnode *vp) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 137 Lines • ▼ Show 20 Lines | vdrop_deactivate(struct vnode *vp) | ||||
* and put it up for recycling on the freelist. | * and put it up for recycling on the freelist. | ||||
*/ | */ | ||||
VNASSERT(!VN_IS_DOOMED(vp), vp, | VNASSERT(!VN_IS_DOOMED(vp), vp, | ||||
("vdrop: returning doomed vnode")); | ("vdrop: returning doomed vnode")); | ||||
VNASSERT(vp->v_op != NULL, vp, | VNASSERT(vp->v_op != NULL, vp, | ||||
("vdrop: vnode already reclaimed.")); | ("vdrop: vnode already reclaimed.")); | ||||
VNASSERT((vp->v_iflag & VI_FREE) == 0, vp, | VNASSERT((vp->v_iflag & VI_FREE) == 0, vp, | ||||
("vnode already free")); | ("vnode already free")); | ||||
VNASSERT((vp->v_iflag & VI_OWEINACT) == 0, vp, | |||||
("vnode with VI_OWEINACT set")); | |||||
VNASSERT((vp->v_iflag & VI_DEFINACT) == 0, vp, | |||||
("vnode with VI_DEFINACT set")); | |||||
VNASSERT(vp->v_holdcnt == 0, vp, | VNASSERT(vp->v_holdcnt == 0, vp, | ||||
("vdrop: freeing when we shouldn't")); | ("vdrop: freeing when we shouldn't")); | ||||
if ((vp->v_iflag & VI_OWEINACT) == 0) { | |||||
mp = vp->v_mount; | mp = vp->v_mount; | ||||
mtx_lock(&mp->mnt_listmtx); | mtx_lock(&mp->mnt_listmtx); | ||||
if (vp->v_iflag & VI_ACTIVE) { | if (vp->v_iflag & VI_ACTIVE) { | ||||
vp->v_iflag &= ~VI_ACTIVE; | vp->v_iflag &= ~VI_ACTIVE; | ||||
TAILQ_REMOVE(&mp->mnt_activevnodelist, vp, v_actfreelist); | TAILQ_REMOVE(&mp->mnt_activevnodelist, vp, v_actfreelist); | ||||
mp->mnt_activevnodelistsize--; | mp->mnt_activevnodelistsize--; | ||||
} | } | ||||
TAILQ_INSERT_TAIL(&mp->mnt_tmpfreevnodelist, vp, v_actfreelist); | TAILQ_INSERT_TAIL(&mp->mnt_tmpfreevnodelist, vp, v_actfreelist); | ||||
mp->mnt_tmpfreevnodelistsize++; | mp->mnt_tmpfreevnodelistsize++; | ||||
vp->v_iflag |= VI_FREE; | vp->v_iflag |= VI_FREE; | ||||
vp->v_mflag |= VMP_TMPMNTFREELIST; | vp->v_mflag |= VMP_TMPMNTFREELIST; | ||||
VI_UNLOCK(vp); | VI_UNLOCK(vp); | ||||
if (mp->mnt_tmpfreevnodelistsize >= mnt_free_list_batch) | if (mp->mnt_tmpfreevnodelistsize >= mnt_free_list_batch) | ||||
vnlru_return_batch_locked(mp); | vnlru_return_batch_locked(mp); | ||||
mtx_unlock(&mp->mnt_listmtx); | mtx_unlock(&mp->mnt_listmtx); | ||||
} else { | |||||
VI_UNLOCK(vp); | |||||
counter_u64_add(free_owe_inact, 1); | |||||
} | } | ||||
} | |||||
void | void | ||||
vdrop(struct vnode *vp) | vdrop(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 (refcount_release_if_not_last(&vp->v_holdcnt)) | if (refcount_release_if_not_last(&vp->v_holdcnt)) | ||||
▲ Show 20 Lines • Show All 333 Lines • ▼ Show 20 Lines | vgonel(struct vnode *vp) | ||||
vp->v_irflag |= VIRF_DOOMED; | vp->v_irflag |= VIRF_DOOMED; | ||||
/* | /* | ||||
* Check to see if the vnode is in use. If so, we have to call | * Check to see if the vnode is in use. If so, we have to call | ||||
* VOP_CLOSE() and VOP_INACTIVE(). | * VOP_CLOSE() and VOP_INACTIVE(). | ||||
*/ | */ | ||||
active = vp->v_usecount > 0; | active = vp->v_usecount > 0; | ||||
oweinact = (vp->v_iflag & VI_OWEINACT) != 0; | oweinact = (vp->v_iflag & VI_OWEINACT) != 0; | ||||
/* | |||||
* If we need to do inactive VI_OWEINACT will be set. | |||||
*/ | |||||
if (vp->v_iflag & VI_DEFINACT) { | |||||
VNASSERT(vp->v_holdcnt > 1, vp, ("lost hold count")); | |||||
vp->v_iflag &= ~VI_DEFINACT; | |||||
vdropl(vp); | |||||
} else { | |||||
VNASSERT(vp->v_holdcnt > 0, vp, ("vnode without hold count")); | |||||
VI_UNLOCK(vp); | VI_UNLOCK(vp); | ||||
} | |||||
vfs_notify_upper(vp, VFS_NOTIFY_UPPER_RECLAIM); | vfs_notify_upper(vp, VFS_NOTIFY_UPPER_RECLAIM); | ||||
/* | /* | ||||
* If purging an active vnode, it must be closed and | * If purging an active vnode, it must be closed and | ||||
* deactivated before being reclaimed. | * deactivated before being reclaimed. | ||||
*/ | */ | ||||
if (active) | if (active) | ||||
VOP_CLOSE(vp, FNONBLOCK, NOCRED, td); | VOP_CLOSE(vp, FNONBLOCK, NOCRED, td); | ||||
▲ Show 20 Lines • Show All 177 Lines • ▼ Show 20 Lines | vn_printf(struct vnode *vp, const char *fmt, ...) | ||||
if (vp->v_iflag & VI_FREE) | if (vp->v_iflag & VI_FREE) | ||||
strlcat(buf, "|VI_FREE", sizeof(buf)); | strlcat(buf, "|VI_FREE", sizeof(buf)); | ||||
if (vp->v_iflag & VI_ACTIVE) | if (vp->v_iflag & VI_ACTIVE) | ||||
strlcat(buf, "|VI_ACTIVE", sizeof(buf)); | strlcat(buf, "|VI_ACTIVE", sizeof(buf)); | ||||
if (vp->v_iflag & VI_DOINGINACT) | if (vp->v_iflag & VI_DOINGINACT) | ||||
strlcat(buf, "|VI_DOINGINACT", sizeof(buf)); | strlcat(buf, "|VI_DOINGINACT", sizeof(buf)); | ||||
if (vp->v_iflag & VI_OWEINACT) | if (vp->v_iflag & VI_OWEINACT) | ||||
strlcat(buf, "|VI_OWEINACT", sizeof(buf)); | strlcat(buf, "|VI_OWEINACT", sizeof(buf)); | ||||
if (vp->v_iflag & VI_DEFINACT) | |||||
strlcat(buf, "|VI_DEFINACT", sizeof(buf)); | |||||
flags = vp->v_iflag & ~(VI_TEXT_REF | VI_MOUNT | VI_FREE | VI_ACTIVE | | flags = vp->v_iflag & ~(VI_TEXT_REF | VI_MOUNT | VI_FREE | VI_ACTIVE | | ||||
VI_DOINGINACT | VI_OWEINACT); | VI_DOINGINACT | VI_OWEINACT | VI_DEFINACT); | ||||
if (flags != 0) { | if (flags != 0) { | ||||
snprintf(buf2, sizeof(buf2), "|VI(0x%lx)", flags); | snprintf(buf2, sizeof(buf2), "|VI(0x%lx)", flags); | ||||
strlcat(buf, buf2, sizeof(buf)); | strlcat(buf, buf2, sizeof(buf)); | ||||
} | } | ||||
if (vp->v_mflag & VMP_TMPMNTFREELIST) | if (vp->v_mflag & VMP_TMPMNTFREELIST) | ||||
strlcat(buf, "|VMP_TMPMNTFREELIST", sizeof(buf)); | strlcat(buf, "|VMP_TMPMNTFREELIST", sizeof(buf)); | ||||
flags = vp->v_mflag & ~(VMP_TMPMNTFREELIST); | flags = vp->v_mflag & ~(VMP_TMPMNTFREELIST); | ||||
if (flags != 0) { | if (flags != 0) { | ||||
▲ Show 20 Lines • Show All 540 Lines • ▼ Show 20 Lines | TAILQ_FOREACH_REVERSE_SAFE(mp, &mountlist, mntlist, mnt_list, tmp) { | ||||
unmount_or_warn(mp); | unmount_or_warn(mp); | ||||
} | } | ||||
if (rootdevmp != NULL) | if (rootdevmp != NULL) | ||||
unmount_or_warn(rootdevmp); | unmount_or_warn(rootdevmp); | ||||
} | } | ||||
/* | static void | ||||
* perform msync on all vnodes under a mount point | vfs_deferred_inactive(struct vnode *vp, int lkflags) | ||||
* the mount point must be locked. | |||||
*/ | |||||
void | |||||
vfs_msync(struct mount *mp, int flags) | |||||
{ | { | ||||
ASSERT_VI_LOCKED(vp, __func__); | |||||
VNASSERT((vp->v_iflag & VI_DEFINACT) == 0, vp, ("VI_DEFINACT still set")); | |||||
if ((vp->v_iflag & VI_OWEINACT) == 0) { | |||||
vdropl(vp); | |||||
return; | |||||
} | |||||
if (vn_lock(vp, lkflags) == 0) { | |||||
VI_LOCK(vp); | |||||
if ((vp->v_iflag & (VI_OWEINACT | VI_DOINGINACT)) == VI_OWEINACT) | |||||
vinactive(vp); | |||||
VOP_UNLOCK(vp); | |||||
vdropl(vp); | |||||
return; | |||||
} | |||||
vdefer_inactive_cond(vp); | |||||
} | |||||
static void __noinline | |||||
vfs_periodic_inactive(struct mount *mp, int flags) | |||||
{ | |||||
struct vnode *vp, *mvp; | struct vnode *vp, *mvp; | ||||
int lkflags; | |||||
lkflags = LK_EXCLUSIVE | LK_INTERLOCK; | |||||
if (flags != MNT_WAIT) | |||||
lkflags |= LK_NOWAIT; | |||||
MNT_VNODE_FOREACH_ACTIVE(vp, mp, mvp) { | |||||
if ((vp->v_iflag & VI_DEFINACT) == 0) { | |||||
VI_UNLOCK(vp); | |||||
continue; | |||||
} | |||||
vp->v_iflag &= ~VI_DEFINACT; | |||||
vfs_deferred_inactive(vp, lkflags); | |||||
} | |||||
} | |||||
static inline bool | |||||
vfs_want_msync(struct vnode *vp) | |||||
{ | |||||
struct vm_object *obj; | struct vm_object *obj; | ||||
if (vp->v_vflag & VV_NOSYNC) | |||||
return (false); | |||||
obj = vp->v_object; | |||||
return (obj != NULL && vm_object_mightbedirty(obj)); | |||||
} | |||||
static void __noinline | |||||
vfs_periodic_msync_inactive(struct mount *mp, int flags) | |||||
{ | |||||
struct vnode *vp, *mvp; | |||||
struct vm_object *obj; | |||||
struct thread *td; | struct thread *td; | ||||
int lkflags, objflags; | int lkflags, objflags; | ||||
bool seen_defer; | |||||
CTR2(KTR_VFS, "%s: mp %p", __func__, mp); | |||||
if ((mp->mnt_kern_flag & MNTK_NOMSYNC) != 0) | |||||
return; | |||||
td = curthread; | td = curthread; | ||||
lkflags = LK_EXCLUSIVE | LK_INTERLOCK; | lkflags = LK_EXCLUSIVE | LK_INTERLOCK; | ||||
if (flags != MNT_WAIT) { | if (flags != MNT_WAIT) { | ||||
lkflags |= LK_NOWAIT; | lkflags |= LK_NOWAIT; | ||||
objflags = OBJPC_NOSYNC; | objflags = OBJPC_NOSYNC; | ||||
} else { | } else { | ||||
objflags = OBJPC_SYNC; | objflags = OBJPC_SYNC; | ||||
} | } | ||||
MNT_VNODE_FOREACH_ACTIVE(vp, mp, mvp) { | MNT_VNODE_FOREACH_ACTIVE(vp, mp, mvp) { | ||||
obj = vp->v_object; | seen_defer = false; | ||||
if (obj == NULL || !vm_object_mightbedirty(obj)) { | if (vp->v_iflag & VI_DEFINACT) { | ||||
vp->v_iflag &= ~VI_DEFINACT; | |||||
seen_defer = true; | |||||
} | |||||
if (!vfs_want_msync(vp)) { | |||||
if (seen_defer) | |||||
vfs_deferred_inactive(vp, lkflags); | |||||
else | |||||
VI_UNLOCK(vp); | VI_UNLOCK(vp); | ||||
continue; | continue; | ||||
} | } | ||||
if (vget(vp, lkflags, td) == 0) { | if (vget(vp, lkflags, td) == 0) { | ||||
obj = vp->v_object; | obj = vp->v_object; | ||||
if (obj != NULL && (vp->v_vflag & VV_NOSYNC) == 0) { | if (obj != NULL && (vp->v_vflag & VV_NOSYNC) == 0) { | ||||
VM_OBJECT_WLOCK(obj); | VM_OBJECT_WLOCK(obj); | ||||
vm_object_page_clean(obj, 0, 0, objflags); | vm_object_page_clean(obj, 0, 0, objflags); | ||||
VM_OBJECT_WUNLOCK(obj); | VM_OBJECT_WUNLOCK(obj); | ||||
} | } | ||||
vput(vp); | vput(vp); | ||||
if (seen_defer) | |||||
vdrop(vp); | |||||
} else { | |||||
if (seen_defer) | |||||
vdefer_inactive_cond(vp); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
void | |||||
vfs_periodic(struct mount *mp, int flags) | |||||
{ | |||||
CTR2(KTR_VFS, "%s: mp %p", __func__, mp); | |||||
if ((mp->mnt_kern_flag & MNTK_NOMSYNC) != 0) | |||||
vfs_periodic_inactive(mp, flags); | |||||
else | |||||
vfs_periodic_msync_inactive(mp, flags); | |||||
} | |||||
static void | static void | ||||
destroy_vpollinfo_free(struct vpollinfo *vi) | destroy_vpollinfo_free(struct vpollinfo *vi) | ||||
{ | { | ||||
knlist_destroy(&vi->vpi_selinfo.si_note); | knlist_destroy(&vi->vpi_selinfo.si_note); | ||||
mtx_destroy(&vi->vpi_lock); | mtx_destroy(&vi->vpi_lock); | ||||
uma_zfree(vnodepoll_zone, vi); | uma_zfree(vnodepoll_zone, vi); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 194 Lines • ▼ Show 20 Lines | if (vn_start_write(NULL, &mp, V_NOWAIT) != 0) { | ||||
return (0); | return (0); | ||||
} | } | ||||
save = curthread_pflags_set(TDP_SYNCIO); | save = curthread_pflags_set(TDP_SYNCIO); | ||||
/* | /* | ||||
* The filesystem at hand may be idle with free vnodes stored in the | * The filesystem at hand may be idle with free vnodes stored in the | ||||
* batch. Return them instead of letting them stay there indefinitely. | * batch. Return them instead of letting them stay there indefinitely. | ||||
*/ | */ | ||||
vnlru_return_batch(mp); | vnlru_return_batch(mp); | ||||
vfs_msync(mp, MNT_NOWAIT); | vfs_periodic(mp, MNT_NOWAIT); | ||||
error = VFS_SYNC(mp, MNT_LAZY); | error = VFS_SYNC(mp, MNT_LAZY); | ||||
curthread_pflags_restore(save); | curthread_pflags_restore(save); | ||||
vn_finished_write(mp); | vn_finished_write(mp); | ||||
vfs_unbusy(mp); | vfs_unbusy(mp); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 1,448 Lines • Show Last 20 Lines |