123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578 |
- #include "../config-host.h"
- /* SPDX-License-Identifier: MIT */
- /*
- * Description: Test that registered waits work
- */
- #include <errno.h>
- #include <stdio.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <string.h>
- #include <fcntl.h>
- #include <sys/time.h>
- #include <sys/mman.h>
- #include <linux/mman.h>
- #include "liburing.h"
- #include "helpers.h"
- #include "test.h"
- #include "../src/syscall.h"
- static const struct io_uring_reg_wait brief_wait = {
- .flags = IORING_REG_WAIT_TS,
- .ts.tv_sec = 0,
- .ts.tv_nsec = 1000,
- };
- static bool has_kernel_regions;
- static int test_wait_reg_offset(struct io_uring *ring,
- unsigned wait_nr, unsigned long offset)
- {
- return __sys_io_uring_enter2(ring->ring_fd, 0, wait_nr,
- IORING_ENTER_GETEVENTS |
- IORING_ENTER_EXT_ARG |
- IORING_ENTER_EXT_ARG_REG,
- (void *)offset,
- sizeof(struct io_uring_reg_wait));
- }
- static int __init_ring_with_region(struct io_uring *ring, unsigned ring_flags,
- struct io_uring_mem_region_reg *pr,
- bool disabled)
- {
- int flags = disabled ? IORING_SETUP_R_DISABLED : 0;
- int ret;
- ret = io_uring_queue_init(8, ring, flags);
- if (ret) {
- if (ret != -EINVAL)
- fprintf(stderr, "ring setup failed: %d\n", ret);
- return ret;
- }
- ret = io_uring_register_region(ring, pr);
- if (ret)
- goto err;
- if (disabled) {
- ret = io_uring_enable_rings(ring);
- if (ret) {
- fprintf(stderr, "io_uring_enable_rings failure %i\n", ret);
- goto err;
- }
- }
- return 0;
- err:
- io_uring_queue_exit(ring);
- return ret;
- }
- static int page_size;
- static struct io_uring_reg_wait *reg;
- static int test_invalid_sig(struct io_uring *ring)
- {
- struct io_uring_cqe *cqe;
- sigset_t sig;
- int ret;
- memset(reg, 0, sizeof(*reg));
- reg->ts.tv_sec = 1;
- reg->ts.tv_nsec = 0;
- reg->sigmask = (unsigned long) &sig;
- reg->sigmask_sz = 1;
- ret = io_uring_submit_and_wait_reg(ring, &cqe, 1, 0);
- if (ret != -EINVAL) {
- fprintf(stderr, "sigmask_sz failed: %d\n", ret);
- return T_EXIT_FAIL;
- }
- memset(reg, 0, sizeof(*reg));
- reg->ts.tv_sec = 1;
- reg->ts.tv_nsec = 0;
- reg->sigmask = 100;
- reg->sigmask_sz = 8;
- ret = io_uring_submit_and_wait_reg(ring, &cqe, 1, 0);
- if (ret != -EFAULT) {
- fprintf(stderr, "sigmask invalid failed: %d\n", ret);
- return T_EXIT_FAIL;
- }
- return T_EXIT_PASS;
- }
- static int test_offsets(struct io_uring *ring, struct io_uring_reg_wait *base,
- size_t size, bool overallocated)
- {
- struct io_uring_cqe *cqe;
- int max_index = size / sizeof(struct io_uring_reg_wait);
- struct io_uring_reg_wait *rw;
- unsigned long offset;
- int copy_size;
- int ret;
- rw = base;
- memcpy(rw, &brief_wait, sizeof(brief_wait));
- ret = io_uring_submit_and_wait_reg(ring, &cqe, 1, 0);
- if (ret != -ETIME) {
- fprintf(stderr, "0 index failed: %d\n", ret);
- return T_EXIT_FAIL;
- }
- if (overallocated) {
- rw = base + max_index;
- memcpy(rw, &brief_wait, sizeof(brief_wait));
- }
- ret = io_uring_submit_and_wait_reg(ring, &cqe, 1, max_index);
- if (ret != -EFAULT) {
- fprintf(stderr, "max+1 index failed: %d\n", ret);
- return T_EXIT_FAIL;
- }
- rw = base + max_index - 1;
- memcpy(rw, &brief_wait, sizeof(brief_wait));
- ret = io_uring_submit_and_wait_reg(ring, &cqe, 1, max_index - 1);
- if (ret != -ETIME) {
- fprintf(stderr, "last index failed: %d\n", ret);
- return T_EXIT_FAIL;
- }
- offset = 0UL - sizeof(long);
- ret = test_wait_reg_offset(ring, 1, offset);
- if (ret != -EFAULT) {
- fprintf(stderr, "overflow offset failed: %d\n", ret);
- return T_EXIT_FAIL;
- }
- offset = size - sizeof(long);
- rw = (void *)base + offset;
- copy_size = overallocated ? sizeof(brief_wait) : sizeof(long);
- memcpy(rw, &brief_wait, copy_size);
- ret = test_wait_reg_offset(ring, 1, offset);
- if (ret != -EFAULT) {
- fprintf(stderr, "OOB offset failed: %d\n", ret);
- return T_EXIT_FAIL;
- }
- offset = 1;
- rw = (void *)base + offset;
- memcpy(rw, &brief_wait, sizeof(brief_wait));
- /* undefined behaviour, check the kernel doesn't crash */
- (void)test_wait_reg_offset(ring, 1, offset);
- return 0;
- }
- static int test_basic(struct io_uring *ring)
- {
- struct io_uring_cqe *cqe;
- struct timeval tv;
- int ret;
- memset(reg, 0, sizeof(*reg));
- reg->ts.tv_sec = 1;
- reg->ts.tv_nsec = 100000000ULL;
- reg->flags = IORING_REG_WAIT_TS;
- gettimeofday(&tv, NULL);
- ret = io_uring_submit_and_wait_reg(ring, &cqe, 2, 0);
- if (ret != -ETIME) {
- fprintf(stderr, "submit_and_wait_reg: %d\n", ret);
- goto err;
- }
- ret = mtime_since_now(&tv);
- /* allow some slack, should be around 1.1s */
- if (ret < 1000 || ret > 1200) {
- fprintf(stderr, "wait too long or short: %d\n", ret);
- goto err;
- }
- return T_EXIT_PASS;
- err:
- return T_EXIT_FAIL;
- }
- static int test_wait_arg(void)
- {
- struct io_uring_region_desc rd = {};
- struct io_uring_mem_region_reg mr = {};
- struct io_uring ring;
- void *buffer;
- int ret;
- ret = io_uring_queue_init(8, &ring, IORING_SETUP_R_DISABLED);
- if (ret) {
- if (ret == -EINVAL) {
- printf("IORING_SETUP_R_DISABLED not supported, skip\n");
- return 0;
- }
- fprintf(stderr, "ring setup failed: %d\n", ret);
- return T_EXIT_FAIL;
- }
- buffer = aligned_alloc(page_size, page_size * 4);
- if (!buffer) {
- fprintf(stderr, "allocation failed\n");
- return T_EXIT_FAIL;
- }
- rd.user_addr = (__u64)(unsigned long)buffer;
- rd.size = page_size;
- rd.flags = IORING_MEM_REGION_TYPE_USER;
- mr.region_uptr = (__u64)(unsigned long)&rd;
- mr.flags = IORING_MEM_REGION_REG_WAIT_ARG;
- ret = io_uring_register_region(&ring, &mr);
- if (ret) {
- fprintf(stderr, "region reg failed %i\n", ret);
- return 1;
- }
- ret = io_uring_enable_rings(&ring);
- if (ret) {
- fprintf(stderr, "io_uring_enable_rings failure %i\n", ret);
- return T_EXIT_FAIL;
- }
- reg = buffer;
- ret = test_basic(&ring);
- if (ret == T_EXIT_FAIL) {
- fprintf(stderr, "test failed\n");
- goto err;
- }
- ret = test_invalid_sig(&ring);
- if (ret == T_EXIT_FAIL) {
- fprintf(stderr, "test_invalid sig failed\n");
- goto err;
- }
- ret = test_offsets(&ring, buffer, page_size, true);
- if (ret == T_EXIT_FAIL) {
- fprintf(stderr, "test_offsets failed\n");
- goto err;
- }
- err:
- free(buffer);
- io_uring_queue_exit(&ring);
- return ret;
- }
- static int test_try_register_region(struct io_uring_mem_region_reg *pr,
- bool disabled)
- {
- struct io_uring ring;
- int ret;
- ret = __init_ring_with_region(&ring, 0, pr, disabled);
- if (!ret)
- io_uring_queue_exit(&ring);
- return ret;
- }
- static int test_regions(void)
- {
- struct io_uring_region_desc rd = {};
- struct io_uring_mem_region_reg mr = {};
- void *buffer;
- int ret;
- buffer = aligned_alloc(page_size, page_size * 4);
- if (!buffer) {
- fprintf(stderr, "allocation failed\n");
- return T_EXIT_FAIL;
- }
- rd.user_addr = (__u64)(unsigned long)buffer;
- rd.size = page_size;
- rd.flags = IORING_MEM_REGION_TYPE_USER;
- mr.region_uptr = (__u64)(unsigned long)&rd;
- mr.flags = IORING_MEM_REGION_REG_WAIT_ARG;
- ret = test_try_register_region(&mr, true);
- if (ret == -EINVAL)
- return T_EXIT_SKIP;
- if (ret) {
- fprintf(stderr, "region: register normal fail %i\n", ret);
- return T_EXIT_FAIL;
- }
- ret = test_try_register_region(&mr, false);
- if (ret != -EINVAL) {
- fprintf(stderr, "region: register with !R_DISABLED fail %i\n", ret);
- return T_EXIT_FAIL;
- }
- rd.size = page_size * 4;
- ret = test_try_register_region(&mr, true);
- if (ret) {
- fprintf(stderr, "test_try_register_region() 16KB fail %i\n", ret);
- return T_EXIT_FAIL;
- }
- rd.size = page_size;
- rd.user_addr = 0;
- ret = test_try_register_region(&mr, true);
- if (ret != -EFAULT) {
- fprintf(stderr, "test_try_register_region() null uptr fail %i\n", ret);
- return T_EXIT_FAIL;
- }
- rd.user_addr = (__u64)(unsigned long)buffer;
- rd.flags = 0;
- ret = test_try_register_region(&mr, true);
- if (!ret) {
- fprintf(stderr, "test_try_register_region() kernel alloc with uptr fail %i\n", ret);
- return T_EXIT_FAIL;
- }
- rd.flags = IORING_MEM_REGION_TYPE_USER;
- rd.size = 0;
- ret = test_try_register_region(&mr, true);
- if (!ret) {
- fprintf(stderr, "test_try_register_region() 0-size fail %i\n", ret);
- return T_EXIT_FAIL;
- }
- rd.size = page_size;
- mr.region_uptr = 0;
- ret = test_try_register_region(&mr, true);
- if (!ret) {
- fprintf(stderr, "test_try_register_region() NULL region %i\n", ret);
- return T_EXIT_FAIL;
- }
- mr.region_uptr = (__u64)(unsigned long)&rd;
- rd.user_addr += 16;
- ret = test_try_register_region(&mr, true);
- if (!ret) {
- fprintf(stderr, "test_try_register_region() misaligned region %i\n", ret);
- return T_EXIT_FAIL;
- }
- rd.user_addr = 0x1000;
- ret = test_try_register_region(&mr, true);
- if (!ret) {
- fprintf(stderr, "test_try_register_region() bogus uptr %i\n", ret);
- return T_EXIT_FAIL;
- }
- rd.user_addr = (__u64)(unsigned long)buffer;
- free(buffer);
- buffer = mmap(NULL, page_size, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
- if (buffer == MAP_FAILED) {
- fprintf(stderr, "mmap alloc failed\n");
- return 1;
- }
- rd.user_addr = (__u64)(unsigned long)buffer;
- ret = test_try_register_region(&mr, true);
- if (ret != -EFAULT) {
- fprintf(stderr, "test_try_register_region() RO uptr %i\n", ret);
- return T_EXIT_FAIL;
- }
- rd.flags = 0;
- rd.user_addr = 0;
- ret = test_try_register_region(&mr, true);
- if (ret == -EINVAL) {
- has_kernel_regions = false;
- goto out;
- }
- if (ret) {
- fprintf(stderr, "test_try_register_region() failed kernel alloc %i\n", ret);
- return T_EXIT_FAIL;
- }
- has_kernel_regions = true;
- rd.flags = 0;
- rd.user_addr = (__u64)(unsigned long)buffer;
- ret = test_try_register_region(&mr, true);
- if (!ret) {
- fprintf(stderr, "test_try_register_region() failed uptr w kernel alloc %i\n", ret);
- return T_EXIT_FAIL;
- }
- out:
- munmap(buffer, page_size);
- return 0;
- }
- struct t_region {
- void *ptr;
- bool user_mem;
- size_t size;
- };
- static void t_region_free(struct t_region *r)
- {
- if (r->ptr)
- munmap(r->ptr, r->size);
- }
- static int t_region_create_kernel(struct t_region *r,
- struct io_uring *ring)
- {
- struct io_uring_region_desc rd = { .size = r->size, };
- struct io_uring_mem_region_reg mr = {
- .region_uptr = (__u64)(unsigned long)&rd,
- .flags = IORING_MEM_REGION_REG_WAIT_ARG,
- };
- void *p;
- int ret;
- ret = io_uring_register_region(ring, &mr);
- if (ret)
- return ret;
- p = mmap(NULL, r->size, PROT_READ | PROT_WRITE,
- MAP_SHARED | MAP_POPULATE, ring->ring_fd, rd.mmap_offset);
- if (p == MAP_FAILED)
- return -EFAULT;
- r->ptr = p;
- r->user_mem = false;
- return 0;
- }
- static int t_region_create_user(struct t_region *r,
- struct io_uring *ring,
- bool huge)
- {
- struct io_uring_region_desc rd = {};
- struct io_uring_mem_region_reg mr = {};
- int flags = MAP_PRIVATE | MAP_ANONYMOUS;
- void *p;
- int ret;
- if (huge)
- flags |= MAP_HUGETLB | MAP_HUGE_2MB;
- p = mmap(NULL, r->size, PROT_READ | PROT_WRITE, flags, -1, 0);
- if (p == MAP_FAILED)
- return -ENOMEM;
- mr.region_uptr = (__u64)(unsigned long)&rd;
- mr.flags = IORING_MEM_REGION_REG_WAIT_ARG;
- rd.user_addr = (__u64)(unsigned long)p;
- rd.flags = IORING_MEM_REGION_TYPE_USER;
- rd.size = r->size;
- ret = io_uring_register_region(ring, &mr);
- if (ret) {
- munmap(p, r->size);
- return ret;
- }
- r->ptr = p;
- r->user_mem = true;
- return 0;
- }
- struct test_param {
- size_t size;
- bool huge_page;
- bool kern_buf;
- };
- static int test_region_buffer_types(void)
- {
- const size_t huge_size = 1024 * 1024 * 2;
- struct test_param params[] = {
- { .size = page_size },
- /* forcing vmap */
- { .size = page_size * 2 },
- { .size = page_size * 16 },
- /* huge page w/o vmap */
- { .size = huge_size, .huge_page = true },
- /* huge page w/ vmap */
- { .size = huge_size * 2, .huge_page = true },
- { .size = page_size, .kern_buf = true },
- /* likely to be a compound page */
- { .size = page_size * 2, .kern_buf = true },
- { .size = page_size * 8, .kern_buf = true },
- /* kernel allocation + vmap */
- { .size = page_size * 512, .kern_buf = true },
- };
- struct io_uring ring;
- int i, ret;
- for (i = 0; i < ARRAY_SIZE(params); i++) {
- struct t_region r = { .size = params[i].size, };
- ret = io_uring_queue_init(8, &ring, IORING_SETUP_R_DISABLED);
- if (ret) {
- fprintf(stderr, "ring setup failed: %d\n", ret);
- return ret;
- }
- if (params[i].kern_buf)
- ret = t_region_create_kernel(&r, &ring);
- else
- ret = t_region_create_user(&r, &ring, params[i].huge_page);
- if (ret) {
- io_uring_queue_exit(&ring);
- if (ret == -ENOMEM || ret == -EINVAL)
- continue;
- fprintf(stderr, "t_region_create_user failed, idx %i\n", i);
- return 1;
- }
- ret = io_uring_enable_rings(&ring);
- if (ret) {
- fprintf(stderr, "io_uring_enable_rings failure %i\n", ret);
- return ret;
- }
- ret = test_offsets(&ring, r.ptr, r.size, false);
- if (ret) {
- fprintf(stderr, "test_offsets failed, idx %i\n", i);
- return 1;
- }
- t_region_free(&r);
- io_uring_queue_exit(&ring);
- }
- return 0;
- }
- int main(int argc, char *argv[])
- {
- int ret;
- if (argc > 1)
- return 0;
- page_size = sysconf(_SC_PAGESIZE);
- if (page_size < 0) {
- perror("sysconf(_SC_PAGESIZE)");
- return 1;
- }
- ret = test_regions();
- if (ret == T_EXIT_SKIP) {
- printf("regions are not supported, skip\n");
- return 0;
- } else if (ret) {
- fprintf(stderr, "test_region failed\n");
- return 1;
- }
- ret = test_wait_arg();
- if (ret == T_EXIT_FAIL) {
- fprintf(stderr, "test_wait_arg failed\n");
- return 1;
- }
- ret = test_region_buffer_types();
- if (ret == T_EXIT_FAIL) {
- fprintf(stderr, "test_region_buffer_types failed\n");
- return 1;
- }
- return 0;
- }
|