Skip to contentSkip to conversation

fsstress,fsx: add io_uring test and do some fix

PATCH Superseded Zorro Lang 2 participants 8 messages Jul 28, 2020 – Aug 2, 2020 io_uring

v1 of 5 Superseded Latest is v5 →

lore
Share & tools
lore

Zorro Lang

author
#0 lore raw

cover fsstress,fsx: add io_uring test and do some fix

This patchset tries to add new IO_URING test into fsstress [1/4] and fsx [4/4]. And then do some changes and bug fix by the way [2/4 and 3/4].

fsstress and fsx are important tools in xfstests to do filesystem I/Os test, lots of test cases use it. So add IO_URING operation into fsstress and fsx will help to cover IO_URING test from fs side.

I'm not an IO_URING expert, so cc io-uring@ list, please feel free to tell me if you find something wrong or have any suggestions to improve the test.

Thanks,
Zorro

author
#1 lore raw

PATCH 1/4 fsstress: add IO_URING read and write operations

IO_URING is a new feature of curent linux kernel, add basic IO_URING read/write into fsstess to cover this kind of IO testing.

---
 README                 |   4 +-
 configure.ac           |   1 +
 include/builddefs.in   |   1 +
 ltp/Makefile           |   5 ++
 ltp/fsstress.c         | 139 ++++++++++++++++++++++++++++++++++++++++-
 m4/Makefile            |   1 +
 m4/package_liburing.m4 |   4 ++
 7 files changed, 152 insertions(+), 3 deletions(-)
 create mode 100644 m4/package_liburing.m4
README +2 -2
index d0e23fcd..ae0f804d 100644
@@ -8,13 +8,13 @@ _______________________
 	sudo apt-get install xfslibs-dev uuid-dev libtool-bin \
 	e2fsprogs automake gcc libuuid1 quota attr libattr1-dev make \
 	libacl1-dev libaio-dev xfsprogs libgdbm-dev gawk fio dbench \
-	uuid-runtime python sqlite3
+	uuid-runtime python sqlite3 liburing-dev
   For Fedora, RHEL, or CentOS:
 	yum install acl attr automake bc dbench dump e2fsprogs fio \
 	gawk gcc indent libtool lvm2 make psmisc quota sed \
 	xfsdump xfsprogs \
 	libacl-devel libattr-devel libaio-devel libuuid-devel \
-	xfsprogs-devel btrfs-progs-devel python sqlite
+	xfsprogs-devel btrfs-progs-devel python sqlite liburing-devel
 	(Older distributions may require xfsprogs-qa-devel as well.)
 	(Note that for RHEL and CentOS, you may need the EPEL repo.)
 - run make
configure.ac +1 -0
index 4bb50b32..8922c47e 100644
@@ -61,6 +61,7 @@ AC_PACKAGE_NEED_ACLINIT_LIBACL
 
 AC_PACKAGE_WANT_GDBM
 AC_PACKAGE_WANT_AIO
+AC_PACKAGE_WANT_URING
 AC_PACKAGE_WANT_DMAPI
 AC_PACKAGE_WANT_LINUX_FIEMAP_H
 AC_PACKAGE_WANT_FALLOCATE
include/builddefs.in +1 -0
index e7894b1a..fded3230 100644
@@ -61,6 +61,7 @@ RPM_VERSION     = @rpm_version@
 ENABLE_SHARED = @enable_shared@
 HAVE_DB = @have_db@
 HAVE_AIO = @have_aio@
+HAVE_URING = @have_uring@
 HAVE_FALLOCATE = @have_fallocate@
 HAVE_OPEN_BY_HANDLE_AT = @have_open_by_handle_at@
 HAVE_DMAPI = @have_dmapi@
ltp/Makefile +5 -0
index ebf40336..198d930f 100644
@@ -24,6 +24,11 @@ LCFLAGS += -DAIO
 LLDLIBS += -laio -lpthread
 endif
 
+ifeq ($(HAVE_URING), true)
+LCFLAGS += -DURING
+LLDLIBS += -luring
+endif
+
 ifeq ($(HAVE_LIBBTRFSUTIL), true)
 LLDLIBS += -lbtrfsutil
 endif
