fuse(4): add tests for the FUSE_ASYNC_READ option

Sponsored by:	The FreeBSD Foundation
This commit is contained in:
Alan Somers 2019-03-15 20:16:35 +00:00
parent bce2c9bbd8
commit 48f58d58cf
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/projects/fuse2/; revision=345201

View File

@ -55,6 +55,7 @@ void expect_lookup(const char *relpath, uint64_t ino)
};
class AioRead: public Read {
public:
virtual void SetUp() {
const char *node = "vfs.aio.enable_unsafe";
int val = 0;
@ -69,6 +70,13 @@ virtual void SetUp() {
}
};
class AsyncRead: public AioRead {
virtual void SetUp() {
m_init_flags = FUSE_ASYNC_READ;
AioRead::SetUp();
}
};
class ReadAhead: public Read, public WithParamInterface<uint32_t> {
virtual void SetUp() {
m_maxreadahead = GetParam();
@ -108,6 +116,159 @@ TEST_F(AioRead, aio_read)
/* Deliberately leak fd. close(2) will be tested in release.cc */
}
/*
* Without the FUSE_ASYNC_READ mount option, fuse(4) should ensure that there
* is at most one outstanding read operation per file handle
*/
TEST_F(AioRead, async_read_disabled)
{
const char FULLPATH[] = "mountpoint/some_file.txt";
const char RELPATH[] = "some_file.txt";
uint64_t ino = 42;
int fd;
ssize_t bufsize = 50;
char buf0[bufsize], buf1[bufsize];
off_t off0 = 0;
off_t off1 = 4096;
struct aiocb iocb0, iocb1;
expect_lookup(RELPATH, ino);
expect_open(ino, 0, 1);
expect_getattr(ino, bufsize);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_READ &&
in->header.nodeid == ino &&
in->body.read.fh == FH &&
in->body.read.offset == (uint64_t)off0 &&
in->body.read.size == bufsize);
}, Eq(true)),
_)
).WillOnce(Invoke([](auto in __unused, auto &out __unused) {
/* Filesystem is slow to respond */
}));
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_READ &&
in->header.nodeid == ino &&
in->body.read.fh == FH &&
in->body.read.offset == (uint64_t)off1 &&
in->body.read.size == bufsize);
}, Eq(true)),
_)
).Times(0);
fd = open(FULLPATH, O_RDONLY);
ASSERT_LE(0, fd) << strerror(errno);
/*
* Submit two AIO read requests, and respond to neither. If the
* filesystem ever gets the second read request, then we failed to
* limit outstanding reads.
*/
iocb0.aio_nbytes = bufsize;
iocb0.aio_fildes = fd;
iocb0.aio_buf = buf0;
iocb0.aio_offset = off0;
iocb0.aio_sigevent.sigev_notify = SIGEV_NONE;
ASSERT_EQ(0, aio_read(&iocb0)) << strerror(errno);
iocb1.aio_nbytes = bufsize;
iocb1.aio_fildes = fd;
iocb1.aio_buf = buf1;
iocb1.aio_offset = off1;
iocb1.aio_sigevent.sigev_notify = SIGEV_NONE;
ASSERT_EQ(0, aio_read(&iocb1)) << strerror(errno);
/*
* Sleep for awhile to make sure the kernel has had a chance to issue
* the second read, even though the first has not yet returned
*/
usleep(250'000);
/* Deliberately leak iocbs */
/* Deliberately leak fd. close(2) will be tested in release.cc */
}
/*
* With the FUSE_ASYNC_READ mount option, fuse(4) may issue multiple
* simultaneous read requests on the same file handle.
*/
/*
* Disabled because we don't yet implement FUSE_ASYNC_READ. No bugzilla
* entry, because that's a feature request, not a bug.
*/
TEST_F(AsyncRead, DISABLED_async_read)
{
const char FULLPATH[] = "mountpoint/some_file.txt";
const char RELPATH[] = "some_file.txt";
uint64_t ino = 42;
int fd;
ssize_t bufsize = 50;
char buf0[bufsize], buf1[bufsize];
off_t off0 = 0;
off_t off1 = 4096;
struct aiocb iocb0, iocb1;
expect_lookup(RELPATH, ino);
expect_open(ino, 0, 1);
expect_getattr(ino, bufsize);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_READ &&
in->header.nodeid == ino &&
in->body.read.fh == FH &&
in->body.read.offset == (uint64_t)off0 &&
in->body.read.size == bufsize);
}, Eq(true)),
_)
).WillOnce(Invoke([](auto in __unused, auto &out __unused) {
/* Filesystem is slow to respond */
}));
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_READ &&
in->header.nodeid == ino &&
in->body.read.fh == FH &&
in->body.read.offset == (uint64_t)off1 &&
in->body.read.size == bufsize);
}, Eq(true)),
_)
).WillOnce(Invoke([](auto in __unused, auto &out __unused) {
/* Filesystem is slow to respond */
}));
fd = open(FULLPATH, O_RDONLY);
ASSERT_LE(0, fd) << strerror(errno);
/*
* Submit two AIO read requests, but respond to neither. Ensure that
* we received both.
*/
iocb0.aio_nbytes = bufsize;
iocb0.aio_fildes = fd;
iocb0.aio_buf = buf0;
iocb0.aio_offset = off0;
iocb0.aio_sigevent.sigev_notify = SIGEV_NONE;
ASSERT_EQ(0, aio_read(&iocb0)) << strerror(errno);
iocb1.aio_nbytes = bufsize;
iocb1.aio_fildes = fd;
iocb1.aio_buf = buf1;
iocb1.aio_offset = off1;
iocb1.aio_sigevent.sigev_notify = SIGEV_NONE;
ASSERT_EQ(0, aio_read(&iocb1)) << strerror(errno);
/*
* Sleep for awhile to make sure the kernel has had a chance to issue
* both reads.
*/
usleep(250'000);
/* Deliberately leak iocbs */
/* Deliberately leak fd. close(2) will be tested in release.cc */
}
/* 0-length reads shouldn't cause any confusion */
TEST_F(Read, direct_io_read_nothing)
{