Add registered buffer copy support
Also add test cases:
regbuf-copy.c: test various register/unregister/copy functions
read-write.c: add operations on copied buffers
Signed-off-by: Jens Axboe <axboe@kernel.dk>
diff --git a/man/io_uring_copy_buffers.3 b/man/io_uring_copy_buffers.3
new file mode 100644
index 0000000..749fdec
--- /dev/null
+++ b/man/io_uring_copy_buffers.3
@@ -0,0 +1,64 @@
+.\" Copyright (C) 2024 Jens Axboe <axboe@kernel.dk>
+.\"
+.\" SPDX-License-Identifier: LGPL-2.0-or-later
+.\"
+.TH io_uring_copy_buffers 3 "September 12, 2024" "liburing-2.8" "liburing Manual"
+.SH NAME
+io_uring_copy_buffers \- Copies registered buffers between rings
+.SH SYNOPSIS
+.nf
+.B #include <liburing.h>
+.PP
+.BI "int io_uring_copy_buffers(struct io_uring *" dst ","
+.BI " struct io_uring * " src ");"
+.PP
+.fi
+.SH DESCRIPTION
+.PP
+The
+.BR io_uring_copy_buffers (3)
+function copies registered buffers from the ring indicated by
+.I src
+to the ring indicated by
+.I dst .
+Upon successful completion of this operation,
+.I src
+and
+.I dst
+will have the same set of registered buffers. This operation is identical to
+performing a
+.BR io_uring_register_buffers (3)
+operation on the
+.I dst
+ring, if the
+.I src
+ring previously had that same buffer registration operating done.
+
+The
+.I dst
+ring must not have any buffers currently registered. If buffers are currently
+registered on the destination ring, they must be unregistered with
+.BR io_uring_unregister_buffers (3)
+first.
+
+On success
+.BR io_uring_copy_buffers (3)
+returns 0.
+On failure, it returns
+.BR -errno ,
+specifically
+.TP
+.B -EBUSY
+The destination ring already has buffers registered.
+.TP
+.B -ENOMEM
+The kernel ran out of memory.
+.TP
+.B -ENXIO
+The source ring doesn't have any buffers registered.
+.SH SEE ALSO
+.BR io_uring_register (2),
+.BR io_uring_unregister_buffers (3),
+.BR io_uring_register_buffers (3),
+.BR io_uring_prep_read_fixed (3),
+.BR io_uring_prep_write_fixed (3)
diff --git a/man/io_uring_register_buffers.3 b/man/io_uring_register_buffers.3
index 00861d9..04576b4 100644
--- a/man/io_uring_register_buffers.3
+++ b/man/io_uring_register_buffers.3
@@ -99,6 +99,7 @@
.BR io_uring_register (2),
.BR io_uring_get_sqe (3),
.BR io_uring_unregister_buffers (3),
+.BR io_uring_copy_buffers (3),
.BR io_uring_register_buf_ring (3),
.BR io_uring_prep_read_fixed (3),
.BR io_uring_prep_write_fixed (3)
diff --git a/src/include/liburing.h b/src/include/liburing.h
index 0dc445d..7c198f3 100644
--- a/src/include/liburing.h
+++ b/src/include/liburing.h
@@ -196,6 +196,7 @@
unsigned min_wait,
sigset_t *sigmask);
+int io_uring_copy_buffers(struct io_uring *dst, struct io_uring *src);
int io_uring_register_buffers(struct io_uring *ring, const struct iovec *iovecs,
unsigned nr_iovecs);
int io_uring_register_buffers_tags(struct io_uring *ring,
diff --git a/src/include/liburing/io_uring.h b/src/include/liburing/io_uring.h
index 4c5a17e..3372e4f 100644
--- a/src/include/liburing/io_uring.h
+++ b/src/include/liburing/io_uring.h
@@ -609,6 +609,9 @@
IORING_REGISTER_CLOCK = 29,
+ /* copy registered buffers from source ring to current ring */
+ IORING_REGISTER_COPY_BUFFERS = 30,
+
/* this goes last */
IORING_REGISTER_LAST,
@@ -694,6 +697,16 @@
__u32 __resv[3];
};
+enum {
+ IORING_REGISTER_SRC_REGISTERED = 1,
+};
+
+struct io_uring_copy_buffers {
+ __u32 src_fd;
+ __u32 flags;
+ __u32 pad[6];
+};
+
struct io_uring_buf {
__u64 addr;
__u32 len;
diff --git a/src/liburing-ffi.map b/src/liburing-ffi.map
index f711692..12fb450 100644
--- a/src/liburing-ffi.map
+++ b/src/liburing-ffi.map
@@ -210,4 +210,5 @@
io_uring_register_clock;
io_uring_submit_and_wait_min_timeout;
io_uring_wait_cqes_min_timeout;
+ io_uring_copy_buffers;
} LIBURING_2.7;
diff --git a/src/liburing.map b/src/liburing.map
index 22498bc..b48b48f 100644
--- a/src/liburing.map
+++ b/src/liburing.map
@@ -101,4 +101,5 @@
io_uring_register_clock;
io_uring_submit_and_wait_min_timeout;
io_uring_wait_cqes_min_timeout;
+ io_uring_copy_buffers;
} LIBURING_2.7;
diff --git a/src/register.c b/src/register.c
index b370f07..c81bc4d 100644
--- a/src/register.c
+++ b/src/register.c
@@ -373,3 +373,17 @@
{
return do_register(ring, IORING_REGISTER_CLOCK, arg, 0);
}
+
+int io_uring_copy_buffers(struct io_uring *dst, struct io_uring *src)
+{
+ struct io_uring_copy_buffers buf = { .src_fd = src->ring_fd, };
+
+ if (src->int_flags & INT_FLAG_REG_REG_RING) {
+ buf.src_fd = src->enter_ring_fd;
+ buf.flags = IORING_REGISTER_SRC_REGISTERED;
+ } else {
+ buf.src_fd = src->ring_fd;
+ }
+
+ return do_register(dst, IORING_REGISTER_COPY_BUFFERS, &buf, 1);
+}
diff --git a/test/Makefile b/test/Makefile
index b1bc396..1e1a861 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -167,6 +167,7 @@
reg-fd-only.c \
reg-hint.c \
reg-reg-ring.c \
+ regbuf-copy.c \
regbuf-merge.c \
register-restrictions.c \
rename.c \
diff --git a/test/read-write.c b/test/read-write.c
index 27b0cc7..98c1edd 100644
--- a/test/read-write.c
+++ b/test/read-write.c
@@ -15,6 +15,7 @@
#include "helpers.h"
#include "liburing.h"
+#include "../src/syscall.h"
#define FILE_SIZE (256 * 1024)
#define BS 8192
@@ -23,6 +24,7 @@
static struct iovec *vecs;
static int no_read;
static int no_buf_select;
+static int no_buf_copy;
static int warned;
static int create_nonaligned_buffers(void)
@@ -42,9 +44,9 @@
return 0;
}
-static int __test_io(const char *file, struct io_uring *ring, int write,
- int buffered, int sqthread, int fixed, int nonvec,
- int buf_select, int seq, int exp_len)
+static int _test_io(const char *file, struct io_uring *ring, int write,
+ int buffered, int sqthread, int fixed, int nonvec,
+ int buf_select, int seq, int exp_len)
{
struct io_uring_sqe *sqe;
struct io_uring_cqe *cqe;
@@ -64,7 +66,7 @@
if (!buffered)
open_flags |= O_DIRECT;
- if (fixed) {
+ if (fixed == 1) {
ret = t_register_buffers(ring, vecs, BUFFERS);
if (ret == T_SETUP_SKIP)
return 0;
@@ -201,13 +203,6 @@
io_uring_cqe_seen(ring, cqe);
}
- if (fixed) {
- ret = io_uring_unregister_buffers(ring);
- if (ret) {
- fprintf(stderr, "buffer unreg failed: %d\n", ret);
- goto err;
- }
- }
if (sqthread) {
ret = io_uring_unregister_files(ring);
if (ret) {
@@ -229,6 +224,65 @@
close(fd);
return 1;
}
+
+static int __test_io(const char *file, struct io_uring *ring, int write,
+ int buffered, int sqthread, int fixed, int nonvec,
+ int buf_select, int seq, int exp_len)
+{
+ int ret;
+
+ ret = _test_io(file, ring, write, buffered, sqthread, fixed, nonvec,
+ buf_select, seq, exp_len);
+ if (ret)
+ return ret;
+
+ if (fixed) {
+ struct io_uring ring2;
+ int ring_flags = 0;
+
+ if (no_buf_copy)
+ return 0;
+ if (sqthread)
+ ring_flags = IORING_SETUP_SQPOLL;
+ ret = t_create_ring(64, &ring2, ring_flags);
+ if (ret == T_SETUP_SKIP)
+ return 0;
+ if (ret != T_SETUP_OK) {
+ fprintf(stderr, "ring create failed: %d\n", ret);
+ return 1;
+ }
+
+ ret = io_uring_copy_buffers(&ring2, ring);
+ if (ret) {
+ if (ret == -EINVAL) {
+ no_buf_copy = 1;
+ io_uring_queue_exit(&ring2);
+ return 0;
+ }
+ fprintf(stderr, "copy buffers: %d\n", ret);
+ return ret;
+ }
+ ret = _test_io(file, &ring2, write, buffered, sqthread, 2,
+ nonvec, buf_select, seq, exp_len);
+ if (ret)
+ return ret;
+
+ ret = io_uring_unregister_buffers(ring);
+ if (ret) {
+ fprintf(stderr, "buffer unreg failed: %d\n", ret);
+ return ret;
+ }
+ ret = io_uring_unregister_buffers(&ring2);
+ if (ret) {
+ fprintf(stderr, "buffer copy unreg failed: %d\n", ret);
+ return ret;
+ }
+ io_uring_queue_exit(&ring2);
+ }
+
+ return ret;
+}
+
static int test_io(const char *file, int write, int buffered, int sqthread,
int fixed, int nonvec, int exp_len)
{
diff --git a/test/regbuf-copy.c b/test/regbuf-copy.c
new file mode 100644
index 0000000..5080b31
--- /dev/null
+++ b/test/regbuf-copy.c
@@ -0,0 +1,187 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test buffer copying between rings
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/uio.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+#define NR_VECS 64
+#define BUF_SIZE 8192
+
+static int test(int reg_src, int reg_dst)
+{
+ struct iovec vecs[NR_VECS];
+ struct io_uring src, dst;
+ int ret, i;
+
+ ret = io_uring_queue_init(1, &src, 0);
+ if (ret) {
+ fprintf(stderr, "ring_init: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ ret = io_uring_queue_init(1, &dst, 0);
+ if (ret) {
+ fprintf(stderr, "ring_init: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ if (reg_src) {
+ ret = io_uring_register_ring_fd(&src);
+ if (ret < 0) {
+ if (ret == -EINVAL)
+ return T_EXIT_SKIP;
+ fprintf(stderr, "register ring: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ }
+ if (reg_dst) {
+ ret = io_uring_register_ring_fd(&dst);
+ if (ret < 0) {
+ if (ret == -EINVAL)
+ return T_EXIT_SKIP;
+ fprintf(stderr, "register ring: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ }
+
+ /* test fail with no buffers in src */
+ ret = io_uring_copy_buffers(&dst, &src);
+ if (ret == -EINVAL) {
+ /* no buffer copy support */
+ return T_EXIT_SKIP;
+ } else if (ret != -ENXIO) {
+ fprintf(stderr, "empty copy: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ for (i = 0; i < NR_VECS; i++) {
+ if (posix_memalign(&vecs[i].iov_base, 4096, BUF_SIZE))
+ return T_EXIT_FAIL;
+ vecs[i].iov_len = BUF_SIZE;
+ }
+
+ ret = io_uring_register_buffers(&src, vecs, NR_VECS);
+ if (ret < 0) {
+ if (ret == -ENOMEM)
+ return T_EXIT_SKIP;
+ return T_EXIT_FAIL;
+ }
+
+ /* copy should work now */
+ ret = io_uring_copy_buffers(&dst, &src);
+ if (ret) {
+ fprintf(stderr, "buffer copy: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ /* try copy again, should get -EBUSY */
+ ret = io_uring_copy_buffers(&dst, &src);
+ if (ret != -EBUSY) {
+ fprintf(stderr, "busy copy: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_unregister_buffers(&dst);
+ if (ret) {
+ fprintf(stderr, "dst unregister buffers: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_unregister_buffers(&dst);
+ if (ret != -ENXIO) {
+ fprintf(stderr, "dst unregister empty buffers: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_unregister_buffers(&src);
+ if (ret) {
+ fprintf(stderr, "src unregister buffers: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_register_buffers(&dst, vecs, NR_VECS);
+ if (ret < 0) {
+ fprintf(stderr, "register buffers dst; %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_copy_buffers(&src, &dst);
+ if (ret) {
+ fprintf(stderr, "buffer copy reverse: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_unregister_buffers(&dst);
+ if (ret) {
+ fprintf(stderr, "dst unregister buffers: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_unregister_buffers(&dst);
+ if (ret != -ENXIO) {
+ fprintf(stderr, "dst unregister empty buffers: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_unregister_buffers(&src);
+ if (ret) {
+ fprintf(stderr, "src unregister buffers: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ io_uring_queue_exit(&src);
+ io_uring_queue_exit(&dst);
+
+ for (i = 0; i < NR_VECS; i++)
+ free(vecs[i].iov_base);
+
+ return T_EXIT_PASS;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = test(0, 0);
+ if (ret == T_EXIT_SKIP) {
+ return T_EXIT_SKIP;
+ } else if (ret != T_EXIT_PASS) {
+ fprintf(stderr, "test 0 0 failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test(0, 1);
+ if (ret == T_EXIT_SKIP) {
+ return T_EXIT_SKIP;
+ } else if (ret != T_EXIT_PASS) {
+ fprintf(stderr, "test 0 1 failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test(1, 0);
+ if (ret == T_EXIT_SKIP) {
+ return T_EXIT_SKIP;
+ } else if (ret != T_EXIT_PASS) {
+ fprintf(stderr, "test 1 0 failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test(1, 1);
+ if (ret == T_EXIT_SKIP) {
+ return T_EXIT_SKIP;
+ } else if (ret != T_EXIT_PASS) {
+ fprintf(stderr, "test 1 1 failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ return T_EXIT_PASS;
+}