ltp/fsstress.c +138 -1

187 lines not on this page · raw message

m4/Makefile +1 -0
index 7fbff822..0352534d 100644
@@ -14,6 +14,7 @@ LSRCFILES = \
 	package_dmapidev.m4 \
 	package_globals.m4 \
 	package_libcdev.m4 \
+	package_liburing.m4 \
 	package_ncurses.m4 \
 	package_pthread.m4 \
 	package_ssldev.m4 \
m4/package_liburing.m4 new file +4 -0
new file mode 100644
index 00000000..c92cc02a
@@ -0,0 +1,4 @@
+AC_DEFUN([AC_PACKAGE_WANT_URING],
+  [ AC_CHECK_HEADERS(liburing.h, [ have_uring=true ], [ have_uring=false ])
+    AC_SUBST(have_uring)
+  ])
Signature · 1 line
2.20.1
author
#2 lore raw

PATCH 2/4 fsstress: reduce the number of events when io_setup

In reply to Zorro Lang

The original number(128) of aio events for io_setup is a little big. When try to run lots of fsstress processes(e.g. -p 1000) always hit io_setup EAGAIN error, due to the nr_events exceeds the limit of available events. So reduce it from 128 to 64, to make more fsstress processes can do AIO test.

---
 ltp/fsstress.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
ltp/fsstress.c +4 -3
index 388ace50..a11206d4 100644
@@ -28,6 +28,7 @@
 #endif
 #ifdef AIO
 #include <libaio.h>
+#define AIO_ENTRIES	64
 io_context_t	io_ctx;
 #endif
 #ifdef URING
@@ -699,8 +700,8 @@ int main(int argc, char **argv)
 			}
 			procid = i;
 #ifdef AIO
-			if (io_setup(128, &io_ctx) != 0) {
-				fprintf(stderr, "io_setup failed");
+			if (io_setup(AIO_ENTRIES, &io_ctx) != 0) {
+				fprintf(stderr, "io_setup failed\n");
 				exit(1);
 			}
 #endif
@@ -714,7 +715,7 @@ int main(int argc, char **argv)
 				doproc();
 #ifdef AIO
 			if(io_destroy(io_ctx) != 0) {
-				fprintf(stderr, "io_destroy failed");
+				fprintf(stderr, "io_destroy failed\n");
 				return 1;
 			}
 #endif
Signature · 1 line
2.20.1
#3 lore raw

Re: [PATCH 2/4] fsstress: reduce the number of events when io_setup

Quoted 5 lines · Zorro Lang

The original number(128) of aio events for io_setup is a little big. When try to run lots of fsstress processes(e.g. -p 1000) always hit io_setup EAGAIN error, due to the nr_events exceeds the limit of available events. So reduce it from 128 to 64, to make more fsstress processes can do AIO test.

It looks to me as though there's only ever one request in flight. I'd just set it to 1.

Also, you've included another change not mentioned in your changelog. Please make sure the changelog matches what's done in the patch.

-Jeff

Quoted 38 lines
---
 ltp/fsstress.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
ltp/fsstress.c +4 -3
index 388ace50..a11206d4 100644
@@ -28,6 +28,7 @@
 #endif
 #ifdef AIO
 #include <libaio.h>
+#define AIO_ENTRIES	64
 io_context_t	io_ctx;
 #endif
 #ifdef URING
@@ -699,8 +700,8 @@ int main(int argc, char **argv)
 			}
 			procid = i;
 #ifdef AIO
