From owner-svn-src-projects@freebsd.org Fri May 31 21:23:01 2019 Return-Path: Delivered-To: svn-src-projects@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 104EB15C614F for ; Fri, 31 May 2019 21:23:01 +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 A8D2285153; Fri, 31 May 2019 21:23:00 +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 84E7F61A4; Fri, 31 May 2019 21:23:00 +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 x4VLN0Ie082250; Fri, 31 May 2019 21:23:00 GMT (envelope-from asomers@FreeBSD.org) Received: (from asomers@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id x4VLMxWl082240; Fri, 31 May 2019 21:22:59 GMT (envelope-from asomers@FreeBSD.org) Message-Id: <201905312122.x4VLMxWl082240@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: asomers set sender to asomers@FreeBSD.org using -f From: Alan Somers Date: Fri, 31 May 2019 21:22:59 +0000 (UTC) To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r348485 - 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: 348485 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Rspamd-Queue-Id: A8D2285153 X-Spamd-Bar: -- Authentication-Results: mx1.freebsd.org X-Spamd-Result: default: False [-2.97 / 15.00]; local_wl_from(0.00)[FreeBSD.org]; NEURAL_HAM_SHORT(-0.97)[-0.968,0]; ASN(0.00)[asn:11403, ipnet:2610:1c1:1::/48, country:US]; NEURAL_HAM_MEDIUM(-1.00)[-1.000,0]; NEURAL_HAM_LONG(-1.00)[-1.000,0] X-BeenThere: svn-src-projects@freebsd.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "SVN commit messages for the src " projects" tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 31 May 2019 21:23:01 -0000 Author: asomers Date: Fri May 31 21:22:58 2019 New Revision: 348485 URL: https://svnweb.freebsd.org/changeset/base/348485 Log: fusefs: check the vnode cache when looking up files for the NFS server FUSE allows entries to be cached for a limited amount of time. fusefs's vnop_lookup method already implements that using the timeout functionality of cache_lookup/cache_enter_time. However, lookups for the NFS server go through a separate path: vfs_vget. That path can't use the same timeout functionality because cache_lookup/cache_enter_time only work on pathnames, whereas vfs_vget works by inode number. This commit adds entry timeout information to the fuse vnode structure, and checks it during vfs_vget. This allows the NFS server to take advantage of cached entries. It's also the same path that FUSE's asynchronous cache invalidation operations will use. 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.h projects/fuse2/sys/fs/fuse/fuse_vfsops.c projects/fuse2/sys/fs/fuse/fuse_vnops.c projects/fuse2/tests/sys/fs/fusefs/nfs.cc Modified: projects/fuse2/sys/fs/fuse/fuse_internal.c ============================================================================== --- projects/fuse2/sys/fs/fuse/fuse_internal.c Fri May 31 21:02:42 2019 (r348484) +++ projects/fuse2/sys/fs/fuse/fuse_internal.c Fri May 31 21:22:58 2019 (r348485) @@ -750,6 +750,7 @@ fuse_internal_vnode_disappear(struct vnode *vp) ASSERT_VOP_ELOCKED(vp, "fuse_internal_vnode_disappear"); fvdat->flag |= FN_REVOKED; bintime_clear(&fvdat->attr_cache_timeout); + bintime_clear(&fvdat->entry_cache_timeout); cache_purge(vp); } Modified: projects/fuse2/sys/fs/fuse/fuse_internal.h ============================================================================== --- projects/fuse2/sys/fs/fuse/fuse_internal.h Fri May 31 21:02:42 2019 (r348484) +++ projects/fuse2/sys/fs/fuse/fuse_internal.h Fri May 31 21:22:58 2019 (r348485) @@ -68,6 +68,9 @@ #include "fuse_ipc.h" #include "fuse_node.h" +extern u_long fuse_lookup_cache_hits; +extern u_long fuse_lookup_cache_misses; + static inline bool vfs_isrdonly(struct mount *mp) { Modified: projects/fuse2/sys/fs/fuse/fuse_node.h ============================================================================== --- projects/fuse2/sys/fs/fuse/fuse_node.h Fri May 31 21:02:42 2019 (r348484) +++ projects/fuse2/sys/fs/fuse/fuse_node.h Fri May 31 21:22:58 2019 (r348485) @@ -97,6 +97,11 @@ struct fuse_vnode_data { /** meta **/ /* The monotonic time after which the attr cache is invalid */ struct bintime attr_cache_timeout; + /* + * Monotonic time after which the entry is invalid. Used for lookups + * by nodeid instead of pathname. + */ + struct bintime entry_cache_timeout; struct vattr cached_attrs; uint64_t nlookup; enum vtype vtype; Modified: projects/fuse2/sys/fs/fuse/fuse_vfsops.c ============================================================================== --- projects/fuse2/sys/fs/fuse/fuse_vfsops.c Fri May 31 21:02:42 2019 (r348484) +++ projects/fuse2/sys/fs/fuse/fuse_vfsops.c Fri May 31 21:22:58 2019 (r348485) @@ -527,19 +527,34 @@ fuse_vfsop_vget(struct mount *mp, ino_t ino, int flags struct fuse_dispatcher fdi; struct fuse_entry_out *feo; struct fuse_vnode_data *fvdat; + struct bintime now; const char dot[] = "."; off_t filesize; enum vtype vtyp; int error; + error = vfs_hash_get(mp, fuse_vnode_hash(nodeid), flags, td, vpp, + fuse_vnode_cmp, &nodeid); + if (error) + return error; /* - * TODO Check the vnode cache, verifying entry cache timeout. Normally - * done during VOP_LOOKUP + * Check the entry cache timeout. We have to do this within fusefs + * instead of by using cache_enter_time/cache_lookup because those + * routines are only intended to work with pathnames, not inodes */ - /*error = vfs_hash_get(mp, fuse_vnode_hash(nodeid), LK_EXCLUSIVE, td, vpp,*/ - /*fuse_vnode_cmp, &nodeid);*/ - /*if (error || *vpp != NULL)*/ - /*return error;*/ + if (*vpp != NULL) { + getbinuptime(&now); + if (bintime_cmp(&(VTOFUD(*vpp)->entry_cache_timeout), &now, >)){ + atomic_add_acq_long(&fuse_lookup_cache_hits, 1); + return 0; + } else { + /* Entry cache timeout */ + atomic_add_acq_long(&fuse_lookup_cache_misses, 1); + cache_purge(*vpp); + vput(*vpp); + *vpp = NULL; + } + } /* Do a LOOKUP, using nodeid as the parent and "." as filename */ fdisp_init(&fdi, sizeof(dot)); @@ -585,6 +600,8 @@ fuse_vfsop_vget(struct mount *mp, ino_t ino, int flags fuse_internal_cache_attrs(*vpp, td->td_ucred, &feo->attr, feo->attr_valid, feo->attr_valid_nsec, NULL); + fuse_validity_2_bintime(feo->entry_valid, feo->entry_valid_nsec, + &fvdat->entry_cache_timeout); out: fdisp_destroy(&fdi); return error; Modified: projects/fuse2/sys/fs/fuse/fuse_vnops.c ============================================================================== --- projects/fuse2/sys/fs/fuse/fuse_vnops.c Fri May 31 21:02:42 2019 (r348484) +++ projects/fuse2/sys/fs/fuse/fuse_vnops.c Fri May 31 21:22:58 2019 (r348485) @@ -218,12 +218,12 @@ struct vop_vector fuse_vnops = { .vop_vptofh = fuse_vnop_vptofh, }; -static u_long fuse_lookup_cache_hits = 0; +u_long fuse_lookup_cache_hits = 0; SYSCTL_ULONG(_vfs_fusefs, OID_AUTO, lookup_cache_hits, CTLFLAG_RD, &fuse_lookup_cache_hits, 0, "number of positive cache hits in lookup"); -static u_long fuse_lookup_cache_misses = 0; +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"); @@ -965,6 +965,8 @@ fuse_vnop_lookup(struct vop_lookup_args *ap) /* Cache timeout */ atomic_add_acq_long(&fuse_lookup_cache_misses, 1); + bintime_clear( + &VTOFUD(*vpp)->entry_cache_timeout); cache_purge(*vpp); if (dvp != *vpp) vput(*vpp); @@ -1103,6 +1105,9 @@ fuse_vnop_lookup(struct vop_lookup_args *ap) MPASS(feo != NULL); fuse_internal_cache_attrs(*vpp, cred, &feo->attr, feo->attr_valid, feo->attr_valid_nsec, NULL); + fuse_validity_2_bintime(feo->entry_valid, + feo->entry_valid_nsec, + &fvdat->entry_cache_timeout); if ((nameiop == DELETE || nameiop == RENAME) && islastcn) @@ -2536,7 +2541,7 @@ fuse_vnop_print(struct vop_print_args *ap) * Get an NFS filehandle for a FUSE file. * * This will only work for FUSE file systems that guarantee the uniqueness of - * nodeid:generation, which most don't + * nodeid:generation, which most don't. */ /* vop_vptofh { Modified: projects/fuse2/tests/sys/fs/fusefs/nfs.cc ============================================================================== --- projects/fuse2/tests/sys/fs/fusefs/nfs.cc Fri May 31 21:02:42 2019 (r348484) +++ projects/fuse2/tests/sys/fs/fusefs/nfs.cc Fri May 31 21:22:58 2019 (r348485) @@ -145,11 +145,7 @@ TEST_F(Fhstat, lookup_dot) } /* Use a file handle whose entry is still cached */ -/* - * Disabled because fuse_vfsop_vget doesn't yet check the entry cache. No PR - * because that's a feature request, not a bug - */ -TEST_F(Fhstat, DISABLED_cached) +TEST_F(Fhstat, cached) { const char FULLPATH[] = "mountpoint/some_dir/."; const char RELDIRPATH[] = "some_dir"; @@ -157,7 +153,6 @@ TEST_F(Fhstat, DISABLED_cached) struct stat sb; const uint64_t ino = 42; const mode_t mode = S_IFDIR | 0755; - const uid_t uid = 12345; EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH) .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { @@ -165,15 +160,57 @@ TEST_F(Fhstat, DISABLED_cached) out.body.entry.attr.mode = mode; out.body.entry.nodeid = ino; out.body.entry.generation = 1; - out.body.entry.attr.uid = uid; + out.body.entry.attr.ino = ino; out.body.entry.attr_valid = UINT64_MAX; out.body.entry.entry_valid = UINT64_MAX; }))); ASSERT_EQ(0, getfh(FULLPATH, &fhp)) << strerror(errno); ASSERT_EQ(0, fhstat(&fhp, &sb)) << strerror(errno); - EXPECT_EQ(uid, sb.st_uid); - EXPECT_EQ(mode, sb.st_mode); + EXPECT_EQ(ino, sb.st_ino); +} + +/* File handle entries should expire from the cache, too */ +TEST_F(Fhstat, cache_expired) +{ + const char FULLPATH[] = "mountpoint/some_dir/."; + const char RELDIRPATH[] = "some_dir"; + fhandle_t fhp; + struct stat sb; + const uint64_t ino = 42; + const mode_t mode = S_IFDIR | 0755; + + EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH) + .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { + SET_OUT_HEADER_LEN(out, entry); + out.body.entry.attr.mode = mode; + out.body.entry.nodeid = ino; + out.body.entry.generation = 1; + out.body.entry.attr.ino = ino; + out.body.entry.attr_valid = UINT64_MAX; + out.body.entry.entry_valid_nsec = NAP_NS / 2; + }))); + + EXPECT_LOOKUP(ino, ".") + .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { + SET_OUT_HEADER_LEN(out, entry); + out.body.entry.attr.mode = mode; + out.body.entry.nodeid = ino; + out.body.entry.generation = 1; + out.body.entry.attr.ino = ino; + out.body.entry.attr_valid = UINT64_MAX; + out.body.entry.entry_valid = 0; + }))); + + ASSERT_EQ(0, getfh(FULLPATH, &fhp)) << strerror(errno); + ASSERT_EQ(0, fhstat(&fhp, &sb)) << strerror(errno); + EXPECT_EQ(ino, sb.st_ino); + + nap(); + + /* Cache should be expired; fuse should issue a FUSE_LOOKUP */ + ASSERT_EQ(0, fhstat(&fhp, &sb)) << strerror(errno); + EXPECT_EQ(ino, sb.st_ino); } /*