fusefs: use cluster_read for more readahead
fusefs will now use cluster_read. This allows readahead of more than one cache block. However, it won't yet actually cluster the reads because that requires VOP_BMAP, which fusefs does not yet implement. Sponsored by: The FreeBSD Foundation
This commit is contained in:
parent
6fa772a88e
commit
402b609c80
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/projects/fuse2/; revision=349158
@ -317,7 +317,13 @@ fuse_read_biobackend(struct vnode *vp, struct uio *uio, int ioflag,
|
|||||||
if (bcount < biosize) {
|
if (bcount < biosize) {
|
||||||
/* If near EOF, don't do readahead */
|
/* If near EOF, don't do readahead */
|
||||||
err = bread(vp, lbn, bcount, NOCRED, &bp);
|
err = bread(vp, lbn, bcount, NOCRED, &bp);
|
||||||
/* TODO: clustered read */
|
} else if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERR) == 0) {
|
||||||
|
/* Try clustered read */
|
||||||
|
long totread = uio->uio_resid + on;
|
||||||
|
seqcount = MIN(seqcount,
|
||||||
|
data->max_readahead / biosize + 1);
|
||||||
|
err = cluster_read(vp, filesize, lbn, bcount, NOCRED,
|
||||||
|
totread, seqcount, 0, &bp);
|
||||||
} else if (seqcount > 1 && data->max_readahead >= nextsize) {
|
} else if (seqcount > 1 && data->max_readahead >= nextsize) {
|
||||||
/* Try non-clustered readahead */
|
/* Try non-clustered readahead */
|
||||||
err = breadn(vp, lbn, bcount, &nextlbn, &nextsize, 1,
|
err = breadn(vp, lbn, bcount, &nextlbn, &nextsize, 1,
|
||||||
|
@ -336,7 +336,8 @@ void MockFS::debug_response(const mockfs_buf_out &out) {
|
|||||||
|
|
||||||
MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions,
|
MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions,
|
||||||
bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags,
|
bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags,
|
||||||
uint32_t kernel_minor_version, uint32_t max_write, bool async)
|
uint32_t kernel_minor_version, uint32_t max_write, bool async,
|
||||||
|
bool noclusterr)
|
||||||
{
|
{
|
||||||
struct sigaction sa;
|
struct sigaction sa;
|
||||||
struct iovec *iov = NULL;
|
struct iovec *iov = NULL;
|
||||||
@ -409,6 +410,10 @@ MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions,
|
|||||||
build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval),
|
build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval),
|
||||||
sizeof(bool));
|
sizeof(bool));
|
||||||
}
|
}
|
||||||
|
if (noclusterr) {
|
||||||
|
build_iovec(&iov, &iovlen, "noclusterr",
|
||||||
|
__DECONST(void*, &trueval), sizeof(bool));
|
||||||
|
}
|
||||||
if (nmount(iov, iovlen, 0))
|
if (nmount(iov, iovlen, 0))
|
||||||
throw(std::system_error(errno, std::system_category(),
|
throw(std::system_error(errno, std::system_category(),
|
||||||
"Couldn't mount filesystem"));
|
"Couldn't mount filesystem"));
|
||||||
|
@ -303,7 +303,8 @@ class MockFS {
|
|||||||
MockFS(int max_readahead, bool allow_other,
|
MockFS(int max_readahead, bool allow_other,
|
||||||
bool default_permissions, bool push_symlinks_in, bool ro,
|
bool default_permissions, bool push_symlinks_in, bool ro,
|
||||||
enum poll_method pm, uint32_t flags,
|
enum poll_method pm, uint32_t flags,
|
||||||
uint32_t kernel_minor_version, uint32_t max_write, bool async);
|
uint32_t kernel_minor_version, uint32_t max_write, bool async,
|
||||||
|
bool no_clusterr);
|
||||||
|
|
||||||
virtual ~MockFS();
|
virtual ~MockFS();
|
||||||
|
|
||||||
|
@ -109,9 +109,12 @@ virtual void SetUp() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class ReadAhead: public ReadCacheable, public WithParamInterface<uint32_t> {
|
class ReadAhead: public ReadCacheable,
|
||||||
|
public WithParamInterface<tuple<bool, uint32_t>>
|
||||||
|
{
|
||||||
virtual void SetUp() {
|
virtual void SetUp() {
|
||||||
m_maxreadahead = GetParam();
|
m_maxreadahead = get<1>(GetParam());
|
||||||
|
m_noclusterr = get<0>(GetParam());
|
||||||
ReadCacheable::SetUp();
|
ReadCacheable::SetUp();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -746,6 +749,48 @@ TEST_F(ReadCacheable, DISABLED_sendfile_eio)
|
|||||||
/* Deliberately leak fd. close(2) will be tested in release.cc */
|
/* Deliberately leak fd. close(2) will be tested in release.cc */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Large reads should be clustered, even across cache block boundaries */
|
||||||
|
/*
|
||||||
|
* Disabled because clustered reads requires VOP_BMAP, which fusefs does not
|
||||||
|
* yet support
|
||||||
|
*/
|
||||||
|
TEST_P(ReadAhead, DISABLED_cluster) {
|
||||||
|
const char FULLPATH[] = "mountpoint/some_file.txt";
|
||||||
|
const char RELPATH[] = "some_file.txt";
|
||||||
|
uint64_t ino = 42;
|
||||||
|
int fd, maxcontig;
|
||||||
|
ssize_t bufsize = 4 * m_maxbcachebuf;
|
||||||
|
ssize_t filesize = bufsize;
|
||||||
|
uint64_t len;
|
||||||
|
char *rbuf, *contents;
|
||||||
|
off_t offs;
|
||||||
|
|
||||||
|
contents = (char*)malloc(filesize);
|
||||||
|
ASSERT_NE(NULL, contents);
|
||||||
|
memset(contents, 'X', filesize);
|
||||||
|
rbuf = (char*)calloc(1, bufsize);
|
||||||
|
|
||||||
|
expect_lookup(RELPATH, ino, filesize);
|
||||||
|
expect_open(ino, 0, 1);
|
||||||
|
maxcontig = m_noclusterr ? m_maxbcachebuf :
|
||||||
|
m_maxbcachebuf + (int)get<1>(GetParam());
|
||||||
|
for (offs = 0; offs < bufsize; offs += maxcontig) {
|
||||||
|
len = std::min((size_t)maxcontig, (size_t)(filesize - offs));
|
||||||
|
expect_read(ino, offs, len, len, contents + offs);
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = open(FULLPATH, O_RDONLY);
|
||||||
|
ASSERT_LE(0, fd) << strerror(errno);
|
||||||
|
|
||||||
|
/* Set the internal readahead counter to a "large" value */
|
||||||
|
ASSERT_EQ(0, fcntl(fd, F_READAHEAD, 1'000'000'000)) << strerror(errno);
|
||||||
|
|
||||||
|
ASSERT_EQ(bufsize, read(fd, rbuf, bufsize)) << strerror(errno);
|
||||||
|
ASSERT_EQ(0, memcmp(rbuf, contents, bufsize));
|
||||||
|
|
||||||
|
/* Deliberately leak fd. close(2) will be tested in release.cc */
|
||||||
|
}
|
||||||
|
|
||||||
/* fuse(4) should honor the filesystem's requested m_readahead parameter */
|
/* fuse(4) should honor the filesystem's requested m_readahead parameter */
|
||||||
TEST_P(ReadAhead, readahead) {
|
TEST_P(ReadAhead, readahead) {
|
||||||
const char FULLPATH[] = "mountpoint/some_file.txt";
|
const char FULLPATH[] = "mountpoint/some_file.txt";
|
||||||
@ -753,7 +798,7 @@ TEST_P(ReadAhead, readahead) {
|
|||||||
uint64_t ino = 42;
|
uint64_t ino = 42;
|
||||||
int fd, i;
|
int fd, i;
|
||||||
ssize_t bufsize = m_maxbcachebuf;
|
ssize_t bufsize = m_maxbcachebuf;
|
||||||
ssize_t filesize = m_maxbcachebuf * 4;
|
ssize_t filesize = m_maxbcachebuf * 6;
|
||||||
char *rbuf, *contents;
|
char *rbuf, *contents;
|
||||||
|
|
||||||
contents = (char*)malloc(filesize);
|
contents = (char*)malloc(filesize);
|
||||||
@ -765,7 +810,7 @@ TEST_P(ReadAhead, readahead) {
|
|||||||
expect_open(ino, 0, 1);
|
expect_open(ino, 0, 1);
|
||||||
/* fuse(4) should only read ahead the allowed amount */
|
/* fuse(4) should only read ahead the allowed amount */
|
||||||
expect_read(ino, 0, m_maxbcachebuf, m_maxbcachebuf, contents);
|
expect_read(ino, 0, m_maxbcachebuf, m_maxbcachebuf, contents);
|
||||||
for (i = 0; i < (int)GetParam() / m_maxbcachebuf; i++) {
|
for (i = 0; i < (int)get<1>(GetParam()) / m_maxbcachebuf; i++) {
|
||||||
off_t offs = (i + 1) * m_maxbcachebuf;
|
off_t offs = (i + 1) * m_maxbcachebuf;
|
||||||
expect_read(ino, offs, m_maxbcachebuf, m_maxbcachebuf,
|
expect_read(ino, offs, m_maxbcachebuf, m_maxbcachebuf,
|
||||||
contents + offs);
|
contents + offs);
|
||||||
@ -783,4 +828,10 @@ TEST_P(ReadAhead, readahead) {
|
|||||||
/* Deliberately leak fd. close(2) will be tested in release.cc */
|
/* Deliberately leak fd. close(2) will be tested in release.cc */
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(RA, ReadAhead, ::testing::Values(0u, 65536));
|
INSTANTIATE_TEST_CASE_P(RA, ReadAhead,
|
||||||
|
Values(tuple<bool, int>(false, 0u),
|
||||||
|
tuple<bool, int>(false, 0x10000),
|
||||||
|
tuple<bool, int>(false, 0x20000),
|
||||||
|
tuple<bool, int>(false, 0x30000),
|
||||||
|
tuple<bool, int>(true, 0u),
|
||||||
|
tuple<bool, int>(true, 0x10000)));
|
||||||
|
@ -113,7 +113,7 @@ void FuseTest::SetUp() {
|
|||||||
m_mock = new MockFS(m_maxreadahead, m_allow_other,
|
m_mock = new MockFS(m_maxreadahead, m_allow_other,
|
||||||
m_default_permissions, m_push_symlinks_in, m_ro,
|
m_default_permissions, m_push_symlinks_in, m_ro,
|
||||||
m_pm, m_init_flags, m_kernel_minor_version,
|
m_pm, m_init_flags, m_kernel_minor_version,
|
||||||
m_maxwrite, m_async);
|
m_maxwrite, m_async, m_noclusterr);
|
||||||
/*
|
/*
|
||||||
* FUSE_ACCESS is called almost universally. Expecting it in
|
* FUSE_ACCESS is called almost universally. Expecting it in
|
||||||
* each test case would be super-annoying. Instead, set a
|
* each test case would be super-annoying. Instead, set a
|
||||||
|
@ -54,6 +54,7 @@ class FuseTest : public ::testing::Test {
|
|||||||
bool m_push_symlinks_in;
|
bool m_push_symlinks_in;
|
||||||
bool m_ro;
|
bool m_ro;
|
||||||
bool m_async;
|
bool m_async;
|
||||||
|
bool m_noclusterr;
|
||||||
MockFS *m_mock = NULL;
|
MockFS *m_mock = NULL;
|
||||||
const static uint64_t FH = 0xdeadbeef1a7ebabe;
|
const static uint64_t FH = 0xdeadbeef1a7ebabe;
|
||||||
|
|
||||||
@ -70,7 +71,8 @@ class FuseTest : public ::testing::Test {
|
|||||||
m_pm(BLOCKING),
|
m_pm(BLOCKING),
|
||||||
m_push_symlinks_in(false),
|
m_push_symlinks_in(false),
|
||||||
m_ro(false),
|
m_ro(false),
|
||||||
m_async(false)
|
m_async(false),
|
||||||
|
m_noclusterr(false)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
virtual void SetUp();
|
virtual void SetUp();
|
||||||
|
Loading…
Reference in New Issue
Block a user