-			if (io_setup(128, &io_ctx) != 0) {
-				fprintf(stderr, "io_setup failed");
+			if (io_setup(AIO_ENTRIES, &io_ctx) != 0) {
+				fprintf(stderr, "io_setup failed\n");
 				exit(1);
 			}
 #endif
@@ -714,7 +715,7 @@ int main(int argc, char **argv)
 				doproc();
 #ifdef AIO
 			if(io_destroy(io_ctx) != 0) {
-				fprintf(stderr, "io_destroy failed");
+				fprintf(stderr, "io_destroy failed\n");
 				return 1;
 			}
 #endif
author
#4 lore raw

Re: [PATCH 2/4] fsstress: reduce the number of events when io_setup

Quoted 8 lines · Jeff Moyer
Quoted 5 lines · Zorro Lang

The original number(128) of aio events for io_setup is a little big. When try to run lots of fsstress processes(e.g. -p 1000) always hit io_setup EAGAIN error, due to the nr_events exceeds the limit of available events. So reduce it from 128 to 64, to make more fsstress processes can do AIO test.

Quoted 5 lines

It looks to me as though there's only ever one request in flight. I'd just set it to 1.

Also, you've included another change not mentioned in your changelog. Please make sure the changelog matches what's done in the patch.

Thanks Jeff, I'll rewrite this patch:) Do you have any review points about those two IO_URING related patches (1/4 and 4/4), or it looks good to you?

Thanks,
Zorro

Quoted 42 lines

-Jeff

Quoted 38 lines
---
 ltp/fsstress.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
ltp/fsstress.c +4 -3
index 388ace50..a11206d4 100644
@@ -28,6 +28,7 @@
 #endif
 #ifdef AIO
 #include <libaio.h>
+#define AIO_ENTRIES	64
 io_context_t	io_ctx;
 #endif
 #ifdef URING
@@ -699,8 +700,8 @@ int main(int argc, char **argv)
 			}
 			procid = i;
 #ifdef AIO
-			if (io_setup(128, &io_ctx) != 0) {
-				fprintf(stderr, "io_setup failed");
+			if (io_setup(AIO_ENTRIES, &io_ctx) != 0) {
+				fprintf(stderr, "io_setup failed\n");
 				exit(1);
 			}
 #endif
@@ -714,7 +715,7 @@ int main(int argc, char **argv)
 				doproc();
 #ifdef AIO
 			if(io_destroy(io_ctx) != 0) {
-				fprintf(stderr, "io_destroy failed");
+				fprintf(stderr, "io_destroy failed\n");
 				return 1;
 			}
 #endif
author
#5 lore raw

PATCH 3/4 fsstress: fix memory leak in do_aio_rw

In reply to Zorro Lang

If io_submit or io_getevents fails, the do_aio_rw() won't free the "buf" and cause memory leak.

---
 ltp/fsstress.c | 31 ++++++++++++++++---------------
 1 file changed, 16 insertions(+), 15 deletions(-)
ltp/fsstress.c +16 -15
index a11206d4..410a2437 100644
@@ -2099,8 +2099,7 @@ do_aio_rw(int opno, long r, int flags)
 	if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) {
 		if (v)
 			printf("%d/%d: do_aio_rw - no filename\n", procid, opno);
-		free_pathname(&f);
-		return;
+		goto aio_out3;
 	}
 	fd = open_path(&f, flags|O_DIRECT);
 	e = fd < 0 ? errno : 0;
@@ -2109,16 +2108,13 @@ do_aio_rw(int opno, long r, int flags)
 		if (v)
 			printf("%d/%d: do_aio_rw - open %s failed %d\n",
 			       procid, opno, f.path, e);
-		free_pathname(&f);
-		return;
+		goto aio_out3;
 	}
 	if (fstat64(fd, &stb) < 0) {
 		if (v)
 			printf("%d/%d: do_aio_rw - fstat64 %s failed %d\n",
 			       procid, opno, f.path, errno);
-		free_pathname(&f);
-		close(fd);
-		return;
+		goto aio_out2;
 	}
 	inode_info(st, sizeof(st), &stb, v);
 	if (!iswrite && stb.st_size == 0) {
@@ -2150,6 +2146,12 @@ do_aio_rw(int opno, long r, int flags)
 	else if (len > diob.d_maxiosz)
 		len = diob.d_maxiosz;
 	buf = memalign(diob.d_mem, len);
+	if (!buf) {
+		if (v)
+			printf("%d/%d: do_aio_rw - memalign failed\n",
+			       procid, opno);
+		goto aio_out2;
+	}
 
 	if (iswrite) {
 		off = (off64_t)(lr % MIN(stb.st_size + (1024 * 1024), MAXFSIZE));
@@ -2166,27 +2168,26 @@ do_aio_rw(int opno, long r, int flags)
 		if (v)
 			printf("%d/%d: %s - io_submit failed %d\n",
 			       procid, opno, iswrite ? "awrite" : "aread", e);
-		free_pathname(&f);
-		close(fd);
-		return;
+		goto aio_out1;
 	}
 	if ((e = io_getevents(io_ctx, 1, 1, &event, NULL)) != 1) {
 		if (v)
 			printf("%d/%d: %s - io_getevents failed %d\n",
 			       procid, opno, iswrite ? "awrite" : "aread", e);
-		free_pathname(&f);
-		close(fd);
-		return;
+		goto aio_out1;
 	}
 
 	e = event.res != len ? event.res2 : 0;
-	free(buf);
 	if (v)
 		printf("%d/%d: %s %s%s [%lld,%d] %d\n",
 		       procid, opno, iswrite ? "awrite" : "aread",
 		       f.path, st, (long long)off, (int)len, e);
-	free_pathname(&f);
+ aio_out1:
+	free(buf);
+ aio_out2:
 	close(fd);
+ aio_out3:
+	free_pathname(&f);
 }
 #endif
 
