[PATCH liburing] Test consistent file position updates

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



read(2)/write(2) and friends support sequential reads without giving an
explicit offset. The result of these should leave the file with an
incremented offset.

Add tests for both read and write to check that io_uring behaves
consistently in these scenarios. Expect that if you queue many
reads/writes, and set the IOSQE_IO_LINK flag, that they will behave
similarly to calling read(2)/write(2) in sequence.

Set IOSQE_ASYNC as well in a set of tests. This exacerbates the problem by
forcing work to happen in different threads to submission.

Also add tests for not setting IOSQE_IO_LINK, but do not treat these
results as errors. It is not clear to me what the outcome of many
overlapping read(2) should be in io_uring. In the read(2) etc calls a lock
is taken on the file preventing concurrent accesses - but I am not
convinced this should happen in the io_uring case.

Signed-off-by: Dylan Yudaken <dylany@xxxxxx>
---
 test/Makefile |   1 +
 test/fpos.c   | 254 ++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 255 insertions(+)
 create mode 100644 test/fpos.c

diff --git a/test/Makefile b/test/Makefile
index 1e318f7..f421f53 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -78,6 +78,7 @@ test_srcs := \
 	file-update.c \
 	file-verify.c \
 	fixed-link.c \
+	fpos.c \
 	fsync.c \
 	hardlink.c \
 	io-cancel.c \
