Changeset View
Changeset View
Standalone View
Standalone View
tests/sys/fs/fusefs/notify.cc
Show First 20 Lines • Show All 317 Lines • ▼ Show 20 Lines | TEST_F(Notify, inval_inode_nonexistent) | ||||
iia.len = 0; | iia.len = 0; | ||||
ASSERT_EQ(0, pthread_create(&th0, NULL, inval_inode, &iia)) | ASSERT_EQ(0, pthread_create(&th0, NULL, inval_inode, &iia)) | ||||
<< strerror(errno); | << strerror(errno); | ||||
pthread_join(th0, &thr0_value); | pthread_join(th0, &thr0_value); | ||||
/* It's not an error for an inode to not be cached */ | /* It's not an error for an inode to not be cached */ | ||||
EXPECT_EQ(0, (intptr_t)thr0_value); | EXPECT_EQ(0, (intptr_t)thr0_value); | ||||
} | } | ||||
TEST_F(Notify, inval_inode_with_clean_cache) | /* Invalidate I/O in the pattern generated by cephfs */ | ||||
TEST_F(Notify, inval_inode_like_cephfs) | |||||
{ | { | ||||
const static char FULLPATH[] = "mountpoint/foo"; | const static char FULLPATH[] = "mountpoint/foo"; | ||||
const static char RELPATH[] = "foo"; | const static char RELPATH[] = "foo"; | ||||
const char CONTENTS0[] = "abcdefgh"; | const char CONTENTS0[] = "0123456789"; | ||||
const char CONTENTS1[] = "ijklmnopqrstuvwxyz"; | const char CONTENTS1[] = "0123456789Singapore"; | ||||
struct inval_inode_args iia; | struct inval_inode_args iia; | ||||
struct stat sb; | struct stat sb; | ||||
ino_t ino = 42; | ino_t ino = 42; | ||||
void *thr0_value; | void *thr0_value; | ||||
Sequence seq; | Sequence seq; | ||||
uid_t uid = 12345; | uid_t uid = 12345; | ||||
pthread_t th0; | pthread_t th0; | ||||
ssize_t size0 = sizeof(CONTENTS0); | ssize_t size0 = sizeof(CONTENTS0); | ||||
ssize_t size1 = sizeof(CONTENTS1); | ssize_t size1 = sizeof(CONTENTS1); | ||||
char buf[80]; | char buf[80]; | ||||
int fd; | int fd; | ||||
expect_lookup(FUSE_ROOT_ID, RELPATH, ino, size0, seq); | expect_lookup(FUSE_ROOT_ID, RELPATH, ino, size0, seq); | ||||
expect_open(ino, 0, 1); | expect_open(ino, 0, 1); | ||||
expect_read(ino, 0, size0, size0, CONTENTS0); | |||||
/* Fill the data cache */ | |||||
fd = open(FULLPATH, O_RDWR); | |||||
ASSERT_LE(0, fd) << strerror(errno); | |||||
ASSERT_EQ(size0, read(fd, buf, sizeof(buf))) << strerror(errno); | |||||
EXPECT_EQ(0, memcmp(buf, CONTENTS0, size0)); | |||||
/* Evict the data cache */ | |||||
iia.mock = m_mock; | |||||
iia.ino = ino; | |||||
iia.off = 0; | |||||
iia.len = 0; | |||||
ASSERT_EQ(0, pthread_create(&th0, NULL, inval_inode, &iia)) | |||||
<< strerror(errno); | |||||
pthread_join(th0, &thr0_value); | |||||
EXPECT_EQ(0, (intptr_t)thr0_value); | |||||
/* cache attributes were purged; this will trigger a new GETATTR */ | |||||
EXPECT_CALL(*m_mock, process( | EXPECT_CALL(*m_mock, process( | ||||
ResultOf([=](auto in) { | ResultOf([=](auto in) { | ||||
return (in.header.opcode == FUSE_GETATTR && | return (in.header.opcode == FUSE_GETATTR && | ||||
in.header.nodeid == ino); | in.header.nodeid == ino); | ||||
}, Eq(true)), | }, Eq(true)), | ||||
_) | _) | ||||
).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto& out) { | ).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto& out) { | ||||
SET_OUT_HEADER_LEN(out, attr); | SET_OUT_HEADER_LEN(out, attr); | ||||
out.body.attr.attr.mode = S_IFREG | 0644; | out.body.attr.attr.mode = S_IFREG | 0644; | ||||
out.body.attr.attr_valid = UINT64_MAX; | out.body.attr.attr_valid = UINT64_MAX; | ||||
out.body.attr.attr.size = size1; | out.body.attr.attr.size = size1; | ||||
out.body.attr.attr.uid = uid; | out.body.attr.attr.uid = uid; | ||||
}))); | }))); | ||||
expect_read(ino, 0, size0, size0, CONTENTS0); | |||||
expect_read(ino, 0, size1, size1, CONTENTS1); | expect_read(ino, 0, size1, size1, CONTENTS1); | ||||
ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno); | |||||
EXPECT_EQ(uid, sb.st_uid); | |||||
EXPECT_EQ(size1, sb.st_size); | |||||
/* This read should not be serviced by cache */ | |||||
ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); | |||||
ASSERT_EQ(size1, read(fd, buf, sizeof(buf))) << strerror(errno); | |||||
EXPECT_EQ(0, memcmp(buf, CONTENTS1, size1)); | |||||
leak(fd); | |||||
} | |||||
TEST_F(Notify, inval_inode_with_clean_cache) | |||||
{ | |||||
const static char FULLPATH[] = "mountpoint/foo"; | |||||
const static char RELPATH[] = "foo"; | |||||
const char CONTENTS0[] = "abcdefgh"; | |||||
const char CONTENTS1[] = "ijklmnopqrstuvwxyz"; | |||||
struct inval_inode_args iia; | |||||
struct stat sb; | |||||
ino_t ino = 42; | |||||
void *thr0_value; | |||||
Sequence seq; | |||||
uid_t uid = 12345; | |||||
pthread_t th0; | |||||
ssize_t size0 = sizeof(CONTENTS0); | |||||
ssize_t size1 = sizeof(CONTENTS1); | |||||
char buf[80]; | |||||
int fd; | |||||
expect_lookup(FUSE_ROOT_ID, RELPATH, ino, size0, seq); | |||||
expect_open(ino, 0, 1); | |||||
expect_read(ino, 0, size0, size0, CONTENTS0); | |||||
/* Fill the data cache */ | /* Fill the data cache */ | ||||
fd = open(FULLPATH, O_RDWR); | fd = open(FULLPATH, O_RDWR); | ||||
ASSERT_LE(0, fd) << strerror(errno); | ASSERT_LE(0, fd) << strerror(errno); | ||||
ASSERT_EQ(size0, read(fd, buf, size0)) << strerror(errno); | ASSERT_EQ(size0, read(fd, buf, sizeof(buf))) << strerror(errno); | ||||
EXPECT_EQ(0, memcmp(buf, CONTENTS0, size0)); | EXPECT_EQ(0, memcmp(buf, CONTENTS0, size0)); | ||||
/* Evict the data cache */ | /* Evict the data cache */ | ||||
iia.mock = m_mock; | iia.mock = m_mock; | ||||
iia.ino = ino; | iia.ino = ino; | ||||
iia.off = 0; | iia.off = 0; | ||||
iia.len = 0; | iia.len = 0; | ||||
ASSERT_EQ(0, pthread_create(&th0, NULL, inval_inode, &iia)) | ASSERT_EQ(0, pthread_create(&th0, NULL, inval_inode, &iia)) | ||||
<< strerror(errno); | << strerror(errno); | ||||
pthread_join(th0, &thr0_value); | pthread_join(th0, &thr0_value); | ||||
EXPECT_EQ(0, (intptr_t)thr0_value); | EXPECT_EQ(0, (intptr_t)thr0_value); | ||||
/* cache attributes were purged; this will trigger a new GETATTR */ | /* cache attributes were purged; this will trigger a new GETATTR */ | ||||
EXPECT_CALL(*m_mock, process( | |||||
ResultOf([=](auto in) { | |||||
return (in.header.opcode == FUSE_GETATTR && | |||||
in.header.nodeid == ino); | |||||
}, Eq(true)), | |||||
_) | |||||
).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto& out) { | |||||
SET_OUT_HEADER_LEN(out, attr); | |||||
out.body.attr.attr.mode = S_IFREG | 0644; | |||||
out.body.attr.attr_valid = UINT64_MAX; | |||||
out.body.attr.attr.size = size1; | |||||
out.body.attr.attr.uid = uid; | |||||
}))); | |||||
expect_read(ino, 0, size1, size1, CONTENTS1); | |||||
ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno); | ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno); | ||||
EXPECT_EQ(uid, sb.st_uid); | EXPECT_EQ(uid, sb.st_uid); | ||||
EXPECT_EQ(size1, sb.st_size); | EXPECT_EQ(size1, sb.st_size); | ||||
/* This read should not be serviced by cache */ | /* This read should not be serviced by cache */ | ||||
ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); | ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); | ||||
ASSERT_EQ(size1, read(fd, buf, size1)) << strerror(errno); | ASSERT_EQ(size1, read(fd, buf, sizeof(buf))) << strerror(errno); | ||||
EXPECT_EQ(0, memcmp(buf, CONTENTS1, size1)); | EXPECT_EQ(0, memcmp(buf, CONTENTS1, size1)); | ||||
leak(fd); | leak(fd); | ||||
} | } | ||||
/* FUSE_NOTIFY_STORE with a file that's not in the entry cache */ | /* FUSE_NOTIFY_STORE with a file that's not in the entry cache */ | ||||
/* disabled because FUSE_NOTIFY_STORE is not yet implemented */ | /* disabled because FUSE_NOTIFY_STORE is not yet implemented */ | ||||
TEST_F(Notify, DISABLED_store_nonexistent) | TEST_F(Notify, DISABLED_store_nonexistent) | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | TEST_F(Notify, DISABLED_store_with_blank_cache) | ||||
sa.offset = 0; | sa.offset = 0; | ||||
sa.size = size1; | sa.size = size1; | ||||
sa.data = (const void*)CONTENTS1; | sa.data = (const void*)CONTENTS1; | ||||
ASSERT_EQ(0, pthread_create(&th0, NULL, store, &sa)) << strerror(errno); | ASSERT_EQ(0, pthread_create(&th0, NULL, store, &sa)) << strerror(errno); | ||||
pthread_join(th0, &thr0_value); | pthread_join(th0, &thr0_value); | ||||
EXPECT_EQ(0, (intptr_t)thr0_value); | EXPECT_EQ(0, (intptr_t)thr0_value); | ||||
/* This read should be serviced by cache */ | /* This read should be serviced by cache */ | ||||
ASSERT_EQ(size1, read(fd, buf, size1)) << strerror(errno); | ASSERT_EQ(size1, read(fd, buf, sizeof(buf))) << strerror(errno); | ||||
EXPECT_EQ(0, memcmp(buf, CONTENTS1, size1)); | EXPECT_EQ(0, memcmp(buf, CONTENTS1, size1)); | ||||
leak(fd); | leak(fd); | ||||
} | } | ||||
TEST_F(NotifyWriteback, inval_inode_with_dirty_cache) | TEST_F(NotifyWriteback, inval_inode_with_dirty_cache) | ||||
{ | { | ||||
const static char FULLPATH[] = "mountpoint/foo"; | const static char FULLPATH[] = "mountpoint/foo"; | ||||
const static char RELPATH[] = "foo"; | const static char RELPATH[] = "foo"; | ||||
const char CONTENTS[] = "abcdefgh"; | const char CONTENTS0[] = "abcdefgh"; | ||||
const char CONTENTS1[] = "ijklmnop"; | |||||
struct inval_inode_args iia; | struct inval_inode_args iia; | ||||
ino_t ino = 42; | ino_t ino = 42; | ||||
void *thr0_value; | void *thr0_value; | ||||
Sequence seq; | Sequence seq; | ||||
pthread_t th0; | pthread_t th0; | ||||
ssize_t bufsize = sizeof(CONTENTS); | ssize_t size0 = sizeof(CONTENTS0); | ||||
ssize_t size1 = sizeof(CONTENTS1); | |||||
char buf[80]; | |||||
int fd; | int fd; | ||||
expect_lookup(FUSE_ROOT_ID, RELPATH, ino, 0, seq); | expect_lookup(FUSE_ROOT_ID, RELPATH, ino, 0, seq); | ||||
expect_open(ino, 0, 1); | expect_open(ino, 0, 1); | ||||
/* Fill the data cache */ | /* Fill the data cache */ | ||||
fd = open(FULLPATH, O_RDWR); | fd = open(FULLPATH, O_RDWR); | ||||
ASSERT_LE(0, fd); | ASSERT_LE(0, fd); | ||||
ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); | ASSERT_EQ(size0, write(fd, CONTENTS0, size0)) << strerror(errno); | ||||
expect_write(ino, 0, bufsize, CONTENTS); | expect_write(ino, 0, size0, CONTENTS0); | ||||
/* | /* | ||||
* The FUSE protocol does not require an fsync here, but FreeBSD's | * The FUSE protocol does not require an fsync here, but FreeBSD's | ||||
* bufobj_invalbuf sends it anyway | * bufobj_invalbuf sends it anyway | ||||
*/ | */ | ||||
maybe_expect_fsync(ino); | maybe_expect_fsync(ino); | ||||
/* Evict the data cache */ | /* Evict the data cache */ | ||||
iia.mock = m_mock; | iia.mock = m_mock; | ||||
iia.ino = ino; | iia.ino = ino; | ||||
iia.off = 0; | iia.off = 0; | ||||
iia.len = 0; | iia.len = 0; | ||||
ASSERT_EQ(0, pthread_create(&th0, NULL, inval_inode, &iia)) | ASSERT_EQ(0, pthread_create(&th0, NULL, inval_inode, &iia)) | ||||
<< strerror(errno); | << strerror(errno); | ||||
pthread_join(th0, &thr0_value); | pthread_join(th0, &thr0_value); | ||||
EXPECT_EQ(0, (intptr_t)thr0_value); | EXPECT_EQ(0, (intptr_t)thr0_value); | ||||
/* | |||||
* Reading again should trigger another FUSE_READ because the cache | |||||
* is empty. | |||||
*/ | |||||
EXPECT_CALL(*m_mock, process( | |||||
ResultOf([=](auto in) { | |||||
return (in.header.opcode == FUSE_GETATTR && | |||||
in.header.nodeid == ino); | |||||
}, Eq(true)), | |||||
_) | |||||
).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto& out) { | |||||
SET_OUT_HEADER_LEN(out, attr); | |||||
out.body.attr.attr.mode = S_IFREG | 0644; | |||||
out.body.attr.attr_valid = UINT64_MAX; | |||||
out.body.attr.attr.size = size1; | |||||
}))); | |||||
expect_read(ino, 0, size1, size1, CONTENTS1); | |||||
ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); | |||||
ASSERT_EQ(size1, read(fd, buf, sizeof(buf))) << strerror(errno); | |||||
leak(fd); | leak(fd); | ||||
} | } | ||||
TEST_F(NotifyWriteback, inval_inode_attrs_only) | TEST_F(NotifyWriteback, inval_inode_attrs_only) | ||||
{ | { | ||||
const static char FULLPATH[] = "mountpoint/foo"; | const static char FULLPATH[] = "mountpoint/foo"; | ||||
const static char RELPATH[] = "foo"; | const static char RELPATH[] = "foo"; | ||||
▲ Show 20 Lines • Show All 55 Lines • Show Last 20 Lines |