Signature · 1 line
2.20.1
author
#6 lore raw

PATCH 4/4 fsx: add IO_URING test

In reply to Zorro Lang

New IO_URING test for fsx, use -U option to enable IO_URING test.

---
 ltp/fsx.c | 154 +++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 140 insertions(+), 14 deletions(-)
ltp/fsx.c +140 -14
index 7c76655a..1b7401a2 100644
@@ -34,6 +34,9 @@
 #ifdef AIO
 #include <libaio.h>
 #endif
+#ifdef URING
+#include <liburing.h>
+#endif
 #include <sys/syscall.h>
 
 #ifndef MAP_FILE
@@ -176,21 +179,17 @@ int	integrity = 0;			/* -i flag */
 int	fsxgoodfd = 0;
 int	o_direct;			/* -Z */
 int	aio = 0;
+int	uring = 0;
 int	mark_nr = 0;
 
 int page_size;
 int page_mask;
 int mmap_mask;
-#ifdef AIO
-int aio_rw(int rw, int fd, char *buf, unsigned len, unsigned offset);
+int fsx_rw(int rw, int fd, char *buf, unsigned len, unsigned offset);
 #define READ 0
 #define WRITE 1
-#define fsxread(a,b,c,d)	aio_rw(READ, a,b,c,d)
-#define fsxwrite(a,b,c,d)	aio_rw(WRITE, a,b,c,d)
-#else
-#define fsxread(a,b,c,d)	read(a,b,c)
-#define fsxwrite(a,b,c,d)	write(a,b,c)
-#endif
+#define fsxread(a,b,c,d)	fsx_rw(READ, a,b,c,d)
+#define fsxwrite(a,b,c,d)	fsx_rw(WRITE, a,b,c,d)
 
 const char *replayops = NULL;
 const char *recordops = NULL;
