Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 26 Jun 2019 02:09:23 +0000 (UTC)
From:      Alan Somers <asomers@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r349403 - in projects/fuse2: sys/fs/fuse tests/sys/fs/fusefs
Message-ID:  <201906260209.x5Q29NDk095897@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
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<unsigned> {
+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



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201906260209.x5Q29NDk095897>