fusefs: implement non-clustered readahead

fusefs will now read ahead at most one cache block at a time (usually 64
KB).  Clustered reads are still TODO.  Individual file systems may disable
read ahead by setting fuse_init_out.max_readahead=0 during initialization.

Sponsored by:	The FreeBSD Foundation
This commit is contained in:
Alan Somers 2019-06-17 16:56:51 +00:00
parent eadd12d35d
commit d569012f45
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/projects/fuse2/; revision=349147
5 changed files with 50 additions and 31 deletions

View File

@ -908,6 +908,7 @@ fuse_internal_init_callback(struct fuse_ticket *tick, struct uio *uio)
if (fuse_libabi_geq(data, 7, 5)) {
if (fticket_resp(tick)->len == sizeof(struct fuse_init_out)) {
data->max_readahead = fiio->max_readahead;
data->max_write = fiio->max_write;
if (fiio->flags & FUSE_ASYNC_READ)
data->dataflags |= FSESS_ASYNC_READ;
@ -951,9 +952,8 @@ fuse_internal_send_init(struct fuse_data *data, struct thread *td)
fiii->major = FUSE_KERNEL_VERSION;
fiii->minor = FUSE_KERNEL_MINOR_VERSION;
/*
* fusefs currently doesn't do any readahead other than fetching whole
* buffer cache block sized regions at once. So the max readahead is
* the size of a buffer cache block.
* fusefs currently reads ahead no more than one cache block at a time.
* See fuse_read_biobackend
*/
fiii->max_readahead = maxbcachebuf;
/*

View File

@ -271,16 +271,22 @@ fuse_read_biobackend(struct vnode *vp, struct uio *uio, int ioflag,
struct ucred *cred, struct fuse_filehandle *fufh, pid_t pid)
{
struct buf *bp;
daddr_t lbn;
int bcount;
int err, n = 0, on = 0;
struct mount *mp;
struct fuse_data *data;
daddr_t lbn, nextlbn;
int bcount, nextsize;
int err, n = 0, on = 0, seqcount;
off_t filesize;
const int biosize = fuse_iosize(vp);
mp = vnode_mount(vp);
data = fuse_get_mpdata(mp);
if (uio->uio_offset < 0)
return (EINVAL);
seqcount = ioflag >> IO_SEQSHIFT;
err = fuse_vnode_size(vp, &filesize, cred, curthread);
if (err)
return err;
@ -302,12 +308,25 @@ fuse_read_biobackend(struct vnode *vp, struct uio *uio, int ioflag,
} else {
bcount = biosize;
}
nextlbn = lbn + 1;
nextsize = MIN(biosize, filesize - nextlbn * biosize);
SDT_PROBE4(fusefs, , io, read_bio_backend_start,
biosize, (int)lbn, on, bcount);
/* TODO: readahead. See ext2_read for an example */
err = bread(vp, lbn, bcount, NOCRED, &bp);
if (bcount < biosize) {
/* If near EOF, don't do readahead */
err = bread(vp, lbn, bcount, NOCRED, &bp);
/* TODO: clustered read */
} else if (seqcount > 1 && data->max_readahead >= nextsize) {
/* Try non-clustered readahead */
err = breadn(vp, lbn, bcount, &nextlbn, &nextsize, 1,
NOCRED, &bp);
} else {
/* Just read what was requested */
err = bread(vp, lbn, bcount, NOCRED, &bp);
}
if (err) {
brelse(bp);
bp = NULL;

View File

@ -197,6 +197,7 @@ struct fuse_data {
uint32_t fuse_libabi_major;
uint32_t fuse_libabi_minor;
uint32_t max_readahead;
uint32_t max_write;
uint32_t max_read;
uint32_t subtype;

View File

@ -112,7 +112,7 @@ virtual void SetUp() {
class ReadAhead: public ReadCacheable, public WithParamInterface<uint32_t> {
virtual void SetUp() {
m_maxreadahead = GetParam();
Read::SetUp();
ReadCacheable::SetUp();
}
};
@ -747,37 +747,40 @@ TEST_F(ReadCacheable, DISABLED_sendfile_eio)
}
/* fuse(4) should honor the filesystem's requested m_readahead parameter */
/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236472 */
TEST_P(ReadAhead, DISABLED_readahead) {
TEST_P(ReadAhead, readahead) {
const char FULLPATH[] = "mountpoint/some_file.txt";
const char RELPATH[] = "some_file.txt";
const char *CONTENTS0 = "abcdefghijklmnop";
uint64_t ino = 42;
int fd;
ssize_t bufsize = 8;
ssize_t filesize = m_maxbcachebuf * 2;
char *contents;
char buf[bufsize];
int fd, i;
ssize_t bufsize = m_maxbcachebuf;
ssize_t filesize = m_maxbcachebuf * 4;
char *rbuf, *contents;
ASSERT_TRUE(GetParam() < (uint32_t)m_maxbcachebuf)
<< "Test assumes that max_readahead < maxbcachebuf";
contents = (char*)calloc(1, filesize);
contents = (char*)malloc(filesize);
ASSERT_NE(NULL, contents);
memmove(contents, CONTENTS0, strlen(CONTENTS0));
memset(contents, 'X', filesize);
rbuf = (char*)calloc(1, bufsize);
expect_lookup(RELPATH, ino, filesize);
expect_open(ino, 0, 1);
/* fuse(4) should only read ahead the allowed amount */
expect_read(ino, 0, GetParam(), GetParam(), contents);
expect_read(ino, 0, m_maxbcachebuf, m_maxbcachebuf, contents);
for (i = 0; i < (int)GetParam() / m_maxbcachebuf; i++) {
off_t offs = (i + 1) * m_maxbcachebuf;
expect_read(ino, offs, m_maxbcachebuf, m_maxbcachebuf,
contents + offs);
}
fd = open(FULLPATH, O_RDONLY);
ASSERT_LE(0, fd) << strerror(errno);
ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
ASSERT_EQ(0, memcmp(buf, CONTENTS0, bufsize));
/* 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 */
}
INSTANTIATE_TEST_CASE_P(RA, ReadAhead, ::testing::Values(0u, 2048u));
INSTANTIATE_TEST_CASE_P(RA, ReadAhead, ::testing::Values(0u, 65536));

View File

@ -61,11 +61,7 @@ class FuseTest : public ::testing::Test {
int m_maxbcachebuf;
FuseTest():
/*
* libfuse's default max_readahead is UINT_MAX, though it can
* be lowered
*/
m_maxreadahead(UINT_MAX),
m_maxreadahead(0),
m_maxwrite(default_max_write),
m_init_flags(0),
m_allow_other(false),