123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658 |
- #include "../config-host.h"
- /* SPDX-License-Identifier: MIT */
- /*
- * Description: test sq/cq ring resizing
- *
- */
- #include <errno.h>
- #include <stdio.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <string.h>
- #include <fcntl.h>
- #include <pthread.h>
- #include "liburing.h"
- #include "helpers.h"
- static bool only_defer, no_defer;
- #define NVECS 128
- #define min(a, b) ((a) < (b) ? (a) : (b))
- struct data {
- pthread_t thread;
- int fd;
- int nr_writes;
- int failed;
- };
- static void *thread_fn(void *__data)
- {
- struct data *d = __data;
- char buffer[8];
- int to_write = d->nr_writes;
- memset(buffer, 0x5a, sizeof(buffer));
- usleep(10000);
- while (to_write) {
- int ret = write(d->fd, buffer, sizeof(buffer));
- if (ret < 0) {
- perror("write");
- d->failed = 1;
- break;
- } else if (ret != sizeof(buffer)) {
- printf("short write %d\n", ret);
- }
- to_write--;
- usleep(5);
- }
- return NULL;
- }
- static int test_pipes(struct io_uring *ring, int async)
- {
- struct io_uring_params p = { };
- struct io_uring_sqe *sqe;
- struct io_uring_cqe *cqe;
- unsigned long ud = 0;
- struct data d = { };
- int ret, i, fds[2], to_read;
- char buffer[8];
- void *tret;
- p.sq_entries = 128;
- p.cq_entries = 128;
- ret = io_uring_resize_rings(ring, &p);
- if (ret < 0) {
- fprintf(stderr, "Failed to resize ring: %d\n", ret);
- return T_EXIT_FAIL;
- }
- if (pipe(fds) < 0) {
- perror("pipe");
- return T_EXIT_FAIL;
- }
- /*
- * Put NVECS inflight, then resize while waiting. Repeat until
- * 'to_read' has been read.
- */
- d.nr_writes = 4096;
- d.fd = fds[1];
- p.sq_entries = 64;
- p.cq_entries = 256;
- pthread_create(&d.thread, NULL, thread_fn, &d);
- to_read = d.nr_writes - 128;
- while (to_read && !d.failed) {
- unsigned long start_ud = -1UL, end_ud;
- int to_wait;
- to_wait = NVECS;
- if (to_wait > to_read)
- to_wait = to_read;
- for (i = 0; i < to_wait; i++) {
- sqe = io_uring_get_sqe(ring);
- /* resized smaller */
- if (!sqe)
- break;
- io_uring_prep_read(sqe, fds[0], buffer, sizeof(buffer), 0);
- if (async)
- sqe->flags |= IOSQE_ASYNC;
- if (start_ud == -1UL)
- start_ud = ud;
- sqe->user_data = ++ud;
- to_read--;
- }
- end_ud = ud;
- ret = io_uring_submit(ring);
- if (ret != i) {
- fprintf(stderr, "submitted; %d\n", ret);
- return T_EXIT_FAIL;
- }
- to_wait = i;
- for (i = 0; i < to_wait; i++) {
- if (i == 0) {
- ret = io_uring_resize_rings(ring, &p);
- if (ret < 0) {
- if (ret != -EOVERFLOW) {
- fprintf(stderr, "resize failed: %d\n", ret);
- return T_EXIT_FAIL;
- }
- }
- p.sq_entries = 32;
- p.cq_entries = 128;
- }
- if (d.failed)
- break;
- ret = io_uring_wait_cqe(ring, &cqe);
- if (ret) {
- fprintf(stderr, "wait cqe: %d\n", ret);
- return T_EXIT_FAIL;
- }
- if (cqe->res < 0) {
- fprintf(stderr, "cqe res %d\n", cqe->res);
- return T_EXIT_FAIL;
- }
- if (cqe->user_data < start_ud ||
- cqe->user_data > end_ud) {
- fprintf(stderr, "use_data out-of-range: <%lu-%lu>: %lu\n",
- start_ud, end_ud, (long) cqe->user_data);
- return T_EXIT_FAIL;
- }
- io_uring_cqe_seen(ring, cqe);
- if (!(i % 17)) {
- ret = io_uring_resize_rings(ring, &p);
- if (ret < 0) {
- if (ret == -EOVERFLOW)
- continue;
- fprintf(stderr, "resize failed: %d\n", ret);
- return T_EXIT_FAIL;
- }
- if (p.sq_entries == 32)
- p.sq_entries = 64;
- else if (p.sq_entries == 64)
- p.sq_entries = 16;
- else
- p.sq_entries = 32;
- if (p.cq_entries == 128)
- p.cq_entries = 256;
- else
- p.cq_entries = 128;
- }
- }
- }
- pthread_join(d.thread, &tret);
- close(fds[0]);
- close(fds[0]);
- return 0;
- }
- static int test_reads(struct io_uring *ring, int fd, int async)
- {
- struct io_uring_params p = { };
- struct io_uring_sqe *sqe;
- struct io_uring_cqe *cqe;
- struct iovec vecs[NVECS];
- unsigned long to_read;
- unsigned long ud = 0;
- unsigned long offset;
- int ret, i;
- if (fd == -1)
- return T_EXIT_SKIP;
- p.sq_entries = 128;
- p.cq_entries = 128;
- ret = io_uring_resize_rings(ring, &p);
- if (ret < 0) {
- fprintf(stderr, "Failed to resize ring: %d\n", ret);
- return T_EXIT_FAIL;
- }
- for (i = 0; i < NVECS; i++) {
- if (posix_memalign(&vecs[i].iov_base, 4096, 4096))
- return T_EXIT_FAIL;
- vecs[i].iov_len = 4096;
- }
- /*
- * Put NVECS inflight, then resize while waiting. Repeat until
- * 'to_read' has been read.
- */
- to_read = 64*1024*1024;
- p.sq_entries = 64;
- p.cq_entries = 256;
- offset = 0;
- while (to_read) {
- unsigned long start_ud = -1UL, end_ud;
- int to_wait;
- for (i = 0; i < NVECS; i++) {
- sqe = io_uring_get_sqe(ring);
- /* resized smaller */
- if (!sqe)
- break;
- io_uring_prep_read(sqe, fd, vecs[i].iov_base,
- vecs[i].iov_len, offset);
- if (async)
- sqe->flags |= IOSQE_ASYNC;
- offset += 8192;
- if (start_ud == -1UL)
- start_ud = ud;
- sqe->user_data = ++ud;
- }
- end_ud = ud;
- ret = io_uring_submit(ring);
- if (ret != i) {
- fprintf(stderr, "submitted; %d\n", ret);
- return T_EXIT_FAIL;
- }
- to_wait = i;
- for (i = 0; i < to_wait; i++) {
- if (i == 0) {
- ret = io_uring_resize_rings(ring, &p);
- if (ret < 0) {
- if (ret != -EOVERFLOW) {
- fprintf(stderr, "resize failed: %d\n", ret);
- return T_EXIT_FAIL;
- }
- }
- p.sq_entries = 32;
- p.cq_entries = 128;
- }
- ret = io_uring_wait_cqe(ring, &cqe);
- if (ret) {
- fprintf(stderr, "wait cqe: %d\n", ret);
- return T_EXIT_FAIL;
- }
- if (cqe->res < 0) {
- fprintf(stderr, "cqe res %d\n", cqe->res);
- return T_EXIT_FAIL;
- }
- if (cqe->user_data < start_ud ||
- cqe->user_data > end_ud) {
- fprintf(stderr, "use_data out-of-range: <%lu-%lu>: %lu\n",
- start_ud, end_ud, (long) cqe->user_data);
- return T_EXIT_FAIL;
- }
- io_uring_cqe_seen(ring, cqe);
- if (to_read)
- to_read -= min(to_read, 4096);
- if (!(i % 17)) {
- ret = io_uring_resize_rings(ring, &p);
- if (ret < 0) {
- if (ret == -EOVERFLOW)
- continue;
- fprintf(stderr, "resize failed: %d\n", ret);
- return T_EXIT_FAIL;
- }
- if (p.sq_entries == 32)
- p.sq_entries = 64;
- else if (p.sq_entries == 64)
- p.sq_entries = 16;
- else
- p.sq_entries = 32;
- if (p.cq_entries == 128)
- p.cq_entries = 256;
- else
- p.cq_entries = 128;
- }
- }
- }
- return 0;
- }
- static int test_basic(struct io_uring *ring, int async)
- {
- struct io_uring_params p = { };
- struct io_uring_sqe *sqe;
- struct io_uring_cqe *cqe;
- int i, ret;
- sqe = io_uring_get_sqe(ring);
- io_uring_prep_nop(sqe);
- if (async)
- sqe->flags |= IOSQE_ASYNC;
- sqe->user_data = 1;
- io_uring_submit(ring);
- p.sq_entries = 32;
- p.cq_entries = 64;
- ret = io_uring_resize_rings(ring, &p);
- if (ret == -EINVAL)
- return T_EXIT_SKIP;
- sqe = io_uring_get_sqe(ring);
- io_uring_prep_nop(sqe);
- if (async)
- sqe->flags |= IOSQE_ASYNC;
- sqe->user_data = 2;
- io_uring_submit(ring);
- for (i = 0; i < 2; i++) {
- ret = io_uring_wait_cqe(ring, &cqe);
- if (ret) {
- fprintf(stderr, "wait cqe %d\n", ret);
- return T_EXIT_FAIL;
- }
- if (cqe->user_data != i + 1) {
- fprintf(stderr, "bad user_data %ld\n", (long) cqe->user_data);
- return T_EXIT_FAIL;
- }
- io_uring_cqe_seen(ring, cqe);
- }
- return T_EXIT_PASS;
- }
- static int test_all_copy(struct io_uring *ring)
- {
- struct io_uring_params p = { };
- struct io_uring_sqe *sqe;
- struct io_uring_cqe *cqe;
- unsigned head;
- int i, ret;
- p.sq_entries = 32;
- p.cq_entries = 64;
- ret = io_uring_resize_rings(ring, &p);
- if (ret) {
- fprintf(stderr, "resize failed: %d\n", ret);
- return T_EXIT_FAIL;
- }
- for (i = 0; i < 32; i++) {
- sqe = io_uring_get_sqe(ring);
- io_uring_prep_nop(sqe);
- sqe->user_data = i + 1;
- }
- io_uring_submit(ring);
- memset(&p, 0, sizeof(p));
- p.sq_entries = 64;
- p.cq_entries = 128;
- ret = io_uring_resize_rings(ring, &p);
- if (ret) {
- fprintf(stderr, "resize failed: %d\n", ret);
- return T_EXIT_FAIL;
- }
- i = 1;
- io_uring_for_each_cqe(ring, head, cqe) {
- if (cqe->user_data != i) {
- fprintf(stderr, "Found cqe at wrong offset\n");
- return T_EXIT_FAIL;
- }
- i++;
- }
- io_uring_cq_advance(ring, 32);
- return T_EXIT_PASS;
- }
- static int test_overflow(struct io_uring *ring)
- {
- struct io_uring_params p = { };
- struct io_uring_sqe *sqe;
- int i, ret;
- p.sq_entries = 32;
- p.cq_entries = 64;
- ret = io_uring_resize_rings(ring, &p);
- if (ret) {
- fprintf(stderr, "resize failed: %d\n", ret);
- return T_EXIT_FAIL;
- }
- for (i = 0; i < 32; i++) {
- sqe = io_uring_get_sqe(ring);
- io_uring_prep_nop(sqe);
- sqe->user_data = i + 1;
- }
- io_uring_submit(ring);
- /* have 32 CQEs pending, resize to CQ size 32 which should work */
- memset(&p, 0, sizeof(p));
- p.sq_entries = 32;
- p.cq_entries = 32;
- ret = io_uring_resize_rings(ring, &p);
- if (ret) {
- fprintf(stderr, "resize failed: %d\n", ret);
- return T_EXIT_FAIL;
- }
- /* now resize to CQ size 16, which should fail with -EOVERFLOW */
- memset(&p, 0, sizeof(p));
- p.sq_entries = 8;
- p.cq_entries = 16;
- ret = io_uring_resize_rings(ring, &p);
- if (ret != -EOVERFLOW) {
- fprintf(stderr, "Expected overflow, got %d\n", ret);
- return T_EXIT_FAIL;
- }
- io_uring_cq_advance(ring, 32);
- return T_EXIT_PASS;
- }
- static int test_same_resize(int flags)
- {
- struct io_uring_params p = { };
- struct io_uring_sqe *sqe;
- struct io_uring_cqe *cqe;
- struct io_uring ring;
- int i, ret;
- ret = io_uring_queue_init(32, &ring, flags);
- if (ret)
- return T_EXIT_FAIL;
- p.sq_entries = 32;
- p.cq_entries = 64;
- ret = io_uring_resize_rings(&ring, &p);
- if (ret) {
- fprintf(stderr, "resize failed: %d\n", ret);
- return T_EXIT_FAIL;
- }
- for (i = 0; i < 32; i++) {
- sqe = io_uring_get_sqe(&ring);
- io_uring_prep_nop(sqe);
- sqe->user_data = i + 1;
- }
- io_uring_submit(&ring);
- for (i = 0; i < 32; i++) {
- ret = io_uring_wait_cqe(&ring, &cqe);
- if (ret) {
- fprintf(stderr, "wait_cqe: %d\n", ret);
- return T_EXIT_FAIL;
- }
- if (cqe->user_data != i + 1) {
- fprintf(stderr, "Found cqe at wrong offset\n");
- return T_EXIT_FAIL;
- }
- io_uring_cqe_seen(&ring, cqe);
- }
- io_uring_queue_exit(&ring);
- return T_EXIT_PASS;
- }
- static int mmap_child(struct io_uring *__ring, struct io_uring_params *__p)
- {
- struct io_uring ring = *__ring;
- struct timeval tv;
- int ret;
- gettimeofday(&tv, NULL);
- do {
- struct io_uring_params p = *__p;
- void *sq_ptr, *cq_ptr;
- ret = io_uring_queue_mmap(__ring->ring_fd, &p, &ring);
- if (ret)
- continue;
- sq_ptr = ring.sq.ring_ptr + 2 * sizeof(__u32);
- cq_ptr = ring.cq.ring_ptr + 2 * sizeof(__u32);
- memset(sq_ptr, 0x5a, ring.sq.ring_sz - 2 * sizeof(__u32));
- memset(cq_ptr, 0xa5, ring.cq.ring_sz - 2 * sizeof(__u32));
- io_uring_unmap_rings(&ring.sq, &ring.cq);
- } while (mtime_since_now(&tv) < 2500);
- exit(T_EXIT_PASS);
- }
- static int test_mmap_race(struct io_uring *ring, struct io_uring_params *__p)
- {
- unsigned long useless_sum;
- int i, w, nr_children;
- struct timeval tv;
- pid_t pid;
- nr_children = sysconf(_SC_NPROCESSORS_ONLN);
- if (nr_children < 0)
- nr_children = 4;
- for (i = 0; i < nr_children; i++) {
- pid = fork();
- if (!pid) {
- mmap_child(ring, __p);
- return T_EXIT_PASS;
- }
- }
- useless_sum = 0;
- gettimeofday(&tv, NULL);
- do {
- struct io_uring_params p = { .sq_entries = 32, };
- void *ptr;
- io_uring_resize_rings(ring, &p);
- ptr = memchr(ring->sq.ring_ptr, 0x5a, ring->sq.ring_sz);
- if (ptr)
- useless_sum += ptr - ring->sq.ring_ptr;
- ptr = memchr(ring->cq.ring_ptr, 0xa5, ring->cq.ring_sz);
- if (ptr)
- useless_sum += ptr - ring->cq.ring_ptr;
- p.sq_entries = 128;
- io_uring_resize_rings(ring, &p);
- } while (mtime_since_now(&tv) < 2500);
- for (i = 0; i < nr_children; i++)
- wait(&w);
- if (useless_sum)
- return T_EXIT_PASS;
- return T_EXIT_PASS;
- }
- static int test(int flags, int fd, int async)
- {
- struct io_uring_params p = {
- .flags = flags,
- };
- struct io_uring ring;
- int ret;
- if (no_defer)
- return T_EXIT_SKIP;
- if (!(flags & IORING_SETUP_DEFER_TASKRUN) && only_defer)
- return T_EXIT_SKIP;
- ret = io_uring_queue_init_params(8, &ring, &p);
- if (ret < 0) {
- fprintf(stderr, "ring setup failed: %d\n", ret);
- return T_EXIT_FAIL;
- }
- ret = test_basic(&ring, async);
- if (ret == T_EXIT_SKIP) {
- if (!(flags & IORING_SETUP_DEFER_TASKRUN)) {
- io_uring_queue_exit(&ring);
- only_defer = true;
- } else {
- no_defer = true;
- }
- return T_EXIT_SKIP;
- } else if (ret == T_EXIT_FAIL) {
- fprintf(stderr, "test_basic %x failed\n", flags);
- return T_EXIT_FAIL;
- }
- ret = test_reads(&ring, fd, async);
- if (ret == T_EXIT_FAIL) {
- fprintf(stderr, "test_reads %x failed\n", flags);
- return T_EXIT_FAIL;
- }
- ret = test_pipes(&ring, async);
- if (ret == T_EXIT_FAIL) {
- fprintf(stderr, "test_pipes %x failed\n", flags);
- return T_EXIT_FAIL;
- }
- if (async)
- return T_EXIT_PASS;
- ret = test_all_copy(&ring);
- if (ret == T_EXIT_FAIL) {
- fprintf(stderr, "test_all_copy %x failed\n", flags);
- return T_EXIT_FAIL;
- }
- ret = test_overflow(&ring);
- if (ret == T_EXIT_FAIL) {
- fprintf(stderr, "test_overflow %x failed\n", flags);
- return T_EXIT_FAIL;
- }
- ret = test_same_resize(flags);
- if (ret == T_EXIT_FAIL) {
- fprintf(stderr, "test_same_resize %x failed\n", flags);
- return T_EXIT_FAIL;
- }
- /* must go at the end, insert more tests above this one */
- ret = test_mmap_race(&ring, &p);
- if (ret == T_EXIT_FAIL) {
- fprintf(stderr, "test_mmap_race %x failed\n", flags);
- return T_EXIT_FAIL;
- }
- io_uring_queue_exit(&ring);
- return T_EXIT_PASS;
- }
- int main(int argc, char *argv[])
- {
- int ret, fd = -1;
- if (argc > 1)
- fd = open("/dev/nvme0n1", O_RDONLY | O_DIRECT);
- ret = test(0, fd, 0);
- if (ret == T_EXIT_SKIP)
- goto try_defer;
- else if (ret == T_EXIT_FAIL)
- return T_EXIT_FAIL;
- ret = test(0, fd, 1);
- if (ret == T_EXIT_FAIL)
- return T_EXIT_FAIL;
- ret = test(IORING_SETUP_SQPOLL, fd, 0);
- if (ret == T_EXIT_FAIL)
- return T_EXIT_FAIL;
- ret = test(IORING_SETUP_SQPOLL, fd, 1);
- if (ret == T_EXIT_FAIL)
- return T_EXIT_FAIL;
- try_defer:
- ret = test(IORING_SETUP_SINGLE_ISSUER | IORING_SETUP_DEFER_TASKRUN, fd, 0);
- if (ret == T_EXIT_FAIL)
- return T_EXIT_FAIL;
- ret = test(IORING_SETUP_SINGLE_ISSUER | IORING_SETUP_DEFER_TASKRUN, fd, 1);
- if (ret == T_EXIT_FAIL)
- return T_EXIT_FAIL;
- return T_EXIT_PASS;
- }
|