summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAmir Goldstein <amir73il@gmail.com>2022-01-03 10:49:46 +0200
committerAmir Goldstein <amir73il@gmail.com>2022-01-05 08:43:43 +0200
commit435a14e0297cf796a19aaf04221fa14957fb3717 (patch)
tree05362cfb72968660b066d81a5e42cbebd0cd56e5
parentdad15aee26835240d72188179ba177a0bbafe659 (diff)
downloadfuse-435a14e0297cf796a19aaf04221fa14957fb3717.tar.gz
Add test for FOPEN_NOFLUSH flag
Simulate write() delay and verify that close(rofd) does not block waiting on pending writes. The support for the flag was added in kernel v5.16-rc1. Signed-off-by: Amir Goldstein <amir73il@gmail.com>
-rw-r--r--test/test_ctests.py5
-rw-r--r--test/test_write_cache.c61
2 files changed, 65 insertions, 1 deletions
diff --git a/test/test_ctests.py b/test/test_ctests.py
index 4290f20..63728c3 100644
--- a/test/test_ctests.py
+++ b/test/test_ctests.py
@@ -33,6 +33,11 @@ def test_write_cache(tmpdir, writeback, output_checker):
mnt_dir ]
if writeback:
cmdline.append('-owriteback_cache')
+ elif LooseVersion(platform.release()) >= '5.16':
+ # Test that close(rofd) does not block waiting for pending writes.
+ # This test requires kernel commit a390ccb316be ("fuse: add FOPEN_NOFLUSH")
+ # so opt-in for this test from kernel 5.16.
+ cmdline.append('--delay_ms=200')
subprocess.check_call(cmdline, stdout=output_checker.fd, stderr=output_checker.fd)
diff --git a/test/test_write_cache.c b/test/test_write_cache.c
index 88344dc..03ccc98 100644
--- a/test/test_write_cache.c
+++ b/test/test_write_cache.c
@@ -34,9 +34,11 @@
struct options {
int writeback;
int data_size;
+ int delay_ms;
} options = {
.writeback = 0,
.data_size = 4096,
+ .delay_ms = 0,
};
#define OPTION(t, p) \
@@ -44,10 +46,15 @@ struct options {
static const struct fuse_opt option_spec[] = {
OPTION("writeback_cache", writeback),
OPTION("--data-size=%d", data_size),
+ OPTION("--delay_ms=%d", delay_ms),
FUSE_OPT_END
};
static int got_write;
+pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+static int write_start, write_done;
+
static void tfs_init (void *userdata, struct fuse_conn_info *conn)
{
(void) userdata;
@@ -117,6 +124,9 @@ static void tfs_open(fuse_req_t req, fuse_ino_t ino,
fuse_reply_err(req, EISDIR);
else {
assert(ino == FILE_INO);
+ /* Test close(rofd) does not block waiting for pending writes */
+ fi->noflush = !options.writeback && options.delay_ms &&
+ (fi->flags & O_ACCMODE) == O_RDONLY;
fuse_reply_open(req, fi);
}
}
@@ -136,6 +146,22 @@ static void tfs_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
expected, size);
else
got_write = 1;
+
+ /* Simulate waiting for pending writes */
+ if (options.delay_ms) {
+ pthread_mutex_lock(&lock);
+ write_start = 1;
+ pthread_cond_signal(&cond);
+ pthread_mutex_unlock(&lock);
+
+ usleep(options.delay_ms * 1000);
+
+ pthread_mutex_lock(&lock);
+ write_done = 1;
+ pthread_cond_signal(&cond);
+ pthread_mutex_unlock(&lock);
+ }
+
fuse_reply_write(req, size);
}
@@ -147,6 +173,25 @@ static struct fuse_lowlevel_ops tfs_oper = {
.write = tfs_write,
};
+static void* close_rofd(void *data) {
+ int rofd = (int)(long) data;
+
+ /* Wait for first write to start */
+ pthread_mutex_lock(&lock);
+ while (!write_start && !write_done)
+ pthread_cond_wait(&cond, &lock);
+ pthread_mutex_unlock(&lock);
+
+ close(rofd);
+ printf("rofd closed. write_start: %d write_done: %d\n", write_start, write_done);
+
+ /* First write should not have been completed */
+ if (write_done)
+ fprintf(stderr, "ERROR: close(rofd) blocked on write!\n");
+
+ return NULL;
+}
+
static void* run_fs(void *data) {
struct fuse_session *se = (struct fuse_session*) data;
assert(fuse_session_loop(se) == 0);
@@ -157,7 +202,8 @@ static void test_fs(char *mountpoint) {
char fname[PATH_MAX];
char *buf;
size_t dsize = options.data_size;
- int fd;
+ int fd, rofd;
+ pthread_t rofd_thread;
buf = malloc(dsize);
assert(buf != NULL);
@@ -173,10 +219,23 @@ static void test_fs(char *mountpoint) {
assert(0);
}
+ if (options.delay_ms) {
+ /* Verify that close(rofd) does not block waiting for pending writes */
+ rofd = open(fname, O_RDONLY);
+ assert(pthread_create(&rofd_thread, NULL, close_rofd, (void *)(long)rofd) == 0);
+ /* Give close_rofd time to start */
+ usleep(options.delay_ms * 1000);
+ }
+
assert(write(fd, buf, dsize) == dsize);
assert(write(fd, buf, dsize) == dsize);
free(buf);
close(fd);
+
+ if (options.delay_ms) {
+ printf("rwfd closed. write_start: %d write_done: %d\n", write_start, write_done);
+ assert(pthread_join(rofd_thread, NULL) == 0);
+ }
}
int main(int argc, char *argv[]) {