@@ -2242,7 +2241,7 @@ void
 usage(void)
 {
 	fprintf(stdout, "usage: %s",
-		"fsx [-dknqxABEFJLOWZ] [-b opnum] [-c Prob] [-g filldata] [-i logdev] [-j logid] [-l flen] [-m start:end] [-o oplen] [-p progressinterval] [-r readbdy] [-s style] [-t truncbdy] [-w writebdy] [-D startingop] [-N numops] [-P dirpath] [-S seed] fname\n\
+		"fsx [-dknqxBEFJLOWZ][-A|-U] [-b opnum] [-c Prob] [-g filldata] [-i logdev] [-j logid] [-l flen] [-m start:end] [-o oplen] [-p progressinterval] [-r readbdy] [-s style] [-t truncbdy] [-w writebdy] [-D startingop] [-N numops] [-P dirpath] [-S seed] fname\n\
 	-b opnum: beginning operation number (default 1)\n\
 	-c P: 1 in P chance of file close+open at each op (default infinity)\n\
 	-d: debug output for all operations\n\
@@ -2265,7 +2264,10 @@ usage(void)
 	-y synchronize changes to a file\n"
 
 #ifdef AIO
-"	-A: Use the AIO system calls\n"
+"	-A: Use the AIO system calls, -A excludes -U\n"
+#endif
+#ifdef URING
+"	-U: Use the IO_URING system calls, -U excludes -A\n"
 #endif
 "	-D startingop: debug output starting at specified operation\n"
 #ifdef HAVE_LINUX_FALLOC_H
@@ -2425,13 +2427,127 @@ out_error:
 	errno = -ret;
 	return -1;
 }
+#endif
+
+#ifdef URING
+struct io_uring ring;
+#define URING_ENTRIES	1024
+int
+uring_setup()
+{
+	int ret;
+
+	ret = io_uring_queue_init(URING_ENTRIES, &ring, 0);
+	if (ret != 0) {
+		fprintf(stderr, "uring_setup: io_uring_queue_init failed: %s\n",
+                        strerror(ret));
+                return -1;
+        }
+        return 0;
+}
 
-int aio_rw(int rw, int fd, char *buf, unsigned len, unsigned offset)
+int
+__uring_rw(int rw, int fd, char *buf, unsigned len, unsigned offset)
 {
+        struct io_uring_sqe	*sqe;
+        struct io_uring_cqe	*cqe;
 	int ret;
+	int res, res2 = 0;
+	char *p = buf;
+	unsigned l = len;
+	unsigned o = offset;
+
+	/*
+	 * Due to io_uring tries non-blocking IOs (especially read), that
+	 * always cause 'normal' short reading. To avoid this short read
+	 * fail, try to loop read/write (escpecilly read) data.
+	 */
+ uring_loop:
+	sqe = io_uring_get_sqe(&ring);
+	if (!sqe) {
+		fprintf(stderr, "uring_rw: io_uring_get_sqe failed: %s\n",
+		        strerror(errno));
+		return -1;
+        }
+
+	if (rw == READ) {
+		io_uring_prep_read(sqe, fd, p, l, o);
+	} else {
+		io_uring_prep_write(sqe, fd, p, l, o);
+	}
+
+	ret = io_uring_submit(&ring);
+	if (ret != 1) {
+		fprintf(stderr, "errcode=%d\n", -ret);
+		fprintf(stderr, "uring %s: io_uring_submit failed: %s\n",
+		        rw == READ ? "read":"write", strerror(-ret));
+		goto uring_error;
+	}
+
+	ret = io_uring_wait_cqe(&ring, &cqe);
+	if (ret < 0) {
+		if (ret == 0)
+			fprintf(stderr, "uring %s: no events available\n",
+			        rw == READ ? "read":"write");
+		else {
+			fprintf(stderr, "errcode=%d\n", -ret);
+			fprintf(stderr, "uring %s: io_uring_wait_cqe failed: %s\n",
+			        rw == READ ? "read":"write", strerror(-ret));
+		}
+		goto uring_error;
+	}
+	res = cqe->res;
+	io_uring_cqe_seen(&ring, cqe);
+
+	res2 += res;
+	if (len != res2) {
+		if (res > 0) {
+			o += res;
+			l -= res;
+			p += res;
+			if (l > 0)
+				goto uring_loop;
+		} else if (res < 0) {
+			ret = res;
+			fprintf(stderr, "errcode=%d\n", -ret);
+			fprintf(stderr, "uring %s: io_uring failed: %s\n",
+			        rw == READ ? "read":"write", strerror(-ret));
+			goto uring_error;
+		} else {
+			fprintf(stderr, "uring %s bad io length: %d instead of %u\n",
+			        rw == READ ? "read":"write", res2, len);
+		}
+	}
+	return res2;
+
+ uring_error:
+	/*
+	 * The caller expects error return in traditional libc
+	 * convention, i.e. -1 and the errno set to error.
+	 */
+	errno = -ret;
+	return -1;
+}
+#endif
+
+int fsx_rw(int rw, int fd, char *buf, unsigned len, unsigned offset)
+{
+	int ret = -1;
 
 	if (aio) {
+#ifdef AIO
 		ret = __aio_rw(rw, fd, buf, len, offset);
+#elif
+		fprintf(stderr, "io_rw: need AIO support!\n");
+		exit(111);
+#endif
+	} else if (uring) {
+#ifdef URING
+		ret = __uring_rw(rw, fd, buf, len, offset);
+#elif
+		fprintf(stderr, "io_rw: need IO_URING support!\n");
+		exit(111);
+#endif
 	} else {
 		if (rw == READ)
 			ret = read(fd, buf, len);
@@ -2441,8 +2557,6 @@ int aio_rw(int rw, int fd, char *buf, unsigned len, unsigned offset)
 	return ret;
 }
 
-#endif
-
 #define test_fallocate(mode) __test_fallocate(mode, #mode)
 
 int
@@ -2496,7 +2610,7 @@ main(int argc, char **argv)
 	setvbuf(stdout, (char *)0, _IOLBF, 0); /* line buffered stdout */
 
 	while ((ch = getopt_long(argc, argv,
-				 "b:c:dfg:i:j:kl:m:no:p:qr:s:t:w:xyABD:EFJKHzCILN:OP:RS:WXZ",
+				 "b:c:dfg:i:j:kl:m:no:p:qr:s:t:w:xyABD:EFJKHzCILN:OP:RS:UWXZ",
 				 longopts, NULL)) != EOF)
 		switch (ch) {
 		case 'b':
@@ -2604,6 +2718,9 @@ main(int argc, char **argv)
 		case 'A':
 		        aio = 1;
 			break;
+		case 'U':
+		        uring = 1;
+			break;
 		case 'D':
 			debugstart = getnum(optarg, &endp);
 			if (debugstart < 1)
@@ -2694,6 +2811,11 @@ main(int argc, char **argv)
 	if (argc != 1)
 		usage();
 
+	if (aio && uring) {
+		fprintf(stderr, "-A and -U shouldn't be used together\n");
+		usage();
+	}
+
 	if (integrity && !dirpath) {
 		fprintf(stderr, "option -i <logdev> requires -P <dirpath>\n");
 		usage();
@@ -2784,6 +2906,10 @@ main(int argc, char **argv)
 	if (aio) 
 		aio_setup();
 #endif
+#ifdef URING
+	if (uring)
+		uring_setup();
+#endif
 
 	if (!(o_flags & O_TRUNC)) {
 		off_t ret;
Signature · 1 line
2.20.1
author
#7 lore raw

Re: [PATCH 4/4] fsx: add IO_URING test

Quoted 110 lines · Zorro Lang

New IO_URING test for fsx, use -U option to enable IO_URING test.

---
 ltp/fsx.c | 154 +++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 140 insertions(+), 14 deletions(-)
ltp/fsx.c +46 -11
index 7c76655a..1b7401a2 100644
@@ -34,6 +34,9 @@
 #ifdef AIO
 #include <libaio.h>
 #endif
+#ifdef URING
+#include <liburing.h>
+#endif
 #include <sys/syscall.h>
 
 #ifndef MAP_FILE
@@ -176,21 +179,17 @@ int	integrity = 0;			/* -i flag */
 int	fsxgoodfd = 0;
 int	o_direct;			/* -Z */
 int	aio = 0;
+int	uring = 0;
 int	mark_nr = 0;
 
 int page_size;
 int page_mask;
 int mmap_mask;
-#ifdef AIO
-int aio_rw(int rw, int fd, char *buf, unsigned len, unsigned offset);
+int fsx_rw(int rw, int fd, char *buf, unsigned len, unsigned offset);
 #define READ 0
 #define WRITE 1
-#define fsxread(a,b,c,d)	aio_rw(READ, a,b,c,d)
-#define fsxwrite(a,b,c,d)	aio_rw(WRITE, a,b,c,d)
-#else
-#define fsxread(a,b,c,d)	read(a,b,c)
-#define fsxwrite(a,b,c,d)	write(a,b,c)
-#endif
+#define fsxread(a,b,c,d)	fsx_rw(READ, a,b,c,d)
+#define fsxwrite(a,b,c,d)	fsx_rw(WRITE, a,b,c,d)
 
 const char *replayops = NULL;
 const char *recordops = NULL;
@@ -2242,7 +2241,7 @@ void
 usage(void)
 {
 	fprintf(stdout, "usage: %s",
-		"fsx [-dknqxABEFJLOWZ] [-b opnum] [-c Prob] [-g filldata] [-i logdev] [-j logid] [-l flen] [-m start:end] [-o oplen] [-p progressinterval] [-r readbdy] [-s style] [-t truncbdy] [-w writebdy] [-D startingop] [-N numops] [-P dirpath] [-S seed] fname\n\
+		"fsx [-dknqxBEFJLOWZ][-A|-U] [-b opnum] [-c Prob] [-g filldata] [-i logdev] [-j logid] [-l flen] [-m start:end] [-o oplen] [-p progressinterval] [-r readbdy] [-s style] [-t truncbdy] [-w writebdy] [-D startingop] [-N numops] [-P dirpath] [-S seed] fname\n\
 	-b opnum: beginning operation number (default 1)\n\
 	-c P: 1 in P chance of file close+open at each op (default infinity)\n\
 	-d: debug output for all operations\n\
@@ -2265,7 +2264,10 @@ usage(void)
 	-y synchronize changes to a file\n"
 
 #ifdef AIO
-"	-A: Use the AIO system calls\n"
+"	-A: Use the AIO system calls, -A excludes -U\n"
+#endif
+#ifdef URING
+"	-U: Use the IO_URING system calls, -U excludes -A\n"
 #endif
 "	-D startingop: debug output starting at specified operation\n"
 #ifdef HAVE_LINUX_FALLOC_H
@@ -2425,13 +2427,127 @@ out_error:
 	errno = -ret;
 	return -1;
 }
+#endif
+
+#ifdef URING
+struct io_uring ring;
+#define URING_ENTRIES	1024
+int
+uring_setup()
+{
+	int ret;
+
+	ret = io_uring_queue_init(URING_ENTRIES, &ring, 0);
+	if (ret != 0) {
+		fprintf(stderr, "uring_setup: io_uring_queue_init failed: %s\n",
+                        strerror(ret));
+                return -1;
+        }
+        return 0;
+}
 
-int aio_rw(int rw, int fd, char *buf, unsigned len, unsigned offset)
+int
+__uring_rw(int rw, int fd, char *buf, unsigned len, unsigned offset)
 {
+        struct io_uring_sqe	*sqe;
+        struct io_uring_cqe	*cqe;
 	int ret;
+	int res, res2 = 0;
+	char *p = buf;
+	unsigned l = len;
+	unsigned o = offset;
+
+	/*
+	 * Due to io_uring tries non-blocking IOs (especially read), that
+	 * always cause 'normal' short reading. To avoid this short read
+	 * fail, try to loop read/write (escpecilly read) data.
+	 */
+ uring_loop:
+	sqe = io_uring_get_sqe(&ring);

ltp/fsx.c out_error: · line 2476

Quoted 10 lines
ltp/fsx.c +10 -0
+	if (!sqe) {
+		fprintf(stderr, "uring_rw: io_uring_get_sqe failed: %s\n",
+		        strerror(errno));
+		return -1;
+        }
+
+	if (rw == READ) {
+		io_uring_prep_read(sqe, fd, p, l, o);
+	} else {
+		io_uring_prep_write(sqe, fd, p, l, o);

I just found some old version liburing doesn't support io_uring_prep_read() and io_uring_prep_write() functions:

  # make
  ...
  /tmp/ccmrsUN8.o: In function `__uring_rw':
  /home/xfstests-zlang/ltp/fsx.c:2474: undefined reference to `io_uring_prep_read'
  /home/xfstests-zlang/ltp/fsx.c:2476: undefined reference to `io_uring_prep_write'
  collect2: error: ld returned 1 exit status
  ...
  # grep -rsn io_uring_prep_read /usr/include/liburing.h
  189:static inline void io_uring_prep_readv(struct io_uring_sqe *sqe, int fd,
  196:static inline void io_uring_prep_read_fixed(struct io_uring_sqe *sqe, int fd,
  # rpm -q liburing
  liburing-0.2-2.xxx.xxxx

I'll turn to use io_uring_prep_readv() and io_uring_prep_writev() to get better compatibility.

Thanks,
Zorro

Quoted 131 lines
ltp/fsx.c +84 -3
+	}
+
+	ret = io_uring_submit(&ring);
+	if (ret != 1) {
+		fprintf(stderr, "errcode=%d\n", -ret);
+		fprintf(stderr, "uring %s: io_uring_submit failed: %s\n",
+		        rw == READ ? "read":"write", strerror(-ret));
+		goto uring_error;
+	}
+
+	ret = io_uring_wait_cqe(&ring, &cqe);
+	if (ret < 0) {
+		if (ret == 0)
+			fprintf(stderr, "uring %s: no events available\n",
+			        rw == READ ? "read":"write");
+		else {
+			fprintf(stderr, "errcode=%d\n", -ret);
+			fprintf(stderr, "uring %s: io_uring_wait_cqe failed: %s\n",
+			        rw == READ ? "read":"write", strerror(-ret));
+		}
+		goto uring_error;
+	}
+	res = cqe->res;
+	io_uring_cqe_seen(&ring, cqe);
+
+	res2 += res;
+	if (len != res2) {
+		if (res > 0) {
+			o += res;
+			l -= res;
+			p += res;
+			if (l > 0)
+				goto uring_loop;
+		} else if (res < 0) {
+			ret = res;
+			fprintf(stderr, "errcode=%d\n", -ret);
+			fprintf(stderr, "uring %s: io_uring failed: %s\n",
+			        rw == READ ? "read":"write", strerror(-ret));
+			goto uring_error;
+		} else {
+			fprintf(stderr, "uring %s bad io length: %d instead of %u\n",
+			        rw == READ ? "read":"write", res2, len);
+		}
+	}
+	return res2;
+
+ uring_error:
+	/*
+	 * The caller expects error return in traditional libc
+	 * convention, i.e. -1 and the errno set to error.
+	 */
+	errno = -ret;
+	return -1;
+}
+#endif
+
+int fsx_rw(int rw, int fd, char *buf, unsigned len, unsigned offset)
+{
+	int ret = -1;
 
 	if (aio) {
+#ifdef AIO
 		ret = __aio_rw(rw, fd, buf, len, offset);
+#elif
+		fprintf(stderr, "io_rw: need AIO support!\n");
+		exit(111);
+#endif
+	} else if (uring) {
+#ifdef URING
+		ret = __uring_rw(rw, fd, buf, len, offset);
+#elif
+		fprintf(stderr, "io_rw: need IO_URING support!\n");
+		exit(111);
+#endif
 	} else {
 		if (rw == READ)
 			ret = read(fd, buf, len);
@@ -2441,8 +2557,6 @@ int aio_rw(int rw, int fd, char *buf, unsigned len, unsigned offset)
 	return ret;
 }
 
-#endif
-
 #define test_fallocate(mode) __test_fallocate(mode, #mode)
 
 int
@@ -2496,7 +2610,7 @@ main(int argc, char **argv)
 	setvbuf(stdout, (char *)0, _IOLBF, 0); /* line buffered stdout */
 
 	while ((ch = getopt_long(argc, argv,
-				 "b:c:dfg:i:j:kl:m:no:p:qr:s:t:w:xyABD:EFJKHzCILN:OP:RS:WXZ",
+				 "b:c:dfg:i:j:kl:m:no:p:qr:s:t:w:xyABD:EFJKHzCILN:OP:RS:UWXZ",
 				 longopts, NULL)) != EOF)
 		switch (ch) {
 		case 'b':
@@ -2604,6 +2718,9 @@ main(int argc, char **argv)
 		case 'A':
 		        aio = 1;
 			break;
+		case 'U':
+		        uring = 1;
+			break;
 		case 'D':
 			debugstart = getnum(optarg, &endp);
 			if (debugstart < 1)
@@ -2694,6 +2811,11 @@ main(int argc, char **argv)
 	if (argc != 1)
 		usage();
 
+	if (aio && uring) {
+		fprintf(stderr, "-A and -U shouldn't be used together\n");
+		usage();
+	}
+
 	if (integrity && !dirpath) {
 		fprintf(stderr, "option -i <logdev> requires -P <dirpath>\n");
 		usage();
@@ -2784,6 +2906,10 @@ main(int argc, char **argv)
 	if (aio) 
 		aio_setup();
 #endif
+#ifdef URING
+	if (uring)
+		uring_setup();
+#endif
 
 	if (!(o_flags & O_TRUNC)) {
 		off_t ret;
Signature · 1 line
2.20.1