From owner-dev-commits-src-main@freebsd.org Sat May 15 20:55:28 2021 Return-Path: Delivered-To: dev-commits-src-main@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id 6FCD464FF85; Sat, 15 May 2021 20:55:28 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4FjHjD2gnLz3rq7; Sat, 15 May 2021 20:55:28 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 453CD42F5; Sat, 15 May 2021 20:55:28 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 14FKtSPX088288; Sat, 15 May 2021 20:55:28 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 14FKtSRD088287; Sat, 15 May 2021 20:55:28 GMT (envelope-from git) Date: Sat, 15 May 2021 20:55:28 GMT Message-Id: <202105152055.14FKtSRD088287@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Mateusz Guzik Subject: git: eec2e4ef7f96 - main - tmpfs: reimplement the mtime scan to use the lazy list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: mjg X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: eec2e4ef7f964d18fcec3dc2cdcd7530be490835 Auto-Submitted: auto-generated X-BeenThere: dev-commits-src-main@freebsd.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Commit messages for the main branch of the src repository List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 15 May 2021 20:55:28 -0000 The branch main has been updated by mjg: URL: https://cgit.FreeBSD.org/src/commit/?id=eec2e4ef7f964d18fcec3dc2cdcd7530be490835 commit eec2e4ef7f964d18fcec3dc2cdcd7530be490835 Author: Mateusz Guzik AuthorDate: 2021-05-07 14:43:43 +0000 Commit: Mateusz Guzik CommitDate: 2021-05-15 20:48:45 +0000 tmpfs: reimplement the mtime scan to use the lazy list Tested by: pho Reviewed by: kib, markj Differential Revision: https://reviews.freebsd.org/D30065 --- sys/fs/tmpfs/tmpfs.h | 1 + sys/fs/tmpfs/tmpfs_subr.c | 107 ++++++++++++++++++++++++++++++++++++++++++++ sys/fs/tmpfs/tmpfs_vfsops.c | 67 ++++++++++++++------------- 3 files changed, 143 insertions(+), 32 deletions(-) diff --git a/sys/fs/tmpfs/tmpfs.h b/sys/fs/tmpfs/tmpfs.h index bb777e29e3d0..99368d67aaaa 100644 --- a/sys/fs/tmpfs/tmpfs.h +++ b/sys/fs/tmpfs/tmpfs.h @@ -46,6 +46,7 @@ MALLOC_DECLARE(M_TMPFSNAME); #endif #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. diff --git a/sys/fs/tmpfs/tmpfs_subr.c b/sys/fs/tmpfs/tmpfs_subr.c index 67eb12598e24..8b75c58d69a2 100644 --- a/sys/fs/tmpfs/tmpfs_subr.c +++ b/sys/fs/tmpfs/tmpfs_subr.c @@ -99,6 +99,92 @@ tmpfs_pager_alloc(void *handle, vm_ooffset_t size, vm_prot_t prot, 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 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_alloc = tmpfs_pager_alloc, .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_getvp = tmpfs_pager_getvp, }; @@ -643,6 +731,7 @@ tmpfs_free_dirent(struct tmpfs_mount *tmp, struct tmpfs_dirent *de) void tmpfs_destroy_vobject(struct vnode *vp, vm_object_t obj) { + bool want_vrele; ASSERT_VOP_ELOCKED(vp, "tmpfs_destroy_vobject"); 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); 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); obj->un_pager.swp.swp_tmpfs = NULL; if (vp->v_writecount < 0) vp->v_writecount = 0; VI_UNLOCK(vp); VM_OBJECT_WUNLOCK(obj); + if (want_vrele) { + vrele(vp); + } } /* @@ -792,6 +893,12 @@ loop: case VREG: object = node->tn_reg.tn_aobj; 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); KASSERT(vp->v_object == NULL, ("Not NULL v_object in tmpfs")); vp->v_object = object; diff --git a/sys/fs/tmpfs/tmpfs_vfsops.c b/sys/fs/tmpfs/tmpfs_vfsops.c index 4f29b5dfc6f0..7dffb9027946 100644 --- a/sys/fs/tmpfs/tmpfs_vfsops.c +++ b/sys/fs/tmpfs/tmpfs_vfsops.c @@ -99,18 +99,38 @@ static const char *tmpfs_updateopts[] = { "from", "export", "nomtime", "size", NULL }; -/* - * Handle updates of time from writes to mmaped regions, if allowed. - * Use MNT_VNODE_FOREACH_ALL instead of MNT_VNODE_FOREACH_LAZY, since - * unmap of the tmpfs-backed vnode does not call vinactive(), due to - * vm object type is basically OBJT_SWAP. If lazy, only handle - * delayed update of mtime due to the writes to mapped files. - */ +static int +tmpfs_update_mtime_lazy_filter(struct vnode *vp, void *arg) +{ + struct vm_object *obj; + + 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 -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 vm_object *obj; if (VFS_TO_TMPFS(mp)->tm_nomtime) return; @@ -119,28 +139,11 @@ tmpfs_update_mtime(struct mount *mp, bool lazy) VI_UNLOCK(vp); continue; } - obj = vp->v_object; - 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); + if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK) != 0) continue; - } + tmpfs_check_mtime(vp); + tmpfs_update(vp); + vput(vp); } } @@ -300,7 +303,7 @@ tmpfs_rw_to_ro(struct mount *mp) MNT_IUNLOCK(mp); for (;;) { 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); if (error != 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; MNT_IUNLOCK(mp); } else if (waitfor == MNT_LAZY) { - tmpfs_update_mtime(mp, true); + tmpfs_update_mtime_lazy(mp); } return (0); }