From owner-freebsd-stable@FreeBSD.ORG Fri Jan 23 10:29:42 2015 Return-Path: Delivered-To: freebsd-stable@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id AC5DB5FC for ; Fri, 23 Jan 2015 10:29:42 +0000 (UTC) Received: from kib.kiev.ua (kib.kiev.ua [IPv6:2001:470:d5e7:1::1]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 4B909E13 for ; Fri, 23 Jan 2015 10:29:42 +0000 (UTC) Received: from tom.home (kostik@localhost [127.0.0.1]) by kib.kiev.ua (8.14.9/8.14.9) with ESMTP id t0NATaAc016641 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Fri, 23 Jan 2015 12:29:36 +0200 (EET) (envelope-from kostikbel@gmail.com) DKIM-Filter: OpenDKIM Filter v2.9.2 kib.kiev.ua t0NATaAc016641 Received: (from kostik@localhost) by tom.home (8.14.9/8.14.9/Submit) id t0NATaoZ016640; Fri, 23 Jan 2015 12:29:36 +0200 (EET) (envelope-from kostikbel@gmail.com) X-Authentication-Warning: tom.home: kostik set sender to kostikbel@gmail.com using -f Date: Fri, 23 Jan 2015 12:29:36 +0200 From: Konstantin Belousov To: Ronald Klop Subject: Re: mmap on tmpfs not updating mtime Message-ID: <20150123102936.GF42409@kib.kiev.ua> References: <20150123084645.GE42409@kib.kiev.ua> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20150123084645.GE42409@kib.kiev.ua> User-Agent: Mutt/1.5.23 (2014-03-12) X-Spam-Status: No, score=-2.0 required=5.0 tests=ALL_TRUSTED,BAYES_00, DKIM_ADSP_CUSTOM_MED,FREEMAIL_FROM,NML_ADSP_CUSTOM_MED autolearn=no autolearn_force=no version=3.4.0 X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on tom.home Cc: freebsd-stable@freebsd.org X-BeenThere: freebsd-stable@freebsd.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: Production branch of FreeBSD source code List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 23 Jan 2015 10:29:42 -0000 On Fri, Jan 23, 2015 at 10:46:45AM +0200, Konstantin Belousov wrote: > The detection of modifications could be done, e.g. by utilizing syncer > to make a pass over all active vnodes and converting OBJ_MIGHTBEDIRTY > flag for the backing vm object into mtime update. Of course, VM must be > modified to also set the flag or its analog (OBJT_MIGHTBEDIRTYTMPFS ?) > for tmpfs vm objects. This is what I mean above as 'relatively hard'. > > I will review (and commit) the patch along these lines. Ok, it is even more complicated due to the interaction with the fast path in the fault handler, which ignored calls to vm_object_set_dirty() for tmpfs vnodes. Below is the prototype. It is on par with e.g. UFS, which only update mtime for writes through mapped regions when syncer finds such dirty pages. diff --git a/sys/fs/tmpfs/tmpfs.h b/sys/fs/tmpfs/tmpfs.h index 445bf61..b077489 100644 --- a/sys/fs/tmpfs/tmpfs.h +++ b/sys/fs/tmpfs/tmpfs.h @@ -398,6 +398,7 @@ int tmpfs_alloc_vp(struct mount *, struct tmpfs_node *, int, void tmpfs_free_vp(struct vnode *); int tmpfs_alloc_file(struct vnode *, struct vnode **, struct vattr *, struct componentname *, char *); +void tmpfs_check_mtime(struct vnode *); void tmpfs_dir_attach(struct vnode *, struct tmpfs_dirent *); void tmpfs_dir_detach(struct vnode *, struct tmpfs_dirent *); void tmpfs_dir_destroy(struct tmpfs_mount *, struct tmpfs_node *); diff --git a/sys/fs/tmpfs/tmpfs_subr.c b/sys/fs/tmpfs/tmpfs_subr.c index d0d9a15..c1930f1 100644 --- a/sys/fs/tmpfs/tmpfs_subr.c +++ b/sys/fs/tmpfs/tmpfs_subr.c @@ -1415,6 +1415,31 @@ retry: return (0); } +void +tmpfs_check_mtime(struct vnode *vp) +{ + struct tmpfs_node *node; + struct vm_object *obj; + + ASSERT_VOP_ELOCKED(vp, "check_mtime"); + if (vp->v_type != VREG) + return; + node = VP_TO_TMPFS_NODE(vp); + obj = vp->v_object; + KASSERT((obj->flags & (OBJ_TMPFS_NODE | OBJ_TMPFS)) == + (OBJ_TMPFS_NODE | OBJ_TMPFS), ("non-tmpfs obj")); + /* unlocked read */ + if ((obj->flags & OBJ_TMPFS_DIRTY) != 0) { + VM_OBJECT_WLOCK(obj); + if ((obj->flags & OBJ_TMPFS_DIRTY) != 0) { + obj->flags &= ~OBJ_TMPFS_DIRTY; + node = VP_TO_TMPFS_NODE(vp); + node->tn_status |= TMPFS_NODE_MODIFIED; + } + VM_OBJECT_WUNLOCK(obj); + } +} + /* * Change flags of the given vnode. * Caller should execute tmpfs_update on vp after a successful execution. diff --git a/sys/fs/tmpfs/tmpfs_vfsops.c b/sys/fs/tmpfs/tmpfs_vfsops.c index f389f1c..2942e5a 100644 --- a/sys/fs/tmpfs/tmpfs_vfsops.c +++ b/sys/fs/tmpfs/tmpfs_vfsops.c @@ -33,10 +33,10 @@ /* * Efficient memory file system. * - * tmpfs is a file system that uses NetBSD's virtual memory sub-system - * (the well-known UVM) to store file data and metadata in an efficient - * way. This means that it does not follow the structure of an on-disk - * file system because it simply does not need to. Instead, it uses + * tmpfs is a file system that uses FreeBSD's virtual memory + * sub-system to store file data and metadata in an efficient way. + * This means that it does not follow the structure of an on-disk file + * system because it simply does not need to. Instead, it uses * memory-specific data structures and algorithms to automatically * allocate and release resources. */ @@ -50,6 +50,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -418,11 +419,45 @@ tmpfs_statfs(struct mount *mp, struct statfs *sbp) static int tmpfs_sync(struct mount *mp, int waitfor) { + struct vnode *vp, *mvp; + struct vm_object *obj; if (waitfor == MNT_SUSPEND) { MNT_ILOCK(mp); mp->mnt_kern_flag |= MNTK_SUSPEND2 | MNTK_SUSPENDED; MNT_IUNLOCK(mp); + } else if (waitfor == MNT_LAZY) { + /* + * Handle lazy updates of mtime from writes to mmaped + * regions. Use MNT_VNODE_FOREACH_ALL instead of + * MNT_VNODE_FOREACH_ACTIVE, since unmap of the + * tmpfs-backed vnode does not call vinactive(), due + * to vm object type is OBJT_SWAP. + */ + MNT_VNODE_FOREACH_ALL(vp, mp, mvp) { + if (vp->v_type != VREG) { + VI_UNLOCK(vp); + continue; + } + obj = vp->v_object; + KASSERT((obj->flags & (OBJ_TMPFS_NODE | OBJ_TMPFS)) == + (OBJ_TMPFS_NODE | OBJ_TMPFS), ("non-tmpfs obj")); + + /* + * Unlocked read, avoid taking vnode lock if + * not needed. Lost update will be handled on + * the next call. + */ + if ((obj->flags & OBJ_TMPFS_DIRTY) == 0) { + VI_UNLOCK(vp); + continue; + } + if (vget(vp, LK_EXCLUSIVE | LK_RETRY | LK_INTERLOCK, + curthread) != 0) + continue; + tmpfs_check_mtime(vp); + vput(vp); + } } return (0); } diff --git a/sys/fs/tmpfs/tmpfs_vnops.c b/sys/fs/tmpfs/tmpfs_vnops.c index c811b9a..65c5f82 100644 --- a/sys/fs/tmpfs/tmpfs_vnops.c +++ b/sys/fs/tmpfs/tmpfs_vnops.c @@ -505,6 +505,7 @@ tmpfs_fsync(struct vop_fsync_args *v) MPASS(VOP_ISLOCKED(vp)); + tmpfs_check_mtime(vp); tmpfs_update(vp); return 0; @@ -1222,16 +1223,16 @@ tmpfs_readlink(struct vop_readlink_args *v) static int tmpfs_inactive(struct vop_inactive_args *v) { - struct vnode *vp = v->a_vp; - + struct vnode *vp; struct tmpfs_node *node; + vp = v->a_vp; node = VP_TO_TMPFS_NODE(vp); - if (node->tn_links == 0) vrecycle(vp); - - return 0; + else + tmpfs_check_mtime(vp); + return (0); } int diff --git a/sys/vm/vm_fault.c b/sys/vm/vm_fault.c index e62410b..71c6d84 100644 --- a/sys/vm/vm_fault.c +++ b/sys/vm/vm_fault.c @@ -358,11 +358,13 @@ RetryFault:; (fault_flags & (VM_FAULT_CHANGE_WIRING | VM_FAULT_DIRTY)) == 0 && /* avoid calling vm_object_set_writeable_dirty() */ ((prot & VM_PROT_WRITE) == 0 || - fs.first_object->type != OBJT_VNODE || + (fs.first_object->type != OBJT_VNODE && + (fs.first_object->flags & OBJ_TMPFS_NODE) == 0) || (fs.first_object->flags & OBJ_MIGHTBEDIRTY) != 0)) { VM_OBJECT_RLOCK(fs.first_object); if ((prot & VM_PROT_WRITE) != 0 && - fs.first_object->type == OBJT_VNODE && + (fs.first_object->type == OBJT_VNODE || + (fs.first_object->flags & OBJ_TMPFS_NODE) != 0) && (fs.first_object->flags & OBJ_MIGHTBEDIRTY) == 0) goto fast_failed; m = vm_page_lookup(fs.first_object, fs.first_pindex); diff --git a/sys/vm/vm_object.c b/sys/vm/vm_object.c index 21c15dc..63127c0 100644 --- a/sys/vm/vm_object.c +++ b/sys/vm/vm_object.c @@ -2199,8 +2199,13 @@ vm_object_set_writeable_dirty(vm_object_t object) { VM_OBJECT_ASSERT_WLOCKED(object); - if (object->type != OBJT_VNODE) + if (object->type != OBJT_VNODE) { + if ((object->flags & OBJ_TMPFS_NODE) != 0) { + KASSERT(object->type == OBJT_SWAP, ("non-swap tmpfs")); + vm_object_set_flag(object, OBJ_TMPFS_DIRTY); + } return; + } object->generation++; if ((object->flags & OBJ_MIGHTBEDIRTY) != 0) return; diff --git a/sys/vm/vm_object.h b/sys/vm/vm_object.h index ab3c7d3..d80b1d8 100644 --- a/sys/vm/vm_object.h +++ b/sys/vm/vm_object.h @@ -187,6 +187,7 @@ struct vm_object { #define OBJ_PIPWNT 0x0040 /* paging in progress wanted */ #define OBJ_MIGHTBEDIRTY 0x0100 /* object might be dirty, only for vnode */ #define OBJ_TMPFS_NODE 0x0200 /* object belongs to tmpfs VREG node */ +#define OBJ_TMPFS_DIRTY 0x0400 /* dirty tmpfs obj */ #define OBJ_COLORED 0x1000 /* pg_color is defined */ #define OBJ_ONEMAPPING 0x2000 /* One USE (a single, non-forked) mapping flag */ #define OBJ_DISCONNECTWNT 0x4000 /* disconnect from vnode wanted */