From owner-svn-src-projects@freebsd.org Wed Jun 26 02:09:25 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 8CDE815DCD64 for ; Wed, 26 Jun 2019 02:09:25 +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 30FD488273; Wed, 26 Jun 2019 02:09:25 +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 164EB20FE5; Wed, 26 Jun 2019 02:09:25 +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 x5Q29OBd095907; Wed, 26 Jun 2019 02:09:24 GMT (envelope-from asomers@FreeBSD.org) Received: (from asomers@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id x5Q29NDk095897; Wed, 26 Jun 2019 02:09:23 GMT (envelope-from asomers@FreeBSD.org) Message-Id: <201906260209.x5Q29NDk095897@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: asomers set sender to asomers@FreeBSD.org using -f From: Alan Somers Date: Wed, 26 Jun 2019 02:09:23 +0000 (UTC) To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r349403 - 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: 349403 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Rspamd-Queue-Id: 30FD488273 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_MEDIUM(-1.00)[-0.999,0]; NEURAL_HAM_LONG(-1.00)[-1.000,0]; NEURAL_HAM_SHORT(-0.97)[-0.971,0]; ASN(0.00)[asn:11403, ipnet:2610:1c1:1::/48, country:US] 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: Wed, 26 Jun 2019 02:09:25 -0000 Author: asomers Date: Wed Jun 26 02:09:22 2019 New Revision: 349403 URL: https://svnweb.freebsd.org/changeset/base/349403 Log: fusefs: implement the "time_gran" feature. If a server supports a timestamp granularity other than 1ns, it can tell the client this as of protocol 7.23. The client will use that granularity when updating its cached timestamps during write. This way the timestamps won't appear to change following flush. Sponsored by: The FreeBSD Foundation Modified: projects/fuse2/sys/fs/fuse/fuse_internal.c projects/fuse2/sys/fs/fuse/fuse_ipc.h projects/fuse2/sys/fs/fuse/fuse_node.c projects/fuse2/tests/sys/fs/fusefs/mockfs.cc projects/fuse2/tests/sys/fs/fusefs/mockfs.hh projects/fuse2/tests/sys/fs/fusefs/utils.cc projects/fuse2/tests/sys/fs/fusefs/utils.hh projects/fuse2/tests/sys/fs/fusefs/write.cc Modified: projects/fuse2/sys/fs/fuse/fuse_internal.c ============================================================================== --- projects/fuse2/sys/fs/fuse/fuse_internal.c Wed Jun 26 01:14:39 2019 (r349402) +++ projects/fuse2/sys/fs/fuse/fuse_internal.c Wed Jun 26 02:09:22 2019 (r349403) @@ -934,8 +934,8 @@ fuse_internal_init_callback(struct fuse_ticket *tick, * redundant with max_write */ /* - * max_background, congestion_threshold, and time_gran - * are not implemented + * max_background and congestion_threshold are not + * implemented */ } else { err = EINVAL; @@ -955,6 +955,12 @@ fuse_internal_init_callback(struct fuse_ticket *tick, fsess_set_notimpl(data->mp, FUSE_BMAP); fsess_set_notimpl(data->mp, FUSE_DESTROY); } + + if (fuse_libabi_geq(data, 7, 23) && fiio->time_gran >= 1 && + fiio->time_gran <= 1000000000) + data->time_gran = fiio->time_gran; + else + data->time_gran = 1; out: if (err) { Modified: projects/fuse2/sys/fs/fuse/fuse_ipc.h ============================================================================== --- projects/fuse2/sys/fs/fuse/fuse_ipc.h Wed Jun 26 01:14:39 2019 (r349402) +++ projects/fuse2/sys/fs/fuse/fuse_ipc.h Wed Jun 26 02:09:22 2019 (r349403) @@ -206,6 +206,7 @@ struct fuse_data { struct selinfo ks_rsel; int daemon_timeout; + unsigned time_gran; uint64_t notimpl; uint64_t mnt_flag; }; Modified: projects/fuse2/sys/fs/fuse/fuse_node.c ============================================================================== --- projects/fuse2/sys/fs/fuse/fuse_node.c Wed Jun 26 01:14:39 2019 (r349402) +++ projects/fuse2/sys/fs/fuse/fuse_node.c Wed Jun 26 02:09:22 2019 (r349403) @@ -455,9 +455,13 @@ void fuse_vnode_update(struct vnode *vp, int flags) { struct fuse_vnode_data *fvdat = VTOFUD(vp); + struct fuse_data *data = fuse_get_mpdata(vnode_mount(vp)); struct timespec ts; vfs_timestamp(&ts); + + if (data->time_gran > 1) + ts.tv_nsec = rounddown(ts.tv_nsec, data->time_gran); if (flags & FN_MTIMECHANGE) fvdat->cached_attrs.va_mtime = ts; Modified: projects/fuse2/tests/sys/fs/fusefs/mockfs.cc ============================================================================== --- projects/fuse2/tests/sys/fs/fusefs/mockfs.cc Wed Jun 26 01:14:39 2019 (r349402) +++ projects/fuse2/tests/sys/fs/fusefs/mockfs.cc Wed Jun 26 02:09:22 2019 (r349403) @@ -348,7 +348,7 @@ void MockFS::debug_response(const mockfs_buf_out &out) MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions, bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags, uint32_t kernel_minor_version, uint32_t max_write, bool async, - bool noclusterr) + bool noclusterr, unsigned time_gran) { struct sigaction sa; struct iovec *iov = NULL; @@ -362,6 +362,7 @@ MockFS::MockFS(int max_readahead, bool allow_other, bo m_maxwrite = max_write; m_nready = -1; m_pm = pm; + m_time_gran = time_gran; m_quit = false; if (m_pm == KQ) m_kq = kqueue(); @@ -475,6 +476,7 @@ void MockFS::init(uint32_t flags) { if (m_kernel_minor_version < 23) { SET_OUT_HEADER_LEN(*out, init_7_22); } else { + out->body.init.time_gran = m_time_gran; SET_OUT_HEADER_LEN(*out, init); } Modified: projects/fuse2/tests/sys/fs/fusefs/mockfs.hh ============================================================================== --- projects/fuse2/tests/sys/fs/fusefs/mockfs.hh Wed Jun 26 01:14:39 2019 (r349402) +++ projects/fuse2/tests/sys/fs/fusefs/mockfs.hh Wed Jun 26 02:09:22 2019 (r349403) @@ -277,6 +277,9 @@ class MockFS { /* Method the daemon should use for I/O to and from /dev/fuse */ enum poll_method m_pm; + /* Timestamp granularity in nanoseconds */ + unsigned m_time_gran; + void debug_request(const mockfs_buf_in&); void debug_response(const mockfs_buf_out&); @@ -320,7 +323,7 @@ class MockFS { bool default_permissions, bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags, uint32_t kernel_minor_version, uint32_t max_write, bool async, - bool no_clusterr); + bool no_clusterr, unsigned time_gran); virtual ~MockFS(); Modified: projects/fuse2/tests/sys/fs/fusefs/utils.cc ============================================================================== --- projects/fuse2/tests/sys/fs/fusefs/utils.cc Wed Jun 26 01:14:39 2019 (r349402) +++ projects/fuse2/tests/sys/fs/fusefs/utils.cc Wed Jun 26 02:09:22 2019 (r349403) @@ -117,7 +117,7 @@ void FuseTest::SetUp() { m_mock = new MockFS(m_maxreadahead, m_allow_other, m_default_permissions, m_push_symlinks_in, m_ro, m_pm, m_init_flags, m_kernel_minor_version, - m_maxwrite, m_async, m_noclusterr); + m_maxwrite, m_async, m_noclusterr, m_time_gran); /* * FUSE_ACCESS is called almost universally. Expecting it in * each test case would be super-annoying. Instead, set a Modified: projects/fuse2/tests/sys/fs/fusefs/utils.hh ============================================================================== --- projects/fuse2/tests/sys/fs/fusefs/utils.hh Wed Jun 26 01:14:39 2019 (r349402) +++ projects/fuse2/tests/sys/fs/fusefs/utils.hh Wed Jun 26 02:09:22 2019 (r349403) @@ -55,6 +55,7 @@ class FuseTest : public ::testing::Test { bool m_ro; bool m_async; bool m_noclusterr; + unsigned m_time_gran; MockFS *m_mock = NULL; const static uint64_t FH = 0xdeadbeef1a7ebabe; @@ -73,7 +74,8 @@ class FuseTest : public ::testing::Test { m_push_symlinks_in(false), m_ro(false), m_async(false), - m_noclusterr(false) + m_noclusterr(false), + m_time_gran(1) {} virtual void SetUp(); Modified: projects/fuse2/tests/sys/fs/fusefs/write.cc ============================================================================== --- projects/fuse2/tests/sys/fs/fusefs/write.cc Wed Jun 26 01:14:39 2019 (r349402) +++ projects/fuse2/tests/sys/fs/fusefs/write.cc Wed Jun 26 02:09:22 2019 (r349403) @@ -229,6 +229,14 @@ virtual void SetUp() { } }; +class TimeGran: public WriteBackAsync, public WithParamInterface { +public: +virtual void SetUp() { + m_time_gran = 1 << GetParam(); + WriteBackAsync::SetUp(); +} +}; + /* Tests for clustered writes with WriteBack cacheing */ class WriteCluster: public WriteBack { public: @@ -1134,6 +1142,43 @@ TEST_F(WriteBackAsync, timestamps_during_setattr) ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno); } + +/* fuse_init_out.time_gran controls the granularity of timestamps */ +TEST_P(TimeGran, timestamps_during_setattr) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS = "abcdefgh"; + ssize_t bufsize = strlen(CONTENTS); + uint64_t ino = 42; + const mode_t newmode = 0755; + int fd; + + expect_lookup(RELPATH, ino, 0); + expect_open(ino, 0, 1); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + uint32_t valid = FATTR_MODE | FATTR_MTIME | FATTR_CTIME; + return (in.header.opcode == FUSE_SETATTR && + in.header.nodeid == ino && + in.body.setattr.valid == valid && + in.body.setattr.mtimensec % m_time_gran == 0 && + in.body.setattr.ctimensec % m_time_gran == 0); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { + SET_OUT_HEADER_LEN(out, attr); + out.body.attr.attr.ino = ino; + out.body.attr.attr.mode = S_IFREG | newmode; + }))); + + fd = open(FULLPATH, O_RDWR); + EXPECT_LE(0, fd) << strerror(errno); + ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); + ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno); +} + +INSTANTIATE_TEST_CASE_P(RA, TimeGran, Range(0u, 10u)); /* * Without direct_io, writes should be committed to cache