From 435a14e0297cf796a19aaf04221fa14957fb3717 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Mon, 3 Jan 2022 10:49:46 +0200 Subject: [PATCH] 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 --- test/test_ctests.py | 5 ++++ test/test_write_cache.c | 61 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 65 insertions(+), 1 deletion(-) 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[]) { -- 2.30.2