* [PATCH 3/5] test/buffer-register: add buffer registration test
2020-12-14 21:09 [PATCH 0/5] liburing: buffer registration enhancements Bijan Mottahedeh
2020-12-14 21:09 ` [PATCH 1/5] liburing: support buffer registration updates Bijan Mottahedeh
2020-12-14 21:09 ` [PATCH 2/5] liburing: support buffer registration sharing Bijan Mottahedeh
@ 2020-12-14 21:09 ` Bijan Mottahedeh
2020-12-14 21:09 ` [PATCH 4/5] test/buffer-update: add buffer registration update test Bijan Mottahedeh
2020-12-14 21:09 ` [PATCH 5/5] test/buffer-share: add buffer registration sharing test Bijan Mottahedeh
4 siblings, 0 replies; 6+ messages in thread
From: Bijan Mottahedeh @ 2020-12-14 21:09 UTC (permalink / raw)
To: axboe, io-uring
Signed-off-by: Bijan Mottahedeh <[email protected]>
---
.gitignore | 1 +
test/Makefile | 2 +
test/buffer-register.c | 701 +++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 704 insertions(+)
create mode 100644 test/buffer-register.c
diff --git a/.gitignore b/.gitignore
index 360064a..9d30cf7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,6 +30,7 @@
/test/across-fork
/test/b19062a56726-test
/test/b5837bd5311d-test
+/test/buffer-register
/test/ce593a6c480a-test
/test/close-opath
/test/config.local
diff --git a/test/Makefile b/test/Makefile
index 6aa1788..293df3d 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -29,6 +29,7 @@ test_targets += \
across-fork splice \
b19062a56726-test \
b5837bd5311d-test \
+ buffer-register \
ce593a6c480a-test \
close-opath \
connect \
@@ -151,6 +152,7 @@ test_srcs := \
across-fork.c \
b19062a56726-test.c \
b5837bd5311d-test.c \
+ buffer-register.c \
ce593a6c480a-test.c \
close-opath.c \
connect.c \
diff --git a/test/buffer-register.c b/test/buffer-register.c
new file mode 100644
index 0000000..35176a1
--- /dev/null
+++ b/test/buffer-register.c
@@ -0,0 +1,701 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: run various buffer registration tests
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "liburing.h"
+
+static int pagesize;
+
+static void free_bufs(struct iovec *iovs, int nr_bufs)
+{
+ int i;
+
+ if (!iovs)
+ return;
+
+ for (i = 0; i < nr_bufs; i++)
+ if (iovs[i].iov_base)
+ free(iovs[i].iov_base);
+
+ free(iovs);
+}
+
+static struct iovec *alloc_bufs(int nr_bufs, int extra)
+{
+ struct iovec *iovs;
+ void *buf;
+ int i;
+
+ iovs = calloc(nr_bufs + extra, sizeof(struct iovec));
+ if (!iovs) {
+ perror("malloc");
+ return NULL;
+ }
+
+ for (i = 0; i < nr_bufs; i++) {
+ buf = malloc(pagesize);
+ if (!buf) {
+ perror("malloc");
+ break;
+ }
+ iovs[i].iov_base = buf;
+ iovs[i].iov_len = pagesize;
+ }
+
+ /* extra buffers already set to zero */
+
+ if (i < nr_bufs) {
+ free_bufs(iovs, nr_bufs);
+ iovs = NULL;
+ }
+
+ return iovs;
+}
+
+static int test_shrink(struct io_uring *ring)
+{
+ int ret, off;
+ struct iovec *iovs, iov = {0};
+
+ iovs = alloc_bufs(50, 0);
+ if (!iovs)
+ return 1;
+ ret = io_uring_register_buffers(ring, iovs, 50);
+ if (ret) {
+ fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ off = 0;
+ do {
+ ret = io_uring_register_buffers_update(ring, off, &iov, 1);
+ if (ret != 1) {
+ if (off == 50 && ret == -EINVAL)
+ break;
+ fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__,
+ ret);
+ break;
+ }
+ off++;
+ } while (1);
+
+ ret = io_uring_unregister_buffers(ring);
+ if (ret) {
+ fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ free_bufs(iovs, 50);
+ return 0;
+err:
+ free_bufs(iovs, 50);
+ return 1;
+}
+
+static int test_grow(struct io_uring *ring)
+{
+ int ret, off, i;
+ struct iovec *iovs, *ups = NULL;
+
+ iovs = alloc_bufs(50, 250);
+ if (!iovs)
+ return 1;
+ ret = io_uring_register_buffers(ring, iovs, 300);
+ if (ret) {
+ fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ ups = alloc_bufs(250, 0);
+ if (!ups)
+ goto err;
+ i = 0;
+ off = 50;
+ do {
+ ret = io_uring_register_buffers_update(ring, off, ups + i, 1);
+ if (ret != 1) {
+ if (off == 300 && ret == -EINVAL)
+ break;
+ fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__,
+ ret);
+ break;
+ }
+ if (off >= 300) {
+ fprintf(stderr, "%s: Succeeded beyond end-of-list?\n",
+ __FUNCTION__);
+ goto err;
+ }
+ off++;
+ i++;
+ } while (1);
+
+ ret = io_uring_unregister_buffers(ring);
+ if (ret) {
+ fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ free_bufs(iovs, 300);
+ free_bufs(ups, 250);
+ return 0;
+err:
+ free_bufs(iovs, 300);
+ free_bufs(ups, 250);
+ return 1;
+}
+
+static int test_replace_all(struct io_uring *ring)
+{
+ struct iovec *iovs, *ups = NULL;
+ int ret;
+
+ iovs = alloc_bufs(100, 0);
+ if (!iovs)
+ return 1;
+ ret = io_uring_register_buffers(ring, iovs, 100);
+ if (ret) {
+ fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ ups = alloc_bufs(0, 100);
+
+ ret = io_uring_register_buffers_update(ring, 0, ups, 100);
+ if (ret != 100) {
+ fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ ret = io_uring_unregister_buffers(ring);
+ if (ret) {
+ fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ free_bufs(iovs, 100);
+ free_bufs(ups, 100);
+ return 0;
+err:
+ free_bufs(iovs, 100);
+ free_bufs(ups, 100);
+ return 1;
+}
+
+static int test_replace(struct io_uring *ring)
+{
+ struct iovec *iovs, *ups = NULL;
+ int ret;
+
+ iovs = alloc_bufs(100, 0);
+ if (!iovs)
+ return 1;
+ ret = io_uring_register_buffers(ring, iovs, 100);
+ if (ret) {
+ fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ ups = alloc_bufs(10, 0);
+ if (!ups)
+ goto err;
+ ret = io_uring_register_buffers_update(ring, 90, ups, 10);
+ if (ret != 10) {
+ fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ ret = io_uring_unregister_buffers(ring);
+ if (ret) {
+ fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ free_bufs(iovs, 100);
+ free_bufs(ups, 10);
+ return 0;
+err:
+ free_bufs(iovs, 100);
+ free_bufs(ups, 10);
+ return 1;
+}
+
+static int test_removals(struct io_uring *ring)
+{
+ struct iovec *iovs, *ups = NULL;
+ int ret;
+
+ iovs = alloc_bufs(100, 0);
+ if (!iovs)
+ return 1;
+ ret = io_uring_register_buffers(ring, iovs, 100);
+ if (ret) {
+ fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ ups = alloc_bufs(0, 10);
+ if (!ups)
+ goto err;
+ ret = io_uring_register_buffers_update(ring, 50, ups, 10);
+ if (ret != 10) {
+ fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ ret = io_uring_unregister_buffers(ring);
+ if (ret) {
+ fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ free_bufs(iovs, 100);
+ free_bufs(ups, 0);
+ return 0;
+err:
+ free_bufs(iovs, 100);
+ free_bufs(ups, 0);
+ return 1;
+}
+
+static int test_additions(struct io_uring *ring)
+{
+ struct iovec *iovs, *ups = NULL;
+ int ret;
+
+ iovs = alloc_bufs(100, 100);
+ if (!iovs)
+ return 1;
+ ret = io_uring_register_buffers(ring, iovs, 200);
+ if (ret) {
+ fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ ups = alloc_bufs(2, 0);
+ if (!ups)
+ goto err;
+ ret = io_uring_register_buffers_update(ring, 100, ups, 2);
+ if (ret != 2) {
+ fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ ret = io_uring_unregister_buffers(ring);
+ if (ret) {
+ fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ free_bufs(iovs, 100);
+ free_bufs(ups, 2);
+ return 0;
+err:
+ free_bufs(iovs, 100);
+ free_bufs(ups, 2);
+ return 1;
+}
+
+static int test_sparse(struct io_uring *ring)
+{
+ struct iovec *iovs;
+ int ret;
+
+ iovs = alloc_bufs(100, 100);
+ if (!iovs)
+ return 1;
+ ret = io_uring_register_buffers(ring, iovs, 200);
+ if (ret) {
+ fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+ ret = io_uring_unregister_buffers(ring);
+ if (ret) {
+ fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+ free_bufs(iovs, 100);
+ return 0;
+err:
+ free_bufs(iovs, 100);
+ return 1;
+}
+
+static int test_basic_many(struct io_uring *ring)
+{
+ struct iovec *iovs;
+ int ret;
+
+ iovs = alloc_bufs(1024, 0);
+ ret = io_uring_register_buffers(ring, iovs, 768);
+ if (ret) {
+ fprintf(stderr, "%s: register %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+ ret = io_uring_unregister_buffers(ring);
+ if (ret) {
+ fprintf(stderr, "%s: unregister %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+ free_bufs(iovs, 1024);
+ return 0;
+err:
+ free_bufs(iovs, 1024);
+ return 1;
+}
+
+static int test_basic(struct io_uring *ring)
+{
+ struct iovec *iovs;
+ int ret;
+
+ iovs = alloc_bufs(100, 0);
+ ret = io_uring_register_buffers(ring, iovs, 100);
+ if (ret) {
+ fprintf(stderr, "%s: register %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+ ret = io_uring_unregister_buffers(ring);
+ if (ret) {
+ fprintf(stderr, "%s: unregister %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+ free_bufs(iovs, 100);
+ return 0;
+err:
+ free_bufs(iovs, 100);
+ return 1;
+}
+
+/*
+ * Register 0 buffers, but reserve space for 10. Then add one buffer.
+ */
+static int test_zero(struct io_uring *ring)
+{
+ struct iovec *iovs, *ups = NULL;
+ int ret;
+
+ iovs = alloc_bufs(0, 10);
+ if (!iovs)
+ return 1;
+ ret = io_uring_register_buffers(ring, iovs, 10);
+ if (ret) {
+ fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ ups = alloc_bufs(1, 0);
+ if (!ups)
+ return 1;
+ ret = io_uring_register_buffers_update(ring, 0, ups, 1);
+ if (ret != 1) {
+ fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ ret = io_uring_unregister_buffers(ring);
+ if (ret) {
+ fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ free_bufs(iovs, 0);
+ free_bufs(ups, 1);
+ return 0;
+err:
+ free_bufs(iovs, 0);
+ free_bufs(ups, 1);
+ return 1;
+}
+
+static int test_fixed_read_write(struct io_uring *ring, int fd,
+ struct iovec *iovs, int index)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct iovec *iov;
+ int ret;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: failed to get sqe\n", __FUNCTION__);
+ return 1;
+ }
+ iov = &iovs[index];
+ io_uring_prep_write_fixed(sqe, fd, iov->iov_base, iov->iov_len, 0,
+ index);
+ sqe->user_data = 1;
+
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ fprintf(stderr, "%s: got %d, wanted 1\n", __FUNCTION__, ret);
+ return 1;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "%s: io_uring_wait_cqe=%d\n", __FUNCTION__,
+ ret);
+ return 1;
+ }
+ if (cqe->res != pagesize) {
+ fprintf(stderr, "%s: write cqe->res=%d\n", __FUNCTION__,
+ cqe->res);
+ return 1;
+ }
+ io_uring_cqe_seen(ring, cqe);
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: failed to get sqe\n", __FUNCTION__);
+ return 1;
+ }
+ iov = &iovs[index + 1];
+ io_uring_prep_read_fixed(sqe, fd, iov->iov_base, iov->iov_len, 0,
+ index + 1);
+ sqe->user_data = 2;
+
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ fprintf(stderr, "%s: got %d, wanted 1\n", __FUNCTION__, ret);
+ return 1;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "%s: io_uring_wait_cqe=%d\n", __FUNCTION__,
+ ret);
+ return 1;
+ }
+ if (cqe->res != pagesize) {
+ fprintf(stderr, "%s: read cqe->res=%d\n", __FUNCTION__,
+ cqe->res);
+ return 1;
+ }
+ io_uring_cqe_seen(ring, cqe);
+
+ return 0;
+}
+
+/*
+ * Register 1k of sparse buffers, update one at a random spot, then do some
+ * file IO to verify it works.
+ */
+static int test_huge(struct io_uring *ring)
+{
+ struct iovec *iovs, *ups = NULL;
+ int ret, fd;
+
+ iovs = alloc_bufs(0, UIO_MAXIOV);
+ if (!iovs)
+ return 1;
+ ret = io_uring_register_buffers(ring, iovs, UIO_MAXIOV);
+ if (ret) {
+ fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+ fd = open(".reg.768", O_RDWR | O_CREAT, 0644);
+ if (fd < 0) {
+ fprintf(stderr, "%s: open=%d\n", __FUNCTION__, errno);
+ goto err;
+ }
+
+ ups = alloc_bufs(2, 0);
+ if (!ups) {
+ fprintf(stderr, "%s: malloc=%d\n", __FUNCTION__, errno);
+ goto err;
+ }
+ memset(ups[0].iov_base, 0x5a, pagesize);
+
+ ret = io_uring_register_buffers_update(ring, 768, ups, 2);
+ if (ret != 2) {
+ fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ iovs[768].iov_base = ups[0].iov_base;
+ iovs[768].iov_len = pagesize;
+ iovs[769].iov_base = ups[1].iov_base;
+ iovs[769].iov_len = pagesize;
+
+ if (test_fixed_read_write(ring, fd, iovs, 768))
+ goto err;
+
+ if (memcmp(ups[0].iov_base, ups[1].iov_base, pagesize)) {
+ fprintf(stderr, "%s: data mismatch\n", __FUNCTION__);
+ goto err;
+ }
+
+ ret = io_uring_unregister_buffers(ring);
+ if (ret) {
+ fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ if (fd != -1) {
+ close(fd);
+ unlink(".reg.768");
+ }
+ free_bufs(iovs, 0);
+ free_bufs(ups, 2);
+ return 0;
+err:
+ free_bufs(iovs, 0);
+ free_bufs(ups, 2);
+ return 1;
+}
+
+static int test_sparse_updates(void)
+{
+ struct iovec *iovs, *ups = NULL;
+ struct io_uring ring;
+ int ret, i;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "queue_init: %d\n", ret);
+ return ret;
+ }
+
+ iovs = alloc_bufs(0, 256);
+
+ ret = io_uring_register_buffers(&ring, iovs, 256);
+ if (ret) {
+ fprintf(stderr, "buffer_register: %d\n", ret);
+ return ret;
+ }
+
+ ups = alloc_bufs(1, 0);
+ for (i = 0; i < 256; i++) {
+ ret = io_uring_register_buffers_update(&ring, i, ups, 1);
+ if (ret != 1) {
+ fprintf(stderr, "buffer_update: %d\n", ret);
+ return ret;
+ }
+ }
+ io_uring_unregister_buffers(&ring);
+
+ for (i = 0; i < 256; i++)
+ iovs[i] = ups[0];
+
+ ret = io_uring_register_buffers(&ring, iovs, 256);
+ if (ret) {
+ fprintf(stderr, "buffer_register: %d\n", ret);
+ return ret;
+ }
+
+ free_bufs(ups, 1);
+ ups = alloc_bufs(0, 1);
+ for (i = 0; i < 256; i++) {
+ ret = io_uring_register_buffers_update(&ring, i, ups, 1);
+ if (ret != 1) {
+ fprintf(stderr, "buffer_update: %d\n", ret);
+ goto done;
+ }
+ }
+ ret = 0;
+done:
+ free_bufs(ups, 0);
+ io_uring_unregister_buffers(&ring);
+
+ io_uring_queue_exit(&ring);
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ int ret;
+
+ if (argc > 1)
+ return 0;
+
+ pagesize = getpagesize();
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ printf("ring setup failed\n");
+ return 1;
+ }
+
+ ret = test_basic(&ring);
+ if (ret) {
+ printf("test_basic failed\n");
+ return ret;
+ }
+
+ ret = test_basic_many(&ring);
+ if (ret) {
+ printf("test_basic_many failed\n");
+ return ret;
+ }
+
+ ret = test_sparse(&ring);
+ if (ret) {
+ printf("test_sparse failed\n");
+ return ret;
+ }
+
+ ret = test_additions(&ring);
+ if (ret) {
+ printf("test_additions failed\n");
+ return ret;
+ }
+
+ ret = test_removals(&ring);
+ if (ret) {
+ printf("test_removals failed\n");
+ return ret;
+ }
+
+ ret = test_replace(&ring);
+ if (ret) {
+ printf("test_replace failed\n");
+ return ret;
+ }
+
+ ret = test_replace_all(&ring);
+ if (ret) {
+ printf("test_replace_all failed\n");
+ return ret;
+ }
+
+ ret = test_grow(&ring);
+ if (ret) {
+ printf("test_grow failed\n");
+ return ret;
+ }
+
+ ret = test_shrink(&ring);
+ if (ret) {
+ printf("test_shrink failed\n");
+ return ret;
+ }
+
+ ret = test_zero(&ring);
+ if (ret) {
+ printf("test_zero failed\n");
+ return ret;
+ }
+
+ ret = test_huge(&ring);
+ if (ret) {
+ printf("test_huge failed\n");
+ return ret;
+ }
+
+ ret = test_sparse_updates();
+ if (ret) {
+ printf("test_sparse_updates failed\n");
+ return ret;
+ }
+
+ return 0;
+}
--
1.8.3.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 5/5] test/buffer-share: add buffer registration sharing test
2020-12-14 21:09 [PATCH 0/5] liburing: buffer registration enhancements Bijan Mottahedeh
` (3 preceding siblings ...)
2020-12-14 21:09 ` [PATCH 4/5] test/buffer-update: add buffer registration update test Bijan Mottahedeh
@ 2020-12-14 21:09 ` Bijan Mottahedeh
4 siblings, 0 replies; 6+ messages in thread
From: Bijan Mottahedeh @ 2020-12-14 21:09 UTC (permalink / raw)
To: axboe, io-uring
Signed-off-by: Bijan Mottahedeh <[email protected]>
---
.gitignore | 1 +
test/Makefile | 3 +
test/buffer-share.c | 1184 +++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 1188 insertions(+)
create mode 100644 test/buffer-share.c
diff --git a/.gitignore b/.gitignore
index b44560e..4e09f1f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,6 +31,7 @@
/test/b19062a56726-test
/test/b5837bd5311d-test
/test/buffer-register
+/test/buffer-share
/test/buffer-update
/test/ce593a6c480a-test
/test/close-opath
diff --git a/test/Makefile b/test/Makefile
index 5cd467c..88381da 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -30,6 +30,7 @@ test_targets += \
b19062a56726-test \
b5837bd5311d-test \
buffer-register \
+ buffer-share \
buffer-update \
ce593a6c480a-test \
close-opath \
@@ -154,6 +155,7 @@ test_srcs := \
b19062a56726-test.c \
b5837bd5311d-test.c \
buffer-register.c \
+ buffer-share.c \
buffer-update.c \
ce593a6c480a-test.c \
close-opath.c \
@@ -252,6 +254,7 @@ ce593a6c480a-test: XCFLAGS = -lpthread
wakeup-hang: XCFLAGS = -lpthread
pipe-eof: XCFLAGS = -lpthread
timeout-new: XCFLAGS = -lpthread
+buffer-share: XCFLAGS = -lrt
install: $(test_targets) runtests.sh runtests-loop.sh
$(INSTALL) -D -d -m 755 $(datadir)/liburing-test/
diff --git a/test/buffer-share.c b/test/buffer-share.c
new file mode 100644
index 0000000..81e0537
--- /dev/null
+++ b/test/buffer-share.c
@@ -0,0 +1,1184 @@
+/* SPDX-License-Identifier: MIT */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <inttypes.h>
+#include <sys/mman.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <linux/mman.h>
+#include <sys/types.h>
+#include <sys/syscall.h>
+#include <sys/socket.h>
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <linux/fs.h>
+
+#include "liburing.h"
+
+/*
+ * The main (granparent) process creates a shared memory status segment for
+ * all tests and then creates a parent process which drives test execution.
+ * The grandparent then waits to reap the status of granchildren test
+ * processes, for the final test case which involves running IO tests after
+ * the parent exits.
+ *
+ * The parent process creates a shared memory data segment where all IO
+ * buffers reside. The parent then runs all test cases. For each test
+ * the parent creates the specified number of test processes, and waits
+ * for the children to execute the actual tests. The parent controls
+ * children's execution with ptrace().
+ *
+ * The tests are organized as follows:
+ *
+ * test ring registration test expected
+ * category sharing type case result
+ * -------- ------- --- ---- ------
+ * registration none none various pass
+ * IO fd based register/pin failure errno
+ * attach
+ *
+ * The IO test has been adapted largely unchanged from test/read-write.c.
+ */
+
+#define MAXID 128 /* max number of tester processes */
+int pids[MAXID];
+int status[MAXID];
+int nids = 1;
+
+#define SHM_ST_SZ (MAXID * 4)
+#define SHM_ST_NONE -1
+
+#define FILE_SIZE (128 * 1024)
+#define BS 4096
+#define BUFFERS (FILE_SIZE / BS)
+
+#define SZ_2M 0x200000
+
+#define SQ_THREAD_IDLE 2000
+
+char *shmbuf;
+char *shmstat;
+
+struct iovec vecs[UIO_MAXIOV];
+unsigned long buf_cnt = BUFFERS;
+unsigned long shm_sz = SZ_2M;
+
+int no_read;
+int warned;
+int verbose = 0;
+
+enum {
+ REG_NOP = 0,
+ REG_PIN,
+ REG_ATT,
+};
+
+char *r2s[] = {
+ "nop",
+ "pin",
+ "att",
+};
+
+enum {
+ TEST_DFLT = 0,
+ TEST_INVFD,
+ TEST_BADFD,
+ TEST_UNREG,
+ TEST_BUSY,
+ TEST_EXIT,
+ TEST_FIN,
+};
+
+char *t2s[] = {
+ "dflt",
+ "invfd",
+ "badfd",
+ "unreg",
+ "busy",
+ "exit",
+ "fin",
+};
+
+enum {
+ TEST_REG = 0,
+ TEST_IO,
+};
+
+char *i2s[] = {
+ "reg",
+ "io",
+};
+
+enum {
+ NO,
+ FD,
+};
+
+char *s2s[] = {
+ "nos",
+ "fd",
+};
+
+enum {
+ PASS,
+ FAIL,
+ NONE,
+};
+
+enum {
+ V0 = 0,
+ V1,
+ V2,
+ V3,
+ V4,
+};
+
+struct test_desc {
+ int id;
+ int idx;
+ int fd;
+ int sp[2];
+ int share;
+ int reg;
+ int preg;
+ int test;
+ int expect;
+};
+struct test_desc tdcs[MAXID];
+
+static int test_reg(struct test_desc *);
+static int test_io(struct test_desc *);
+
+int (*test_fn[])(struct test_desc *) = {
+ test_reg,
+ test_io,
+};
+
+static void
+vinfo(int level, char *fmt, ...)
+{
+ va_list args;
+
+ if (verbose < level)
+ return;
+
+ fprintf(stderr, "%-5d ", getpid());
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fflush(stderr);
+}
+
+static void
+verror(char *msg)
+{
+ if (!verbose)
+ return;
+
+ fprintf(stderr, "%-5d %s: %s\n", getpid(), msg, strerror(errno));
+ fflush(stderr);
+}
+
+static void
+send_fd(int socket, int fd)
+{
+ char buf[CMSG_SPACE(sizeof(fd))];
+ struct cmsghdr *cmsg;
+ struct msghdr msg;
+
+ memset(buf, 0, sizeof(buf));
+ memset(&msg, 0, sizeof(msg));
+
+ msg.msg_control = buf;
+ msg.msg_controllen = sizeof(buf);
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+
+ memmove(CMSG_DATA(cmsg), &fd, sizeof(fd));
+
+ msg.msg_controllen = CMSG_SPACE(sizeof(fd));
+
+ if (sendmsg(socket, &msg, 0) < 0) {
+ verror("sendmsg");
+ exit(1);
+ }
+}
+
+static int
+recv_fd(int socket)
+{
+ int *fdp, fd = -1, ret;
+ char buf[CMSG_SPACE(sizeof(fd))];
+ struct cmsghdr *cmsg;
+ struct msghdr msg;
+
+ memset(buf, 0, sizeof(buf));
+ memset(&msg, 0, sizeof(msg));
+
+ msg.msg_control = buf;
+ msg.msg_controllen = sizeof(buf);
+
+ ret = recvmsg(socket, &msg, 0);
+ if (ret < 0) {
+ verror("recvmsg");
+ exit(1);
+ }
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&msg,cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_RIGHTS) {
+ fdp = (int *)CMSG_DATA(cmsg);
+ fd = *fdp;
+ }
+ }
+
+ return fd;
+}
+
+static int
+shm_create(void)
+{
+ int id;
+
+ id = shmget(2, shm_sz, SHM_HUGETLB | IPC_CREAT | SHM_R | SHM_W);
+ if (id < 0) {
+ fprintf(stderr, "Unable to map a huge page. "
+ "Increase /proc/sys/vm/nr_hugepages by at least 1.\n");
+ verror("shmget");
+ exit(1);
+ }
+ vinfo(V3, "shm_create shmid: 0x%x\n", id);
+
+ return id;
+}
+
+static char *
+shm_attach(int id)
+{
+ char *addr;
+
+ addr = shmat(id, 0, 0);
+ if (addr == MAP_FAILED) {
+ verror("shmat");
+ shmctl(id, IPC_RMID, NULL);
+ }
+ vinfo(V3, "shm_attach shmid: 0x%x shmbuf: %p\n", id, addr);
+
+ return addr;
+}
+
+static void
+shm_destroy(int id, char *addr)
+{
+ int ret;
+
+ vinfo(V3, "shm_destroy shmid: 0x%x\n", id);
+
+ ret = shmdt((const void *)addr);
+ if (ret)
+ verror("shmdt");
+
+ shmctl(id, IPC_RMID, NULL);
+
+ if (ret)
+ exit(1);
+}
+
+static void *
+shmstat_create(void)
+{
+ int res;
+ int fd;
+ char fname[32];
+ void *addr = NULL;
+
+ snprintf(fname, sizeof(fname), "shmstat.%d", getpid());
+
+ /* get shared memory file descriptor (NOT a file) */
+ fd = shm_open(fname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
+ if (fd == -1) {
+ verror("open");
+ goto done;
+ }
+
+ /* extend shared memory object as by default it's set to size 0 */
+ res = ftruncate(fd, SHM_ST_SZ);
+ if (res == -1) {
+ verror("ftruncate");
+ goto done;
+ }
+
+ /* map shared memory to process address space */
+ addr = mmap(NULL, SHM_ST_SZ, PROT_WRITE, MAP_SHARED, fd, 0);
+ if (addr == MAP_FAILED) {
+ verror("mmap");
+ goto done;
+ }
+ memset(addr, 0, SHM_ST_SZ);
+done:
+ return addr;
+}
+
+static void
+shmstat_store(struct test_desc *tdc, int data)
+{
+ vinfo(V4, "stat_store %i %d\n", tdc->idx, data);
+ shmstat[tdc->idx] = data;
+}
+
+static int
+shmstat_check(int expect)
+{
+ int i;
+
+ for (i = 0; i < nids; i++) {
+ if (shmstat[i] != expect) {
+ vinfo(V3, "check_shmstat child %d: expect %d got %d\n",
+ pids[i], expect, shmstat[i]);
+ return shmstat[i];
+ }
+ }
+
+ return 0;
+}
+
+static int
+queue_init(struct test_desc *tdc, int qd, struct io_uring *ring,
+ struct io_uring_params *p, int reg)
+{
+ int ret;
+
+ if (tdc->share == FD) {
+ if (reg == REG_ATT) {
+ p->flags |= IORING_SETUP_ATTACH_BUF;
+ if (tdc->test == TEST_BADFD)
+ p->wq_fd = -1;
+ else if (tdc->test == TEST_INVFD)
+ p->wq_fd = 0;
+ else
+ p->wq_fd = tdc->fd;
+ } else
+ p->flags |= IORING_SETUP_SHARE_BUF;
+ }
+
+ ret = io_uring_queue_init_params(qd, ring, p);
+ if (ret)
+ verror("queue_init");
+ vinfo(V2, "queue_init %d\n", ring->ring_fd);
+
+ return ret;
+}
+
+static void
+queue_exit(struct io_uring *ring)
+{
+ vinfo(V2, "queue_exit %d\n", ring->ring_fd);
+ io_uring_queue_exit(ring);
+}
+
+static void
+create_buffers(char *adr)
+{
+ int i;
+
+ if (buf_cnt > UIO_MAXIOV) {
+ fprintf(stderr, "invalid buffer count: %ld\n", buf_cnt);
+ exit(1);
+ }
+
+ for (i = 0; i < buf_cnt; i++) {
+ vecs[i].iov_base = adr;
+ vecs[i].iov_len = BS;
+ adr += BS;
+ }
+}
+
+static int
+register_buffers(struct test_desc *tdc, struct io_uring *ring)
+{
+ if (tdc->reg == REG_ATT)
+ return io_uring_register_buffers(ring, 0, 0);
+
+ return io_uring_register_buffers(ring, vecs, buf_cnt);
+}
+
+static void
+stop(void)
+{
+ vinfo(V2, "raise SIGSTOP\n");
+ raise(SIGSTOP);
+ vinfo(V2, "resume SIGSTOP\n");
+}
+
+static int
+test_reg(struct test_desc *tdc)
+{
+ int ret;
+ struct io_uring_params p = {0};
+ struct io_uring ring;
+
+ ret = queue_init(tdc, 1, &ring, &p, tdc->reg);
+ if (ret)
+ return ret;
+
+ ret = register_buffers(tdc, &ring);
+ if (ret)
+ verror("register test_reg");
+
+ if (tdc->test == TEST_BUSY) {
+ vinfo(V2, "delay\n");
+ sleep(5);
+ } else if (tdc->test == TEST_EXIT) {
+ stop();
+ }
+
+ queue_exit(&ring);
+
+ return ret;
+}
+
+static int
+create_file(const char *file)
+{
+ ssize_t ret;
+ char *buf;
+ int fd;
+
+ buf = malloc(FILE_SIZE);
+ memset(buf, 0xaa, FILE_SIZE);
+
+ fd = open(file, O_WRONLY | O_CREAT, 0644);
+ if (fd < 0) {
+ verror("open");
+ return 1;
+ }
+ ret = write(fd, buf, FILE_SIZE);
+ close(fd);
+ return ret != FILE_SIZE;
+}
+
+static int
+do_io(struct test_desc *tdc, const char *file,
+ struct io_uring *ring, int write, int buffered,
+ int sqthread, int fixed, int nonvec, int seq, int exp_len)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int open_flags;
+ int i, fd, ret = 0;
+ off_t offset;
+
+ vinfo(V1, "%s: %d/%d/%d/%d/%d\n",
+ __FUNCTION__, write, buffered, sqthread, fixed, nonvec);
+
+ if (sqthread && geteuid()) {
+ vinfo(V1, "SKIPPED (not root)\n");
+ return 0;
+ }
+
+ if (write)
+ open_flags = O_WRONLY;
+ else
+ open_flags = O_RDONLY;
+ if (!buffered)
+ open_flags |= O_DIRECT;
+
+ fd = open(file, open_flags);
+ if (fd < 0) {
+ verror("open");
+ goto err;
+ }
+
+ if (fixed) {
+ ret = register_buffers(tdc, ring);
+ if (ret) {
+ vinfo(V1, "buffer reg failed: %d\n", ret);
+ goto err;
+ }
+ }
+ if (fixed || sqthread) {
+ ret = io_uring_register_files(ring, &fd, 1);
+ if (ret) {
+ vinfo(V1, "file reg failed: %d\n", ret);
+ goto err;
+ }
+ }
+
+ offset = 0;
+ for (i = 0; i < BUFFERS; i++) {
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ vinfo(V1, "sqe get failed\n");
+ goto err;
+ }
+ if (!seq)
+ offset = BS * (rand() % BUFFERS);
+ if (write) {
+ int use_fd = fd;
+
+ if (fixed || sqthread)
+ use_fd = 0;
+
+ if (fixed) {
+ io_uring_prep_write_fixed(sqe, use_fd,
+ vecs[i].iov_base,
+ vecs[i].iov_len,
+ offset, i);
+ } else if (nonvec) {
+ io_uring_prep_write(sqe, use_fd,
+ vecs[i].iov_base,
+ vecs[i].iov_len, offset);
+ } else {
+ io_uring_prep_writev(sqe, use_fd, &vecs[i], 1,
+ offset);
+ }
+ } else {
+ int use_fd = fd;
+
+ if (fixed || sqthread)
+ use_fd = 0;
+
+ if (fixed) {
+ io_uring_prep_read_fixed(sqe, use_fd,
+ vecs[i].iov_base,
+ vecs[i].iov_len,
+ offset, i);
+ } else if (nonvec) {
+ io_uring_prep_read(sqe, use_fd,
+ vecs[i].iov_base,
+ vecs[i].iov_len, offset);
+ } else {
+ io_uring_prep_readv(sqe, use_fd, &vecs[i], 1,
+ offset);
+ }
+
+ }
+ if (fixed || sqthread)
+ sqe->flags |= IOSQE_FIXED_FILE;
+ if (seq)
+ offset += BS;
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret != BUFFERS) {
+ vinfo(V1, "submit got %d, wanted %d\n", ret, BUFFERS);
+ goto err;
+ }
+
+ for (i = 0; i < BUFFERS; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ vinfo(V1, "wait_cqe %d\n", ret);
+ goto err;
+ }
+ if (cqe->res == -EINVAL && nonvec) {
+ if (!warned) {
+ vinfo(V1, "Non-vectored IO not "
+ "supported, skipping\n");
+ warned = 1;
+ no_read = 1;
+ }
+ } else if (cqe->res != exp_len) {
+ vinfo(V1, "cqe res %d, wanted %d\n", cqe->res, exp_len);
+ ret = cqe->res;
+ goto err;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ if (fixed) {
+ ret = io_uring_unregister_buffers(ring);
+ if (ret) {
+ vinfo(V1, "buffer unreg failed: %d\n", ret);
+ goto err;
+ }
+ }
+ if (fixed || sqthread) {
+ ret = io_uring_unregister_files(ring);
+ if (ret) {
+ vinfo(V1, "file unreg failed: %d\n", ret);
+ goto err;
+ }
+ }
+
+ close(fd);
+ vinfo(V1, "%s: %d/%d/%d/%d/%d: PASS\n",
+ __FUNCTION__, write, buffered, sqthread, fixed, nonvec);
+ return 0;
+err:
+ (void)io_uring_unregister_buffers(ring);
+ if (fd != -1)
+ close(fd);
+ return ret;
+}
+
+static int
+run_test_io(struct test_desc *tdc, const char *file,
+ int write, int buffered, int sqthread, int fixed, int nonvec)
+{
+ struct io_uring_params p = {0};
+ struct io_uring ring;
+ int share, ret;
+
+ share = tdc->share;
+
+ if (!fixed)
+ tdc->share = NO;
+
+ if (sqthread) {
+ if (geteuid()) {
+ if (!warned) {
+ fprintf(stderr,
+ "SQPOLL requires root, skipping\n");
+ warned = 1;
+ }
+ return 0;
+ }
+ p.flags = IORING_SETUP_SQPOLL;
+ p.sq_thread_idle = nids * SQ_THREAD_IDLE;
+ }
+ ret = queue_init(tdc, 64, &ring, &p, tdc->reg);
+
+ if (ret)
+ goto done;
+
+ ret = do_io(tdc, file, &ring, write, buffered, sqthread, fixed,
+ nonvec, 0, BS);
+
+ queue_exit(&ring);
+done:
+ tdc->share = share;
+ return ret;
+}
+
+
+static int
+has_nonvec_read(void)
+{
+ struct io_uring_probe *p;
+ struct io_uring ring;
+ int ret;
+
+ ret = io_uring_queue_init(1, &ring, 0);
+ vinfo(V1, "io_uring_queue_init %d\n", ring.ring_fd);
+ if (ret) {
+ verror("io_uring_queue_init");
+ exit(ret);
+ }
+
+ p = calloc(1, sizeof(*p) + 256 * sizeof(struct io_uring_probe_op));
+ ret = io_uring_register_probe(&ring, p, 256);
+ /* if we don't have PROBE_REGISTER, we don't have OP_READ/WRITE */
+ if (ret == -EINVAL) {
+out:
+ queue_exit(&ring);
+ return 0;
+ } else if (ret) {
+ fprintf(stderr, "register_probe: %d\n", ret);
+ goto out;
+ }
+
+ if (p->ops_len <= IORING_OP_READ)
+ goto out;
+ if (!(p->ops[IORING_OP_READ].flags & IO_URING_OP_SUPPORTED))
+ goto out;
+ queue_exit(&ring);
+ return 1;
+}
+
+static int
+test_io(struct test_desc *tdc)
+{
+ int i, ret, nr;
+ char fname[32];
+
+ snprintf(fname, sizeof(fname), ".basic-rw.%d", getpid());
+ if (create_file(fname)) {
+ fprintf(stderr, "file creation failed\n");
+ ret = 1;
+ goto err;
+ }
+
+ if (tdc->test == TEST_EXIT)
+ stop();
+
+ /* if we don't have nonvec read, skip testing that */
+ if (has_nonvec_read())
+ nr = 64;
+ else
+ nr = 32;
+
+ for (i = 0; i < nr; i++) {
+ int v1, v2, v3, v4, v5;
+
+ v1 = (i & 1) != 0;
+ v2 = (i & 2) != 0;
+ v3 = (i & 4) != 0;
+ v4 = (i & 8) != 0;
+ v5 = (i & 16) != 0;
+ ret = run_test_io(tdc, fname, v1, v2, v3, v4, v5);
+ if (ret) {
+ vinfo(V1, "test_io failed %d/%d/%d/%d/%d\n",
+ v1, v2, v3, v4, v5);
+ goto err;
+ }
+ }
+err:
+ unlink(fname);
+ return ret;
+}
+
+static int
+get_socketpair(int sp[])
+{
+ if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sp) != 0) {
+ verror("socketpair\n");
+ return errno;
+ }
+ return 0;
+}
+
+static void
+waitstoppid(int pid)
+{
+ int ret;
+ int status;
+
+ while (1) {
+ vinfo(V2, "waitstop %d\n", pid);
+
+ ret = waitpid(pid, &status, WNOHANG | WUNTRACED | WCONTINUED);
+ if (ret != pid) {
+ sleep(1);
+ continue;
+ }
+ if (WIFSTOPPED(status))
+ return;
+
+ break;
+ }
+
+ vinfo(V2, "pid %d was not stopped!\n", pid);
+ exit(1);
+}
+
+static void
+waitstop(void)
+{
+ int i;
+
+ for (i = 0; i < nids; i++)
+ waitstoppid(pids[i]);
+}
+
+static void
+waitexitpid(int pid, int *status)
+{
+ int ret, xs;
+
+ while (1) {
+ vinfo(V2, "waitexit %d\n", pid);
+
+ ret = waitpid(pid, status, WNOHANG | WUNTRACED | WCONTINUED);
+ if (ret != pid) {
+ sleep(1);
+ continue;
+ }
+ xs = *status;
+ if (WIFEXITED(xs)) {
+ vinfo(2, "child %d exited: %d\n", pid, WEXITSTATUS(xs));
+ *status = WEXITSTATUS(xs);
+ return;
+ }
+
+ vinfo(V2, "waitexit 0x%x e:%d s:%d d:%d t:%d c:%d\n", xs,
+ WIFEXITED(xs), WIFSIGNALED(xs), WCOREDUMP(xs),
+ WIFSTOPPED(xs), WIFCONTINUED(xs));
+ break;
+ }
+
+ vinfo(V2, "pid %d didn't exit!\n", pid);
+ exit(1);
+}
+
+static void
+waitexit(void)
+{
+ int i;
+
+ for (i = 0; i < nids; i++) {
+ status[i] = 0;
+ waitexitpid(pids[i], &status[i]);
+ }
+}
+
+static void
+contpid(int pid)
+{
+ int ret;
+
+ vinfo(V2, "cont %d\n", pid);
+
+ ret = ptrace(PTRACE_CONT, pid, NULL, NULL);
+ if (ret < 0) {
+ verror("ptrace");
+ exit(1);
+ }
+}
+
+static void
+cont(void)
+{
+ int i;
+
+ for (i = 0; i < nids; i++)
+ contpid(pids[i]);
+}
+
+static void
+send_ring_fd(int fd)
+{
+ int i;
+ struct test_desc *tdc;
+
+ for (i = 0; i < nids; i++) {
+ tdc = &tdcs[i];
+ vinfo(V3, "send_fd %d %d: %d\n", pids[i], tdc->sp[0], fd);
+ send_fd(tdc->sp[0], fd);
+ }
+}
+
+static void
+close_sockets(void)
+{
+ int i;
+
+ for (i = 0; i < nids; i++) {
+ shutdown(tdcs[i].sp[0], SHUT_RDWR);
+ shutdown(tdcs[i].sp[1], SHUT_RDWR);
+ }
+}
+
+static int
+run_parent(struct test_desc *tdc)
+{
+ int ret;
+ struct io_uring_params p = {0};
+ struct io_uring ring;
+
+ waitstop();
+
+ ret = queue_init(tdc, 1, &ring, &p, tdc->preg);
+ if (ret)
+ return ret;
+
+ if (tdc->preg == REG_NOP)
+ goto send;
+
+ ret = io_uring_register_buffers(&ring, vecs, buf_cnt);
+ if (ret) {
+ verror("register run_parent");
+ goto done;
+ }
+
+ if (tdc->test == TEST_UNREG) {
+ ret = io_uring_unregister_buffers(&ring);
+ if (ret) {
+ verror("unregister run_parent");
+ goto done;
+ }
+ }
+send:
+ if (tdc->share == FD)
+ send_ring_fd(ring.ring_fd);
+
+ cont();
+
+ if (tdc->test == TEST_BUSY) {
+ ret = io_uring_unregister_buffers(&ring);
+ if (ret)
+ verror("unregister run_parent busy");
+ if (-ret == tdc->expect)
+ ret = -ret;
+ } else if (tdc->test == TEST_EXIT) {
+ waitstop();
+ vinfo(2, "parent exiting\n");
+ exit(2);
+ }
+
+ waitexit();
+ if (!ret && tdc->test == TEST_FIN) {
+ ret = io_uring_unregister_buffers(&ring);
+ if (ret)
+ verror("unregister run_parent fin");
+ }
+
+done:
+ queue_exit(&ring);
+
+ if (ret)
+ cont();
+
+ return ret;
+}
+
+static void
+run_child(struct test_desc *tdc)
+{
+ int from_fd, ret = ECONNABORTED;
+ int (*fn)(struct test_desc *);
+
+ vinfo(V1, "child stop\n");
+
+ ptrace(PTRACE_TRACEME, 0, NULL, NULL);
+ raise(SIGSTOP);
+
+ if (tdc->share == FD) {
+ vinfo(V3, "recv_fd %d\n", tdc->sp[1]);
+ from_fd = recv_fd(tdc->sp[1]);
+ if (from_fd < 0)
+ goto done;
+ vinfo(V3, "recv_fd %d: %d\n", tdc->sp[1], from_fd);
+ tdc->fd = from_fd;
+ }
+
+ fn = test_fn[tdc->id];
+ ret = fn(tdc);
+
+ if (tdc->share == FD)
+ close_sockets();
+
+done:
+ vinfo(V2, "child exiting %d\n", ret);
+ /*
+ * The parent exits for the final test before waiting for children
+ * so save the test status.
+ */
+ shmstat_store(tdc, -ret);
+ exit(-ret);
+}
+
+static int
+run_test(struct test_desc *proto)
+{
+ int i, pid, ret;
+ struct test_desc *tdc = NULL;
+
+ ret = pid = 0;
+
+ for (i = 0; i < nids; i++) {
+ tdcs[i] = *proto;
+ tdc = & tdcs[i];
+ tdc->idx = i;
+ if (tdc->share == FD) {
+ ret = get_socketpair(tdc->sp);
+ if (ret)
+ return -ret;
+ }
+ pid = fork();
+ if (pid) {
+ pids[i] = pid;
+ continue;
+ }
+ run_child(tdc);
+ }
+
+ ret = run_parent(proto);
+
+ if (proto->share == FD)
+ close_sockets();
+
+ return ret;
+}
+
+static int
+check_exitstat(struct test_desc *tdc, int *stat)
+{
+ int i, ret = 0;
+
+ for (i = 0; i < nids; i++) {
+ if (status[i] != tdc->expect) {
+ vinfo(V1, "check_exitstat child %d: expect %d got %d\n",
+ pids[i], tdc->expect, status[i]);
+ if (!ret) {
+ ret = 1;
+ *stat = status[i];
+ }
+ }
+ }
+
+ if (!ret)
+ *stat = status[0];
+
+ return ret;
+}
+
+static int
+test(int id, int share, int preg, int reg, int test, int expect)
+{
+ int ret, stat;
+ char *res;
+
+ struct test_desc tdc = {
+ .id = id,
+ .fd = -1,
+ .sp = {-1, -1},
+ .share = share,
+ .preg = preg,
+ .reg = reg,
+ .test = test,
+ .expect = expect,
+ };
+
+ vinfo(V1, "test(%s %s %s %s %s)\n", i2s[tdc.id], s2s[tdc.share],
+ r2s[tdc.preg], r2s[tdc.reg], t2s[tdc.test]);
+
+ memset(tdcs, 0, sizeof(tdcs));
+ memset(pids, 0, sizeof(pids));
+ memset(status, 0, sizeof(status));
+ memset(shmstat, SHM_ST_NONE, SHM_ST_SZ);
+
+ ret = run_test(&tdc);
+ stat = ret;
+
+ if (ret) {
+ ret = 1;
+ res = "FAIL RUN";
+ } else {
+ ret = check_exitstat(&tdc, &stat);
+ res = ret ? "FAIL" : "PASS";
+ }
+
+ vinfo(V1, "test(%s %s %s %s %s: %d %d) %s\n",
+ i2s[tdc.id], s2s[tdc.share], r2s[tdc.preg],
+ r2s[tdc.reg], t2s[tdc.test], tdc.expect, stat, res);
+
+ return ret;
+}
+
+static int
+test_exit(int expect)
+{
+ int status, i, ret = 0;
+
+ vinfo(V1, "grandparent pid\n");
+
+ wait(&status);
+
+ vinfo(V2, "parent exited: %d\n", WEXITSTATUS(status));
+
+ for (i = 0; i < 60; i++) {
+ ret = shmstat_check(expect);
+ if (ret >= 0)
+ break;
+ sleep(1);
+ }
+
+ if (ret) {
+ vinfo(V1, "Bad test_exit stat %d\n", ret);
+ ret = 1;
+ }
+
+ vinfo(V1, "Test exit %d\n", ret);
+ exit(ret);
+}
+
+/*
+ * main()
+ * -> shm_create()
+ * -> create_buffers()
+ * parent:
+ * -> test()
+ * -> run_test()
+ * -> run_parent()
+ * -> io_uring_register_buffers()
+ * -> run_child()
+ * -> test_reg()
+ * -> register_buffers()
+ * -> test_io()
+ * -> run_test_io()
+ * -> do_io()
+ * -> register_buffers()
+ *
+ * grandparent:
+ * -> test_exit()
+ */
+
+int
+main(int argc, char *argv[])
+{
+ int shmid, pid, ret = 0;
+ char c;
+
+ while ((c = getopt(argc, argv, "s:v:")) != -1)
+ switch (c) {
+ case 's':
+ shm_sz = atoi(optarg) * SZ_2M;
+ break;
+ case 'v':
+ verbose = atoi(optarg);
+ break;
+ default:
+ exit(1);
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc)
+ nids = atoi(argv[0]);
+
+ if (!nids || nids > MAXID)
+ exit(1);
+
+ shmstat = shmstat_create();
+ if (shmstat == MAP_FAILED)
+ exit(1);
+
+ pid = fork();
+ if (pid)
+ exit(test_exit(PASS));
+
+ vinfo(V1, "parent pid\n");
+
+ shmid = shm_create();
+ if (shmid < 0)
+ exit(1);
+
+ shmbuf = shm_attach(shmid);
+ if (shmbuf == MAP_FAILED) {
+ shm_destroy(shmid, shmbuf);
+ exit(1);
+ }
+
+ create_buffers(shmbuf);
+
+ ret |= test(TEST_REG, NO, REG_PIN, REG_PIN, TEST_DFLT, PASS);
+ ret |= test(TEST_REG, NO, REG_NOP, REG_NOP, TEST_DFLT, PASS);
+ ret |= test(TEST_REG, NO, REG_NOP, REG_ATT, TEST_DFLT, EINVAL);
+ ret |= test(TEST_REG, FD, REG_PIN, REG_ATT, TEST_DFLT, PASS);
+ ret |= test(TEST_REG, FD, REG_NOP, REG_ATT, TEST_DFLT, PASS);
+ ret |= test(TEST_REG, FD, REG_PIN, REG_ATT, TEST_BUSY, PASS);
+ ret |= test(TEST_REG, FD, REG_PIN, REG_ATT, TEST_FIN, PASS);
+ ret |= test(TEST_REG, FD, REG_PIN, REG_ATT, TEST_BADFD, EBADF);
+ ret |= test(TEST_REG, FD, REG_PIN, REG_ATT, TEST_INVFD, EINVAL);
+ ret |= test(TEST_REG, FD, REG_PIN, REG_ATT, TEST_UNREG, EINVAL);
+ ret |= test(TEST_IO, FD, REG_PIN, REG_PIN, TEST_DFLT, PASS);
+ ret |= test(TEST_IO, FD, REG_PIN, REG_ATT, TEST_DFLT, PASS);
+ ret |= test(TEST_IO, FD, REG_NOP, REG_ATT, TEST_DFLT, EFAULT);
+ ret |= test(TEST_IO, FD, REG_PIN, REG_ATT, TEST_EXIT, NONE);
+
+ shm_destroy(shmid, shmbuf);
+
+ return ret;
+}
--
1.8.3.1
^ permalink raw reply related [flat|nested] 6+ messages in thread