From b5aaf286eabead9f764595b989d9f3d04e0a4847 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Fri, 14 Jun 2019 19:47:48 +0000 Subject: [PATCH] fusefs: fix the "write-through" of write-through cacheing Our fusefs(5) module supports three cache modes: uncached, write-through, and write-back. However, the write-through mode (which is the default) has never actually worked as its name suggests. Rather, it's always been more like "write-around". It wrote directly, bypassing the cache. The cache would only be populated by a subsequent read of the same data. This commit fixes that problem. Now the write-through mode works as one would expect: write(2) immediately adds data to the cache and then blocks while the daemon processes the write operation. A side effect of this change is that non-cache-block-aligned writes will now incur a read-modify-write cycle of the cache block. The old behavior (bypassing write cache entirely) can still be achieved by opening a file with O_DIRECT. PR: 237588 Sponsored by: The FreeBSD Foundation --- sys/fs/fuse/fuse_io.c | 10 ++----- tests/sys/fs/fusefs/write.cc | 56 ++---------------------------------- 2 files changed, 6 insertions(+), 60 deletions(-) diff --git a/sys/fs/fuse/fuse_io.c b/sys/fs/fuse/fuse_io.c index 06cfb2a933ba..e2a7c19d1cfa 100644 --- a/sys/fs/fuse/fuse_io.c +++ b/sys/fs/fuse/fuse_io.c @@ -219,13 +219,7 @@ fuse_io_dispatch(struct vnode *vp, struct uio *uio, int ioflag, bool pages, } break; case UIO_WRITE: - /* - * Kludge: simulate write-through caching via write-around - * caching. Same effect, as far as never caching dirty data, - * but slightly pessimal in that newly written data is not - * cached. - */ - if (directio || fuse_data_cache_mode == FUSE_CACHE_WT) { + if (directio) { const int iosize = fuse_iosize(vp); off_t start, end, filesize; @@ -250,6 +244,8 @@ fuse_io_dispatch(struct vnode *vp, struct uio *uio, int ioflag, bool pages, } else { SDT_PROBE2(fusefs, , io, trace, 1, "buffered write of vnode"); + if (fuse_data_cache_mode == FUSE_CACHE_WT) + ioflag |= IO_SYNC; err = fuse_write_biobackend(vp, uio, cred, fufh, ioflag, pid); } diff --git a/tests/sys/fs/fusefs/write.cc b/tests/sys/fs/fusefs/write.cc index 407effab7db8..983c93d38eb0 100644 --- a/tests/sys/fs/fusefs/write.cc +++ b/tests/sys/fs/fusefs/write.cc @@ -531,60 +531,13 @@ TEST_F(Write, mmap) free(zeros); } -/* In WriteThrough mode, a write should evict overlapping cached data */ -TEST_F(WriteThrough, evicts_read_cache) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - ssize_t bufsize = 65536; - /* End the write in the middle of a page */ - ssize_t wrsize = bufsize - 1000; - char *contents0, *contents1, *readbuf, *expected; - uint64_t ino = 42; - int fd; - - contents0 = (char*)malloc(bufsize); - memset(contents0, 'X', bufsize); - contents0[bufsize - 1] = '\0'; // Null-terminate - contents1 = (char*)malloc(wrsize); - memset(contents1, 'Y', wrsize); - readbuf = (char*)calloc(bufsize, 1); - expected = (char*)malloc(bufsize); - memset(expected, 'Y', wrsize); - memset(expected + wrsize, 'X', bufsize - wrsize); - expected[bufsize - 1] = '\0'; // Null-terminate - - expect_lookup(RELPATH, ino, bufsize); - expect_open(ino, 0, 1); - expect_read(ino, 0, bufsize, bufsize, contents0); - expect_write(ino, 0, wrsize, wrsize, contents1); - - fd = open(FULLPATH, O_RDWR); - EXPECT_LE(0, fd) << strerror(errno); - - // Prime cache - ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); - - // Write directly, evicting cache - ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); - ASSERT_EQ(wrsize, write(fd, contents1, wrsize)) << strerror(errno); - - // Read again. Cache should be bypassed - expect_read(ino, 0, bufsize, bufsize, expected); - ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); - ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); - ASSERT_STREQ(readbuf, expected); - - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - TEST_F(WriteThrough, pwrite) { const char FULLPATH[] = "mountpoint/some_file.txt"; const char RELPATH[] = "some_file.txt"; const char *CONTENTS = "abcdefgh"; uint64_t ino = 42; - uint64_t offset = 4096; + uint64_t offset = 65536; int fd; ssize_t bufsize = strlen(CONTENTS); @@ -901,11 +854,7 @@ TEST_F(WriteBack, o_direct) /* * Without direct_io, writes should be committed to cache */ -/* - * Disabled because we don't yet implement write-through caching. No bugzilla - * entry, because that's a feature request, not a bug. - */ -TEST_F(WriteThrough, DISABLED_writethrough) +TEST_F(WriteThrough, writethrough) { const char FULLPATH[] = "mountpoint/some_file.txt"; const char RELPATH[] = "some_file.txt"; @@ -927,6 +876,7 @@ TEST_F(WriteThrough, DISABLED_writethrough) * A subsequent read should be serviced by cache, without querying the * filesystem daemon */ + ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); /* Deliberately leak fd. close(2) will be tested in release.cc */ }