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:
parent
8eecd9ce05
commit
b5aaf286ea
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/projects/fuse2/; revision=349038
@ -219,13 +219,7 @@ fuse_io_dispatch(struct vnode *vp, struct uio *uio, int ioflag, bool pages,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case UIO_WRITE:
|
case UIO_WRITE:
|
||||||
/*
|
if (directio) {
|
||||||
* 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) {
|
|
||||||
const int iosize = fuse_iosize(vp);
|
const int iosize = fuse_iosize(vp);
|
||||||
off_t start, end, filesize;
|
off_t start, end, filesize;
|
||||||
|
|
||||||
@ -250,6 +244,8 @@ fuse_io_dispatch(struct vnode *vp, struct uio *uio, int ioflag, bool pages,
|
|||||||
} else {
|
} else {
|
||||||
SDT_PROBE2(fusefs, , io, trace, 1,
|
SDT_PROBE2(fusefs, , io, trace, 1,
|
||||||
"buffered write of vnode");
|
"buffered write of vnode");
|
||||||
|
if (fuse_data_cache_mode == FUSE_CACHE_WT)
|
||||||
|
ioflag |= IO_SYNC;
|
||||||
err = fuse_write_biobackend(vp, uio, cred, fufh, ioflag,
|
err = fuse_write_biobackend(vp, uio, cred, fufh, ioflag,
|
||||||
pid);
|
pid);
|
||||||
}
|
}
|
||||||
|
@ -531,60 +531,13 @@ TEST_F(Write, mmap)
|
|||||||
free(zeros);
|
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)
|
TEST_F(WriteThrough, pwrite)
|
||||||
{
|
{
|
||||||
const char FULLPATH[] = "mountpoint/some_file.txt";
|
const char FULLPATH[] = "mountpoint/some_file.txt";
|
||||||
const char RELPATH[] = "some_file.txt";
|
const char RELPATH[] = "some_file.txt";
|
||||||
const char *CONTENTS = "abcdefgh";
|
const char *CONTENTS = "abcdefgh";
|
||||||
uint64_t ino = 42;
|
uint64_t ino = 42;
|
||||||
uint64_t offset = 4096;
|
uint64_t offset = 65536;
|
||||||
int fd;
|
int fd;
|
||||||
ssize_t bufsize = strlen(CONTENTS);
|
ssize_t bufsize = strlen(CONTENTS);
|
||||||
|
|
||||||
@ -901,11 +854,7 @@ TEST_F(WriteBack, o_direct)
|
|||||||
/*
|
/*
|
||||||
* Without direct_io, writes should be committed to cache
|
* Without direct_io, writes should be committed to cache
|
||||||
*/
|
*/
|
||||||
/*
|
TEST_F(WriteThrough, writethrough)
|
||||||
* 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)
|
|
||||||
{
|
{
|
||||||
const char FULLPATH[] = "mountpoint/some_file.txt";
|
const char FULLPATH[] = "mountpoint/some_file.txt";
|
||||||
const char RELPATH[] = "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
|
* A subsequent read should be serviced by cache, without querying the
|
||||||
* filesystem daemon
|
* filesystem daemon
|
||||||
*/
|
*/
|
||||||
|
ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
|
||||||
ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
|
ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
|
||||||
/* Deliberately leak fd. close(2) will be tested in release.cc */
|
/* Deliberately leak fd. close(2) will be tested in release.cc */
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user