From owner-svn-src-projects@freebsd.org Tue Sep 3 14:06:29 2019 Return-Path: Delivered-To: svn-src-projects@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 78945DCA09 for ; Tue, 3 Sep 2019 14:06:29 +0000 (UTC) (envelope-from yuripv@freebsd.org) Received: from freefall.freebsd.org (freefall.freebsd.org [96.47.72.132]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "freefall.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 46N7zS28GVz4Pb7; Tue, 3 Sep 2019 14:06:28 +0000 (UTC) (envelope-from yuripv@freebsd.org) Received: by freefall.freebsd.org (Postfix, from userid 1452) id 259681A6FE; Tue, 3 Sep 2019 14:06:09 +0000 (UTC) X-Original-To: yuripv@localmail.freebsd.org Delivered-To: yuripv@localmail.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [96.47.72.80]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (Client CN "mx1.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by freefall.freebsd.org (Postfix) with ESMTPS id AE5F111610; Tue, 9 Apr 2019 00:47:43 +0000 (UTC) (envelope-from owner-src-committers@freebsd.org) Received: from freefall.freebsd.org (freefall.freebsd.org [96.47.72.132]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "freefall.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 3662B6C3C8; Tue, 9 Apr 2019 00:47:43 +0000 (UTC) (envelope-from owner-src-committers@freebsd.org) Received: by freefall.freebsd.org (Postfix, from userid 538) id 0F86A11602; Tue, 9 Apr 2019 00:47:43 +0000 (UTC) Delivered-To: src-committers@localmail.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (Client CN "mx1.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by freefall.freebsd.org (Postfix) with ESMTPS id D21A9115FF for ; Tue, 9 Apr 2019 00:47:40 +0000 (UTC) (envelope-from asomers@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) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 89C806C3C2; Tue, 9 Apr 2019 00:47:40 +0000 (UTC) (envelope-from asomers@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 5A8543447; Tue, 9 Apr 2019 00:47:40 +0000 (UTC) (envelope-from asomers@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id x390le25055453; Tue, 9 Apr 2019 00:47:40 GMT (envelope-from asomers@FreeBSD.org) Received: (from asomers@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id x390lc2W055446; Tue, 9 Apr 2019 00:47:38 GMT (envelope-from asomers@FreeBSD.org) Message-Id: <201904090047.x390lc2W055446@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: asomers set sender to asomers@FreeBSD.org using -f From: Alan Somers To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r346046 - in projects/fuse2: sys/fs/fuse tests/sys/fs/fusefs X-SVN-Group: projects X-SVN-Commit-Author: asomers X-SVN-Commit-Paths: in projects/fuse2: sys/fs/fuse tests/sys/fs/fusefs X-SVN-Commit-Revision: 346046 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Precedence: bulk X-Loop: FreeBSD.org Sender: owner-src-committers@freebsd.org X-Rspamd-Queue-Id: 3662B6C3C8 X-Spamd-Bar: -- Authentication-Results: mx1.freebsd.org X-Spamd-Result: default: False [-2.96 / 15.00]; local_wl_from(0.00)[freebsd.org]; NEURAL_HAM_MEDIUM(-1.00)[-0.996,0]; NEURAL_HAM_SHORT(-0.96)[-0.964,0]; ASN(0.00)[asn:11403, ipnet:96.47.64.0/20, country:US]; NEURAL_HAM_LONG(-1.00)[-1.000,0] Status: O X-BeenThere: svn-src-projects@freebsd.org X-Mailman-Version: 2.1.29 List-Id: "SVN commit messages for the src " projects" tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Date: Tue, 03 Sep 2019 14:06:29 -0000 X-Original-Date: Tue, 9 Apr 2019 00:47:38 +0000 (UTC) X-List-Received-Date: Tue, 03 Sep 2019 14:06:29 -0000 Author: asomers Date: Tue Apr 9 00:47:38 2019 New Revision: 346046 URL: https://svnweb.freebsd.org/changeset/base/346046 Log: fusefs: implement attribute cache timeouts The FUSE protocol allows the server to specify the timeout period for the client's attribute and entry caches. This commit implements the timeout period for the attribute cache. The entry cache's timeout period is currently disabled because it panics, and is guarded by the vfs.fusefs.lookup_cache_expire sysctl. PR: 235773 Reported by: cem Sponsored by: The FreeBSD Foundation Modified: projects/fuse2/sys/fs/fuse/fuse_internal.c projects/fuse2/sys/fs/fuse/fuse_internal.h projects/fuse2/sys/fs/fuse/fuse_node.c projects/fuse2/sys/fs/fuse/fuse_node.h projects/fuse2/sys/fs/fuse/fuse_vnops.c projects/fuse2/tests/sys/fs/fusefs/getattr.cc projects/fuse2/tests/sys/fs/fusefs/lookup.cc Modified: projects/fuse2/sys/fs/fuse/fuse_internal.c ============================================================================== --- projects/fuse2/sys/fs/fuse/fuse_internal.c Tue Apr 9 00:35:08 2019 (r346045) +++ projects/fuse2/sys/fs/fuse/fuse_internal.c Tue Apr 9 00:47:38 2019 (r346046) @@ -189,15 +189,22 @@ fuse_internal_cache_attrs(struct vnode *vp, struct fus struct mount *mp; struct fuse_vnode_data *fvdat; struct vattr *vp_cache_at; + struct timespec now, duration, timeout; mp = vnode_mount(vp); fvdat = VTOFUD(vp); - /* Honor explicit do-not-cache requests from user filesystems. */ - if (attr_valid == 0 && attr_valid_nsec == 0) - fvdat->valid_attr_cache = false; - else - fvdat->valid_attr_cache = true; + getnanouptime(&now); + /* "+ 2" is the bound of attr_valid_nsec + now.tv_nsec */ + /* Why oh why isn't there a TIME_MAX defined? */ + if (attr_valid >= INT_MAX || attr_valid + now.tv_sec + 2 >= INT_MAX) { + fvdat->attr_cache_timeout.sec = INT_MAX; + } else { + duration.tv_sec = attr_valid; + duration.tv_nsec = attr_valid_nsec; + timespecadd(&duration, &now, &timeout); + timespec2bintime(&timeout, &fvdat->attr_cache_timeout); + } vp_cache_at = VTOVA(vp); @@ -646,7 +653,7 @@ fuse_internal_vnode_disappear(struct vnode *vp) ASSERT_VOP_ELOCKED(vp, "fuse_internal_vnode_disappear"); fvdat->flag |= FN_REVOKED; - fvdat->valid_attr_cache = false; + bintime_clear(&fvdat->attr_cache_timeout); cache_purge(vp); } Modified: projects/fuse2/sys/fs/fuse/fuse_internal.h ============================================================================== --- projects/fuse2/sys/fs/fuse/fuse_internal.h Tue Apr 9 00:35:08 2019 (r346045) +++ projects/fuse2/sys/fs/fuse/fuse_internal.h Tue Apr 9 00:47:38 2019 (r346046) @@ -68,6 +68,8 @@ #include "fuse_ipc.h" #include "fuse_node.h" +extern int fuse_lookup_cache_expire; + static inline bool vfs_isrdonly(struct mount *mp) { Modified: projects/fuse2/sys/fs/fuse/fuse_node.c ============================================================================== --- projects/fuse2/sys/fs/fuse/fuse_node.c Tue Apr 9 00:35:08 2019 (r346045) +++ projects/fuse2/sys/fs/fuse/fuse_node.c Tue Apr 9 00:47:38 2019 (r346046) @@ -293,7 +293,22 @@ fuse_vnode_get(struct mount *mp, (feo->entry_valid != 0 || feo->entry_valid_nsec != 0)) { ASSERT_VOP_LOCKED(*vpp, "fuse_vnode_get"); ASSERT_VOP_LOCKED(dvp, "fuse_vnode_get"); - cache_enter(dvp, *vpp, cnp); + if (fuse_lookup_cache_expire) { + struct timespec duration, now, timeout; + + getnanouptime(&now); + if (feo->entry_valid >= INT_MAX || + feo->entry_valid + now.tv_sec + 2 >= INT_MAX) { + timeout.tv_sec = INT_MAX; + } else { + duration.tv_sec = feo->entry_valid; + duration.tv_nsec = feo->entry_valid_nsec; + timespecadd(&duration, &now, &timeout); + } + cache_enter_time(dvp, *vpp, cnp, &timeout, NULL); + } else { + cache_enter(dvp, *vpp, cnp); + } } /* Modified: projects/fuse2/sys/fs/fuse/fuse_node.h ============================================================================== --- projects/fuse2/sys/fs/fuse/fuse_node.h Tue Apr 9 00:35:08 2019 (r346045) +++ projects/fuse2/sys/fs/fuse/fuse_node.h Tue Apr 9 00:47:38 2019 (r346046) @@ -87,7 +87,8 @@ struct fuse_vnode_data { uint32_t flag; /** meta **/ - bool valid_attr_cache; + /* The monotonic time after which the attr cache is invalid */ + struct bintime attr_cache_timeout; struct vattr cached_attrs; off_t filesize; uint64_t nlookup; @@ -97,9 +98,18 @@ struct fuse_vnode_data { #define VTOFUD(vp) \ ((struct fuse_vnode_data *)((vp)->v_data)) #define VTOI(vp) (VTOFUD(vp)->nid) -#define VTOVA(vp) \ - (VTOFUD(vp)->valid_attr_cache ? \ - &(VTOFUD(vp)->cached_attrs) : NULL) +static inline struct vattr* +VTOVA(struct vnode *vp) +{ + struct bintime now; + + getbinuptime(&now); + if (bintime_cmp(&(VTOFUD(vp)->attr_cache_timeout), &now, >)) + return &(VTOFUD(vp)->cached_attrs); + else + return NULL; +} + #define VTOILLU(vp) ((uint64_t)(VTOFUD(vp) ? VTOI(vp) : 0)) #define FUSE_NULL_ID 0 Modified: projects/fuse2/sys/fs/fuse/fuse_vnops.c ============================================================================== --- projects/fuse2/sys/fs/fuse/fuse_vnops.c Tue Apr 9 00:35:08 2019 (r346045) +++ projects/fuse2/sys/fs/fuse/fuse_vnops.c Tue Apr 9 00:47:38 2019 (r346046) @@ -195,10 +195,11 @@ static u_long fuse_lookup_cache_misses = 0; SYSCTL_ULONG(_vfs_fusefs, OID_AUTO, lookup_cache_misses, CTLFLAG_RD, &fuse_lookup_cache_misses, 0, "number of cache misses in lookup"); -int fuse_lookup_cache_enable = 1; +int fuse_lookup_cache_expire = 0; -SYSCTL_INT(_vfs_fusefs, OID_AUTO, lookup_cache_enable, CTLFLAG_RW, - &fuse_lookup_cache_enable, 0, "if non-zero, enable lookup cache"); +SYSCTL_INT(_vfs_fusefs, OID_AUTO, lookup_cache_expire, CTLFLAG_RW, + &fuse_lookup_cache_expire, 0, + "if non-zero, expire fuse lookup cache entries at the proper time"); /* * XXX: This feature is highly experimental and can bring to instabilities, @@ -749,7 +750,43 @@ fuse_vnop_lookup(struct vop_lookup_args *ap) fdisp_init(&fdi, 0); op = FUSE_GETATTR; goto calldaemon; - } else if (fuse_lookup_cache_enable) { + } else if (fuse_lookup_cache_expire) { + struct timespec now, timeout; + + err = cache_lookup(dvp, vpp, cnp, &timeout, NULL); + switch (err) { + + case -1: /* positive match */ + getnanouptime(&now); + if (timespeccmp(&timeout, &now, <=)) { + atomic_add_acq_long(&fuse_lookup_cache_hits, 1); + } else { + /* Cache timeout */ + atomic_add_acq_long(&fuse_lookup_cache_misses, + 1); + cache_purge(*vpp); + break; + } + return 0; + + case 0: /* no match in cache */ + atomic_add_acq_long(&fuse_lookup_cache_misses, 1); + break; + + case ENOENT: /* negative match */ + getnanouptime(&now); + if (timespeccmp(&timeout, &now, >)) { + /* Cache timeout */ + printf("Purging vnode %p name=%s\n", *vpp, + cnp->cn_nameptr); + fuse_internal_vnode_disappear(*vpp); + break; + } + /* fall through */ + default: + return err; + } + } else { err = cache_lookup(dvp, vpp, cnp, NULL, NULL); switch (err) { Modified: projects/fuse2/tests/sys/fs/fusefs/getattr.cc ============================================================================== --- projects/fuse2/tests/sys/fs/fusefs/getattr.cc Tue Apr 9 00:35:08 2019 (r346045) +++ projects/fuse2/tests/sys/fs/fusefs/getattr.cc Tue Apr 9 00:47:38 2019 (r346046) @@ -94,8 +94,7 @@ TEST_F(Getattr, attr_cache) * discard the cached attributes and requery the daemon after the timeout * period passes. */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=235773 */ -TEST_F(Getattr, DISABLED_attr_cache_timeout) +TEST_F(Getattr, attr_cache_timeout) { const char FULLPATH[] = "mountpoint/some_file.txt"; const char RELPATH[] = "some_file.txt"; @@ -107,7 +106,7 @@ TEST_F(Getattr, DISABLED_attr_cache_timeout) */ long timeout_ns = 250'000'000; - expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 2, 0, 0); + expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1, 0, 0); EXPECT_CALL(*m_mock, process( ResultOf([](auto in) { return (in->header.opcode == FUSE_GETATTR && @@ -118,13 +117,14 @@ TEST_F(Getattr, DISABLED_attr_cache_timeout) .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto out) { SET_OUT_HEADER_LEN(out, attr); out->body.attr.attr_valid_nsec = timeout_ns; - out->body.attr.attr_valid = UINT64_MAX; + out->body.attr.attr_valid = 0; out->body.attr.attr.ino = ino; // Must match nodeid out->body.attr.attr.mode = S_IFREG | 0644; }))); + EXPECT_EQ(0, stat(FULLPATH, &sb)); usleep(2 * timeout_ns / 1000); - /* Timeout has expire. stat(2) should requery the daemon */ + /* Timeout has expired. stat(2) should requery the daemon */ EXPECT_EQ(0, stat(FULLPATH, &sb)); } Modified: projects/fuse2/tests/sys/fs/fusefs/lookup.cc ============================================================================== --- projects/fuse2/tests/sys/fs/fusefs/lookup.cc Tue Apr 9 00:35:08 2019 (r346045) +++ projects/fuse2/tests/sys/fs/fusefs/lookup.cc Tue Apr 9 00:47:38 2019 (r346046) @@ -104,8 +104,7 @@ TEST_F(Lookup, attr_cache) * If lookup returns a finite but non-zero cache timeout, then we should discard * the cached attributes and requery the daemon. */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=235773 */ -TEST_F(Lookup, DISABLED_attr_cache_timeout) +TEST_F(Lookup, attr_cache_timeout) { const char FULLPATH[] = "mountpoint/some_file.txt"; const char RELPATH[] = "some_file.txt"; @@ -118,6 +117,7 @@ TEST_F(Lookup, DISABLED_attr_cache_timeout) long timeout_ns = 250'000'000; EXPECT_LOOKUP(1, RELPATH) + .Times(2) .WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto out) { SET_OUT_HEADER_LEN(out, entry); out->body.entry.nodeid = ino; @@ -125,10 +125,10 @@ TEST_F(Lookup, DISABLED_attr_cache_timeout) out->body.entry.attr.ino = ino; // Must match nodeid out->body.entry.attr.mode = S_IFREG | 0644; }))); - expect_getattr(ino, 0); - /* access(2) will issue a VOP_LOOKUP but not a VOP_GETATTR */ + /* access(2) will issue a VOP_LOOKUP and fill the attr cache */ ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno); + /* Next access(2) will use the cached attributes */ usleep(2 * timeout_ns / 1000); /* The cache has timed out; VOP_GETATTR should query the daemon*/ ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno); @@ -222,16 +222,21 @@ TEST_F(Lookup, DISABLED_entry_cache_timeout) */ long timeout_ns = 250'000'000; - EXPECT_LOOKUP(1, RELPATH).Times(2) + EXPECT_LOOKUP(1, RELPATH) + .Times(2) .WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto out) { SET_OUT_HEADER_LEN(out, entry); out->body.entry.entry_valid_nsec = timeout_ns; out->body.entry.attr.mode = S_IFREG | 0644; out->body.entry.nodeid = 14; }))); + + /* access(2) will issue a VOP_LOOKUP and fill the entry cache */ ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno); + /* Next access(2) will use the cached entry */ + ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno); usleep(2 * timeout_ns / 1000); - /* The cache has timed out; VOP_LOOKUP should query the daemon*/ + /* The cache has timed out; VOP_LOOKUP should requery the daemon*/ ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno); }