From 1d1074b2a297ad939419afffd74b1c0781a77426 Mon Sep 17 00:00:00 2001 From: asomers Date: Mon, 17 Jun 2019 16:56:51 +0000 Subject: [PATCH] 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 --- sys/fs/fuse/fuse_internal.c | 6 +++--- sys/fs/fuse/fuse_io.c | 29 ++++++++++++++++++++++----- sys/fs/fuse/fuse_ipc.h | 1 + tests/sys/fs/fusefs/read.cc | 39 +++++++++++++++++++----------------- tests/sys/fs/fusefs/utils.hh | 6 +----- 5 files changed, 50 insertions(+), 31 deletions(-) diff --git a/sys/fs/fuse/fuse_internal.c b/sys/fs/fuse/fuse_internal.c index d17a315c3cb..cd24acd4035 100644 --- a/sys/fs/fuse/fuse_internal.c +++ b/sys/fs/fuse/fuse_internal.c @@ -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; /* diff --git a/sys/fs/fuse/fuse_io.c b/sys/fs/fuse/fuse_io.c index e2a7c19d1cf..e923d93bfcd 100644 --- a/sys/fs/fuse/fuse_io.c +++ b/sys/fs/fuse/fuse_io.c @@ -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; diff --git a/sys/fs/fuse/fuse_ipc.h b/sys/fs/fuse/fuse_ipc.h index 89a17d7f7e2..440a4f67149 100644 --- a/sys/fs/fuse/fuse_ipc.h +++ b/sys/fs/fuse/fuse_ipc.h @@ -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; diff --git a/tests/sys/fs/fusefs/read.cc b/tests/sys/fs/fusefs/read.cc index e986c131014..18e7217a2fa 100644 --- a/tests/sys/fs/fusefs/read.cc +++ b/tests/sys/fs/fusefs/read.cc @@ -112,7 +112,7 @@ virtual void SetUp() { class ReadAhead: public ReadCacheable, public WithParamInterface { 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]; - - ASSERT_TRUE(GetParam() < (uint32_t)m_maxbcachebuf) - << "Test assumes that max_readahead < maxbcachebuf"; + int fd, i; + ssize_t bufsize = m_maxbcachebuf; + ssize_t filesize = m_maxbcachebuf * 4; + char *rbuf, *contents; - 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)); diff --git a/tests/sys/fs/fusefs/utils.hh b/tests/sys/fs/fusefs/utils.hh index 82b32bceafe..96868361706 100644 --- a/tests/sys/fs/fusefs/utils.hh +++ b/tests/sys/fs/fusefs/utils.hh @@ -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), -- 2.45.0