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
This commit is contained in:
Alan Somers 2019-06-14 19:47:48 +00:00
parent 8eecd9ce05
commit b5aaf286ea
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/projects/fuse2/; revision=349038
2 changed files with 6 additions and 60 deletions

View File

@ -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);
}

View File

@ -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 */
}