tmpfs: reimplement the mtime scan to use the lazy list
Tested by: pho Reviewed by: kib, markj Differential Revision: https://reviews.freebsd.org/D30065
This commit is contained in:
parent
128e25842e
commit
eec2e4ef7f
@ -46,6 +46,7 @@ MALLOC_DECLARE(M_TMPFSNAME);
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define OBJ_TMPFS OBJ_PAGERPRIV1 /* has tmpfs vnode allocated */
|
#define OBJ_TMPFS OBJ_PAGERPRIV1 /* has tmpfs vnode allocated */
|
||||||
|
#define OBJ_TMPFS_VREF OBJ_PAGERPRIV2 /* vnode is referenced */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Internal representation of a tmpfs directory entry.
|
* Internal representation of a tmpfs directory entry.
|
||||||
|
@ -99,6 +99,92 @@ tmpfs_pager_alloc(void *handle, vm_ooffset_t size, vm_prot_t prot,
|
|||||||
return (object);
|
return (object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure tmpfs vnodes with writable mappings can be found on the lazy list.
|
||||||
|
*
|
||||||
|
* This allows for periodic mtime updates while only scanning vnodes which are
|
||||||
|
* plausibly dirty, see tmpfs_update_mtime_lazy.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
tmpfs_pager_writecount_recalc(vm_object_t object, vm_offset_t old,
|
||||||
|
vm_offset_t new)
|
||||||
|
{
|
||||||
|
struct vnode *vp;
|
||||||
|
|
||||||
|
VM_OBJECT_ASSERT_WLOCKED(object);
|
||||||
|
|
||||||
|
vp = object->un_pager.swp.swp_tmpfs;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Forced unmount?
|
||||||
|
*/
|
||||||
|
if (vp == NULL) {
|
||||||
|
KASSERT((object->flags & OBJ_TMPFS_VREF) == 0,
|
||||||
|
("object %p with OBJ_TMPFS_VREF but without vnode", object));
|
||||||
|
VM_OBJECT_WUNLOCK(object);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (old == 0) {
|
||||||
|
VNASSERT((object->flags & OBJ_TMPFS_VREF) == 0, vp,
|
||||||
|
("object without writable mappings has a reference"));
|
||||||
|
VNPASS(vp->v_usecount > 0, vp);
|
||||||
|
} else {
|
||||||
|
VNASSERT((object->flags & OBJ_TMPFS_VREF) != 0, vp,
|
||||||
|
("object with writable mappings does not have a reference"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (old == new) {
|
||||||
|
VM_OBJECT_WUNLOCK(object);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new == 0) {
|
||||||
|
vm_object_clear_flag(object, OBJ_TMPFS_VREF);
|
||||||
|
VM_OBJECT_WUNLOCK(object);
|
||||||
|
vrele(vp);
|
||||||
|
} else {
|
||||||
|
if ((object->flags & OBJ_TMPFS_VREF) == 0) {
|
||||||
|
vref(vp);
|
||||||
|
vlazy(vp);
|
||||||
|
vm_object_set_flag(object, OBJ_TMPFS_VREF);
|
||||||
|
}
|
||||||
|
VM_OBJECT_WUNLOCK(object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
tmpfs_pager_update_writecount(vm_object_t object, vm_offset_t start,
|
||||||
|
vm_offset_t end)
|
||||||
|
{
|
||||||
|
vm_offset_t new, old;
|
||||||
|
|
||||||
|
VM_OBJECT_WLOCK(object);
|
||||||
|
KASSERT((object->flags & OBJ_ANON) == 0,
|
||||||
|
("%s: object %p with OBJ_ANON", __func__, object));
|
||||||
|
old = object->un_pager.swp.writemappings;
|
||||||
|
object->un_pager.swp.writemappings += (vm_ooffset_t)end - start;
|
||||||
|
new = object->un_pager.swp.writemappings;
|
||||||
|
tmpfs_pager_writecount_recalc(object, old, new);
|
||||||
|
VM_OBJECT_ASSERT_UNLOCKED(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
tmpfs_pager_release_writecount(vm_object_t object, vm_offset_t start,
|
||||||
|
vm_offset_t end)
|
||||||
|
{
|
||||||
|
vm_offset_t new, old;
|
||||||
|
|
||||||
|
VM_OBJECT_WLOCK(object);
|
||||||
|
KASSERT((object->flags & OBJ_ANON) == 0,
|
||||||
|
("%s: object %p with OBJ_ANON", __func__, object));
|
||||||
|
old = object->un_pager.swp.writemappings;
|
||||||
|
object->un_pager.swp.writemappings -= (vm_ooffset_t)end - start;
|
||||||
|
new = object->un_pager.swp.writemappings;
|
||||||
|
tmpfs_pager_writecount_recalc(object, old, new);
|
||||||
|
VM_OBJECT_ASSERT_UNLOCKED(object);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
tmpfs_pager_getvp(vm_object_t object, struct vnode **vpp, bool *vp_heldp)
|
tmpfs_pager_getvp(vm_object_t object, struct vnode **vpp, bool *vp_heldp)
|
||||||
{
|
{
|
||||||
@ -131,6 +217,8 @@ struct pagerops tmpfs_pager_ops = {
|
|||||||
.pgo_kvme_type = KVME_TYPE_VNODE,
|
.pgo_kvme_type = KVME_TYPE_VNODE,
|
||||||
.pgo_alloc = tmpfs_pager_alloc,
|
.pgo_alloc = tmpfs_pager_alloc,
|
||||||
.pgo_set_writeable_dirty = vm_object_set_writeable_dirty_,
|
.pgo_set_writeable_dirty = vm_object_set_writeable_dirty_,
|
||||||
|
.pgo_update_writecount = tmpfs_pager_update_writecount,
|
||||||
|
.pgo_release_writecount = tmpfs_pager_release_writecount,
|
||||||
.pgo_mightbedirty = vm_object_mightbedirty_,
|
.pgo_mightbedirty = vm_object_mightbedirty_,
|
||||||
.pgo_getvp = tmpfs_pager_getvp,
|
.pgo_getvp = tmpfs_pager_getvp,
|
||||||
};
|
};
|
||||||
@ -643,6 +731,7 @@ tmpfs_free_dirent(struct tmpfs_mount *tmp, struct tmpfs_dirent *de)
|
|||||||
void
|
void
|
||||||
tmpfs_destroy_vobject(struct vnode *vp, vm_object_t obj)
|
tmpfs_destroy_vobject(struct vnode *vp, vm_object_t obj)
|
||||||
{
|
{
|
||||||
|
bool want_vrele;
|
||||||
|
|
||||||
ASSERT_VOP_ELOCKED(vp, "tmpfs_destroy_vobject");
|
ASSERT_VOP_ELOCKED(vp, "tmpfs_destroy_vobject");
|
||||||
if (vp->v_type != VREG || obj == NULL)
|
if (vp->v_type != VREG || obj == NULL)
|
||||||
@ -650,12 +739,24 @@ tmpfs_destroy_vobject(struct vnode *vp, vm_object_t obj)
|
|||||||
|
|
||||||
VM_OBJECT_WLOCK(obj);
|
VM_OBJECT_WLOCK(obj);
|
||||||
VI_LOCK(vp);
|
VI_LOCK(vp);
|
||||||
|
/*
|
||||||
|
* May be going through forced unmount.
|
||||||
|
*/
|
||||||
|
want_vrele = false;
|
||||||
|
if ((obj->flags & OBJ_TMPFS_VREF) != 0) {
|
||||||
|
vm_object_clear_flag(obj, OBJ_TMPFS_VREF);
|
||||||
|
want_vrele = true;
|
||||||
|
}
|
||||||
|
|
||||||
vm_object_clear_flag(obj, OBJ_TMPFS);
|
vm_object_clear_flag(obj, OBJ_TMPFS);
|
||||||
obj->un_pager.swp.swp_tmpfs = NULL;
|
obj->un_pager.swp.swp_tmpfs = NULL;
|
||||||
if (vp->v_writecount < 0)
|
if (vp->v_writecount < 0)
|
||||||
vp->v_writecount = 0;
|
vp->v_writecount = 0;
|
||||||
VI_UNLOCK(vp);
|
VI_UNLOCK(vp);
|
||||||
VM_OBJECT_WUNLOCK(obj);
|
VM_OBJECT_WUNLOCK(obj);
|
||||||
|
if (want_vrele) {
|
||||||
|
vrele(vp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -792,6 +893,12 @@ tmpfs_alloc_vp(struct mount *mp, struct tmpfs_node *node, int lkflag,
|
|||||||
case VREG:
|
case VREG:
|
||||||
object = node->tn_reg.tn_aobj;
|
object = node->tn_reg.tn_aobj;
|
||||||
VM_OBJECT_WLOCK(object);
|
VM_OBJECT_WLOCK(object);
|
||||||
|
KASSERT((object->flags & OBJ_TMPFS_VREF) == 0,
|
||||||
|
("%s: object %p with OBJ_TMPFS_VREF but without vnode",
|
||||||
|
__func__, object));
|
||||||
|
KASSERT(object->un_pager.swp.writemappings == 0,
|
||||||
|
("%s: object %p has writemappings",
|
||||||
|
__func__, object));
|
||||||
VI_LOCK(vp);
|
VI_LOCK(vp);
|
||||||
KASSERT(vp->v_object == NULL, ("Not NULL v_object in tmpfs"));
|
KASSERT(vp->v_object == NULL, ("Not NULL v_object in tmpfs"));
|
||||||
vp->v_object = object;
|
vp->v_object = object;
|
||||||
|
@ -99,18 +99,38 @@ static const char *tmpfs_updateopts[] = {
|
|||||||
"from", "export", "nomtime", "size", NULL
|
"from", "export", "nomtime", "size", NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
static int
|
||||||
* Handle updates of time from writes to mmaped regions, if allowed.
|
tmpfs_update_mtime_lazy_filter(struct vnode *vp, void *arg)
|
||||||
* Use MNT_VNODE_FOREACH_ALL instead of MNT_VNODE_FOREACH_LAZY, since
|
{
|
||||||
* unmap of the tmpfs-backed vnode does not call vinactive(), due to
|
struct vm_object *obj;
|
||||||
* vm object type is basically OBJT_SWAP. If lazy, only handle
|
|
||||||
* delayed update of mtime due to the writes to mapped files.
|
if (vp->v_type != VREG)
|
||||||
*/
|
return (0);
|
||||||
|
|
||||||
|
obj = atomic_load_ptr(&vp->v_object);
|
||||||
|
if (obj == NULL)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
return (vm_object_mightbedirty_(obj));
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
tmpfs_update_mtime(struct mount *mp, bool lazy)
|
tmpfs_update_mtime_lazy(struct mount *mp)
|
||||||
|
{
|
||||||
|
struct vnode *vp, *mvp;
|
||||||
|
|
||||||
|
MNT_VNODE_FOREACH_LAZY(vp, mp, mvp, tmpfs_update_mtime_lazy_filter, NULL) {
|
||||||
|
if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK) != 0)
|
||||||
|
continue;
|
||||||
|
tmpfs_check_mtime(vp);
|
||||||
|
vput(vp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
tmpfs_update_mtime_all(struct mount *mp)
|
||||||
{
|
{
|
||||||
struct vnode *vp, *mvp;
|
struct vnode *vp, *mvp;
|
||||||
struct vm_object *obj;
|
|
||||||
|
|
||||||
if (VFS_TO_TMPFS(mp)->tm_nomtime)
|
if (VFS_TO_TMPFS(mp)->tm_nomtime)
|
||||||
return;
|
return;
|
||||||
@ -119,28 +139,11 @@ tmpfs_update_mtime(struct mount *mp, bool lazy)
|
|||||||
VI_UNLOCK(vp);
|
VI_UNLOCK(vp);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
obj = vp->v_object;
|
if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK) != 0)
|
||||||
MPASS(obj->type == tmpfs_pager_type);
|
|
||||||
MPASS((obj->flags & OBJ_TMPFS) != 0);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* In lazy case, do unlocked read, avoid taking vnode
|
|
||||||
* lock if not needed. Lost update will be handled on
|
|
||||||
* the next call.
|
|
||||||
* For non-lazy case, we must flush all pending
|
|
||||||
* metadata changes now.
|
|
||||||
*/
|
|
||||||
if (!lazy || obj->generation != obj->cleangeneration) {
|
|
||||||
if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK) != 0)
|
|
||||||
continue;
|
|
||||||
tmpfs_check_mtime(vp);
|
|
||||||
if (!lazy)
|
|
||||||
tmpfs_update(vp);
|
|
||||||
vput(vp);
|
|
||||||
} else {
|
|
||||||
VI_UNLOCK(vp);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
tmpfs_check_mtime(vp);
|
||||||
|
tmpfs_update(vp);
|
||||||
|
vput(vp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,7 +303,7 @@ tmpfs_rw_to_ro(struct mount *mp)
|
|||||||
MNT_IUNLOCK(mp);
|
MNT_IUNLOCK(mp);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
tmpfs_all_rw_maps(mp, tmpfs_revoke_rw_maps_cb, NULL);
|
tmpfs_all_rw_maps(mp, tmpfs_revoke_rw_maps_cb, NULL);
|
||||||
tmpfs_update_mtime(mp, false);
|
tmpfs_update_mtime_all(mp);
|
||||||
error = vflush(mp, 0, flags, curthread);
|
error = vflush(mp, 0, flags, curthread);
|
||||||
if (error != 0) {
|
if (error != 0) {
|
||||||
VFS_TO_TMPFS(mp)->tm_ronly = 0;
|
VFS_TO_TMPFS(mp)->tm_ronly = 0;
|
||||||
@ -653,7 +656,7 @@ tmpfs_sync(struct mount *mp, int waitfor)
|
|||||||
mp->mnt_kern_flag |= MNTK_SUSPEND2 | MNTK_SUSPENDED;
|
mp->mnt_kern_flag |= MNTK_SUSPEND2 | MNTK_SUSPENDED;
|
||||||
MNT_IUNLOCK(mp);
|
MNT_IUNLOCK(mp);
|
||||||
} else if (waitfor == MNT_LAZY) {
|
} else if (waitfor == MNT_LAZY) {
|
||||||
tmpfs_update_mtime(mp, true);
|
tmpfs_update_mtime_lazy(mp);
|
||||||
}
|
}
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user