diff --git a/test/fpos.c b/test/fpos.c
new file mode 100644
index 0000000..42b0617
--- /dev/null
+++ b/test/fpos.c
@@ -0,0 +1,254 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test io_uring fpos handling
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <assert.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+#define FILE_SIZE 10000
+#define QUEUE_SIZE 1024
+
+static void create_file(const char *file, size_t size)
+{
+	ssize_t ret;
+	char *buf;
+	size_t idx;
+	int fd;
+
+	buf = t_malloc(size);
+	for (idx = 0; idx < size; ++idx) {
+		/* write 0 or 1 */
+		buf[idx] = (unsigned char)(idx & 0x01);
+	}
+
+	fd = open(file, O_WRONLY | O_CREAT, 0644);
+	assert(fd >= 0);
+
+	ret = write(fd, buf, size);
+	fsync(fd);
+	close(fd);
+	free(buf);
+	assert(ret == size);
+}
+
+static int test_read(struct io_uring *ring, bool async, bool link)
+{
+	int ret, fd, i;
+	bool done = false;
+	struct io_uring_sqe *sqe;
+	struct io_uring_cqe *cqe;
+	loff_t current, expected = 0;
+	int count_ok;
+	int count_0 = 0, count_1 = 0;
+	unsigned char buff[QUEUE_SIZE];
+
+	create_file(".test_read", FILE_SIZE);
+	fd = open(".test_read", O_RDONLY);
+	unlink(".test_read");
+	assert(fd >= 0);
+
+	while (!done) {
+		for (i = 0; i < QUEUE_SIZE; ++i) {
+			sqe = io_uring_get_sqe(ring);
+			if (!sqe) {
+				fprintf(stderr, "no sqe\n");
+				return -1;
+			}
+			io_uring_prep_read(sqe, fd, buff + i, 1, -1);
+			sqe->user_data = 1;
+			if (async)
+				sqe->flags |= IOSQE_ASYNC;
+			if (link && i != QUEUE_SIZE - 1)
+				sqe->flags |= IOSQE_IO_LINK;
+		}
+		ret = io_uring_submit_and_wait(ring, QUEUE_SIZE);
+		if (ret != QUEUE_SIZE) {
+			fprintf(stderr, "submit failed: %d\n", ret);
+			return 1;
+		}
+		count_ok  = 0;
+		for (i = 0; i < QUEUE_SIZE; ++i) {
+			int res;
+
+			ret = io_uring_peek_cqe(ring, &cqe);
+			if (ret) {
+				fprintf(stderr, "peek failed: %d\n", ret);
+				return ret;
+			}
+			res = cqe->res;
+			io_uring_cqe_seen(ring, cqe);
+			if (res == 0) {
+				done = true;
+			} else if (done) {
+				continue;
+			} else if (res < 0 || res != 1) {
+				fprintf(stderr, "bad read: %d\n", cqe->res);
+				return -1;
+			} else {
+				++expected;
+				++count_ok;
+			}
+		}
+		ret = 0;
+		for (i = 0; i < count_ok; i++) {
+			if (buff[i] == 1) {
+				count_1++;
+			} else if (buff[i] == 0) {
+				count_0++;
+			} else {
+				fprintf(stderr, "odd read %d\n", (int)buff[i]);
+				ret = -1;
+				break;
+			}
+		}
+		if (labs(count_1 - count_0) > 1) {
+			fprintf(stderr, "inconsistent reads, got 0s:%d 1s:%d\n",
+				count_0, count_1);
+			ret = -1;
+		}
+		current = lseek(fd, 0, SEEK_CUR);
+		if (current != expected) {
+			fprintf(stderr,
+				"f_pos incorrect, expected %ld have %ld\n",
+				expected,
+				current);
+			ret = -1;
+		}
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+
+
+static int test_write(struct io_uring *ring, bool async, bool link)
+{
+	int ret, fd, i;
+	struct io_uring_sqe *sqe;
+	struct io_uring_cqe *cqe;
+	bool fail = false;
+	loff_t current;
+	const char *data = "ABCD";
+	int const n_data = strlen(data);
+	char readbuff[QUEUE_SIZE+1];
+
+	fd = open(".test_write", O_RDWR | O_CREAT, 0644);
+	unlink(".test_write");
+	assert(fd >= 0);
+
+	for (i = 0; i < QUEUE_SIZE; ++i) {
+		sqe = io_uring_get_sqe(ring);
+		if (!sqe) {
+			fprintf(stderr, "no sqe\n");
+			return -1;
+		}
+		io_uring_prep_write(sqe, fd, data + (i % n_data), 1, -1);
+		sqe->user_data = 1;
+		if (async)
+			sqe->flags |= IOSQE_ASYNC;
+		if (link && i != QUEUE_SIZE - 1)
+			sqe->flags |= IOSQE_IO_LINK;
+	}
+	ret = io_uring_submit_and_wait(ring, QUEUE_SIZE);
+	if (ret != QUEUE_SIZE) {
+		fprintf(stderr, "submit failed: %d\n", ret);
+		return 1;
+	}
+	for (i = 0; i < QUEUE_SIZE; ++i) {
+		int res;
+
+		ret = io_uring_peek_cqe(ring, &cqe);
+		res = cqe->res;
+		if (ret) {
+			fprintf(stderr, "peek failed: %d\n", ret);
+			return ret;
+		}
+		io_uring_cqe_seen(ring, cqe);
+		if (!fail && res != 1) {
+			fprintf(stderr, "bad result %d\n", res);
+			fail = true;
+		}
+	}
+	current = lseek(fd, 0, SEEK_CUR);
+	if (current != QUEUE_SIZE) {
+		fprintf(stderr,
+			"f_pos incorrect, expected %ld have %d\n",
+			current,
+			QUEUE_SIZE);
+		fail = true;
+	}
+	current = lseek(fd, 0, SEEK_SET);
+	if (current != 0) {
+		perror("seek to start");
+		return -1;
+	}
+	ret = read(fd, readbuff, QUEUE_SIZE);
+	if (ret != QUEUE_SIZE) {
+		fprintf(stderr, "did not read enough: %d\n", ret);
+		return -1;
+	}
+	i = 0;
+	while (i < QUEUE_SIZE - n_data) {
+		if (strncmp(readbuff + i, data, n_data)) {
+			char bad[QUEUE_SIZE+1];
+
+			memcpy(bad, readbuff + i, n_data);
+			bad[n_data] = '\0';
+			fail = true;
+			fprintf(stderr, "unexpected data %s\n", bad);
+		}
+		i += n_data;
+	}
+
+	return fail ? -1 : 0;
+}
+
+
+int main(int argc, char *argv[])
+{
+	struct io_uring ring;
+	int ret;
+	int failed = 0;
+
+	if (argc > 1)
+		return 0;
+
+	ret = io_uring_queue_init(QUEUE_SIZE, &ring, 0);
+	if (ret) {
+		fprintf(stderr, "ring setup failed\n");
+		return 1;
+	}
+
+	for (int async = 0; async < 2; async++) {
+	for (int link = 0; link < 2; link++) {
+	for (int write = 0; write < 2; write++) {
+		fprintf(stderr, "*********\n");
+		ret = write
+			? test_write(&ring, !!async, !!link)
+			: test_read(&ring, !!async, !!link);
+		fprintf(stderr, "test %s async=%d link=%d:\t%s\n",
+			write ? "write":"read",
+			async, link, ret ? "failed" : "passed");
+
+		if (!link && ret) {
+			/* right now this doesn't actually work, and it
+			 * is still unclear if it should
+			 */
+			fprintf(stderr, "ignoring !link failure\n");
+		} else if (ret) {
+			failed = 1;
+		}
+	}
+	}
+	}
+	return failed ? -1 : 0;
+}

base-commit: 20bb37e0f828909742f845b8113b2bb7e1065cd1
-- 
2.30.2





[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux