mirror of
https://github.com/openharmony/third_party_liburing.git
synced 2026-08-26 18:26:44 -04:00
4b8f3098a5
Co-authored-by: Pavel Begunkov<asml.silence@gmail.com> Co-authored-by: Guillem Jover<guillem@hadrons.org> Co-authored-by: Jens Axboe<axboe@kernel.dk> Co-authored-by: Khem Raj<raj.khem@gmail.com> Co-authored-by: Michael de Lang<kingoipo@gmail.com> Co-authored-by: David Disseldorp<ddiss@suse.de> Co-authored-by: Ming Lei<ming.lei@redhat.com>
581 lines
13 KiB
C
581 lines
13 KiB
C
/* 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;
|
|
void *rw_ptr;
|
|
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_ptr = (void *) base + offset;
|
|
memcpy(rw_ptr, &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 = t_aligned_alloc(page_size, page_size * 4);
|
|
if (!buffer) {
|
|
fprintf(stderr, "allocation failed\n");
|
|
return T_EXIT_FAIL;
|
|
}
|
|
|
|
rd.user_addr = uring_ptr_to_u64(buffer);
|
|
rd.size = page_size;
|
|
rd.flags = IORING_MEM_REGION_TYPE_USER;
|
|
mr.region_uptr = uring_ptr_to_u64(&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 = t_aligned_alloc(page_size, page_size * 4);
|
|
if (!buffer) {
|
|
fprintf(stderr, "allocation failed\n");
|
|
return T_EXIT_FAIL;
|
|
}
|
|
|
|
rd.user_addr = uring_ptr_to_u64(buffer);
|
|
rd.size = page_size;
|
|
rd.flags = IORING_MEM_REGION_TYPE_USER;
|
|
|
|
mr.region_uptr = uring_ptr_to_u64(&rd);
|
|
mr.flags = IORING_MEM_REGION_REG_WAIT_ARG;
|
|
|
|
ret = test_try_register_region(&mr, true);
|
|
if (ret == -EINVAL) {
|
|
free(buffer);
|
|
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 = uring_ptr_to_u64(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 = uring_ptr_to_u64(&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 = uring_ptr_to_u64(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 = uring_ptr_to_u64(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 = uring_ptr_to_u64(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 = uring_ptr_to_u64(&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 = uring_ptr_to_u64(&rd);
|
|
mr.flags = IORING_MEM_REGION_REG_WAIT_ARG;
|
|
rd.user_addr = uring_ptr_to_u64(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;
|
|
}
|