Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 17 Jun 2019 16:56:51 +0000 (UTC)
From:      Alan Somers <asomers@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r349147 - in projects/fuse2: sys/fs/fuse tests/sys/fs/fusefs
Message-ID:  <201906171656.x5HGupbU003915@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: asomers
Date: Mon Jun 17 16:56:51 2019
New Revision: 349147
URL: https://svnweb.freebsd.org/changeset/base/349147

Log:
  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

Modified:
  projects/fuse2/sys/fs/fuse/fuse_internal.c
  projects/fuse2/sys/fs/fuse/fuse_io.c
  projects/fuse2/sys/fs/fuse/fuse_ipc.h
  projects/fuse2/tests/sys/fs/fusefs/read.cc
  projects/fuse2/tests/sys/fs/fusefs/utils.hh

Modified: projects/fuse2/sys/fs/fuse/fuse_internal.c
==============================================================================
--- projects/fuse2/sys/fs/fuse/fuse_internal.c	Mon Jun 17 16:54:51 2019	(r349146)
+++ projects/fuse2/sys/fs/fuse/fuse_internal.c	Mon Jun 17 16:56:51 2019	(r349147)
@@ -908,6 +908,7 @@ fuse_internal_init_callback(struct fuse_ticket *tick, 
 
 	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
 	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;
 	/*

Modified: projects/fuse2/sys/fs/fuse/fuse_io.c
==============================================================================
--- projects/fuse2/sys/fs/fuse/fuse_io.c	Mon Jun 17 16:54:51 2019	(r349146)
+++ projects/fuse2/sys/fs/fuse/fuse_io.c	Mon Jun 17 16:56:51 2019	(r349147)
@@ -271,16 +271,22 @@ fuse_read_biobackend(struct vnode *vp, struct uio *uio
     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
 		} 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;

Modified: projects/fuse2/sys/fs/fuse/fuse_ipc.h
==============================================================================
--- projects/fuse2/sys/fs/fuse/fuse_ipc.h	Mon Jun 17 16:54:51 2019	(r349146)
+++ projects/fuse2/sys/fs/fuse/fuse_ipc.h	Mon Jun 17 16:56:51 2019	(r349147)
@@ -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;

Modified: projects/fuse2/tests/sys/fs/fusefs/read.cc
==============================================================================
--- projects/fuse2/tests/sys/fs/fusefs/read.cc	Mon Jun 17 16:54:51 2019	(r349146)
+++ projects/fuse2/tests/sys/fs/fusefs/read.cc	Mon Jun 17 16:56:51 2019	(r349147)
@@ -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));

Modified: projects/fuse2/tests/sys/fs/fusefs/utils.hh
==============================================================================
--- projects/fuse2/tests/sys/fs/fusefs/utils.hh	Mon Jun 17 16:54:51 2019	(r349146)
+++ projects/fuse2/tests/sys/fs/fusefs/utils.hh	Mon Jun 17 16:56:51 2019	(r349147)
@@ -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),



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201906171656.x5HGupbU003915>