[PATCH v2] t/io_uring: Vectored fixed buffer test support for nvme passthrough path

Xiaobing Li <[email protected]> Wed, 17 Sep 2025 06:58:38 +0000
Newsgroups org.kernel.vger.fio
Message-ID <[email protected]>
The current kernel NVMe passthrough path already supports vectored IO
when using fixed buffers, but fio has not yet adapted it. This patch
aims to add a corresponding test interface in fio.

Test results:

taskset -c 1 t/io_uring -b512 -d64 -c2 -s2 -p1 -F1 -B1 -O0 -n1 -V1 -u1 -r4 /dev/ng1n1
submitter=0, tid=6179, file=/dev/ng1n1, nfiles=1, node=-1
polled=1, fixedbufs=1, register_files=1, buffered=1, QD=64
Engine=io_uring, sq_ring=64, cq_ring=64
IOPS=289.78K, BW=141MiB/s, IOS/call=1/1
IOPS=294.68K, BW=143MiB/s, IOS/call=1/1
IOPS=295.26K, BW=144MiB/s, IOS/call=1/1
Exiting on timeout
Maximum IOPS=295.26K

taskset -c 1 t/io_uring -b512 -d64 -c2 -s2 -p1 -F1 -B1 -O0 -n1 -V0 -u1 -r4 /dev/ng1n1
submitter=0, tid=6183, file=/dev/ng1n1, nfiles=1, node=-1
polled=1, fixedbufs=1, register_files=1, buffered=1, QD=64
Engine=io_uring, sq_ring=64, cq_ring=64
IOPS=292.31K, BW=142MiB/s, IOS/call=1/1
IOPS=295.79K, BW=144MiB/s, IOS/call=1/1
IOPS=290.78K, BW=141MiB/s, IOS/call=1/1
Exiting on timeout
Maximum IOPS=295.79K

Signed-off-by: Xiaobing Li <[email protected]>
---
 t/io_uring.c | 41 +++++++++++++----------------------------
 1 file changed, 13 insertions(+), 28 deletions(-)

diff --git a/t/io_uring.c b/t/io_uring.c
index 581cfe53..2a5d0c81 100644
--- a/t/io_uring.c
+++ b/t/io_uring.c
@@ -146,14 +146,13 @@ static int random_io = 1;	/* random or sequential IO */
 static int register_ring = 1;	/* register ring */
 static int use_sync = 0;	/* use preadv2 */
 static int numa_placement = 0;	/* set to node of device */
+static int vectored = 0;	/* use vectored IO */
 static int pt = 0;		/* passthrough I/O or not */
 
 static unsigned long tsc_rate;
 
 #define TSC_RATE_FILE	"tsc-rate"
 
-static int vectored = 1;
-
 static float plist[] = { 1.0, 5.0, 10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0,
 			80.0, 90.0, 95.0, 99.0, 99.5, 99.9, 99.95, 99.99 };
 static int plist_len = 17;
@@ -461,28 +460,6 @@ retry:
 	return ret;
 }
 
-static void io_uring_probe(int fd)
-{
-	struct io_uring_probe *p;
-	int ret;
-
-	p = calloc(1, sizeof(*p) + 256 * sizeof(struct io_uring_probe_op));
-	if (!p)
-		return;
-
-	ret = syscall(__NR_io_uring_register, fd, IORING_REGISTER_PROBE, p, 256);
-	if (ret < 0)
-		goto out;
-
-	if (IORING_OP_READ > p->ops_len)
-		goto out;
-
-	if ((p->ops[IORING_OP_READ].flags & IO_URING_OP_SUPPORTED))
-		vectored = 0;
-out:
-	free(p);
-}
-
 static int io_uring_enter(struct submitter *s, unsigned int to_submit,
 			  unsigned int min_complete, unsigned int flags)
 {
@@ -630,6 +607,12 @@ static void init_io_pt(struct submitter *s, unsigned index)
 		sqe->uring_cmd_flags = IORING_URING_CMD_FIXED;
 		sqe->buf_index = 0;
 	}
+	if (vectored) {
+		sqe->cmd_op = NVME_URING_CMD_IO_VEC;
+		cmd->addr = (unsigned long) &s->iovecs[index];
+		cmd->data_len = 1;
+		sqe->buf_index = 0;
+	}
 	cmd->nsid = f->nsid;
 	cmd->opcode = 2;
 }
@@ -927,8 +910,6 @@ static int setup_ring(struct submitter *s)
 	}
 	s->ring_fd = s->enter_ring_fd = fd;
 
-	io_uring_probe(fd);
-
 	if (fixedbufs) {
 		struct rlimit rlim;
 
@@ -1521,11 +1502,12 @@ static void usage(char *argv, int status)
 		" -S <bool> : Use sync IO (preadv2), default %d\n"
 		" -X <bool> : Use registered ring %d\n"
 		" -P <bool> : Automatically place on device home node %d\n"
+		" -V <bool> : Vectored IO, default %d\n"
 		" -u <bool> : Use nvme-passthrough I/O, default %d\n",
 		argv, DEPTH, BATCH_SUBMIT, BATCH_COMPLETE, BS, polled,
 		fixedbufs, register_files, nthreads, !buffered, do_nop,
 		stats, runtime == 0 ? "unlimited" : runtime_str, random_io, aio,
-		use_sync, register_ring, numa_placement, pt);
+		use_sync, register_ring, numa_placement, vectored, pt);
 	exit(status);
 }
 
@@ -1584,7 +1566,7 @@ int main(int argc, char *argv[])
 	if (!do_nop && argc < 2)
 		usage(argv[0], 1);
 
-	while ((opt = getopt(argc, argv, "d:s:c:b:p:B:F:n:N:O:t:T:a:r:D:R:X:S:P:u:h?")) != -1) {
+	while ((opt = getopt(argc, argv, "d:s:c:b:p:B:F:n:N:O:t:T:a:r:D:R:X:S:P:V:u:h?")) != -1) {
 		switch (opt) {
 		case 'a':
 			aio = !!atoi(optarg);
@@ -1662,6 +1644,9 @@ int main(int argc, char *argv[])
 		case 'P':
 			numa_placement = !!atoi(optarg);
 			break;
+		case 'V':
+			vectored = !!atoi(optarg);
+			break;
 		case 'u':
 			pt = !!atoi(optarg);
 			break;
-- 
2.34.1