|
Packit |
d3489f |
/* SPDX-License-Identifier: MIT */
|
|
Packit |
d3489f |
/*
|
|
Packit |
d3489f |
* io_uring_register.c
|
|
Packit |
d3489f |
*
|
|
Packit |
d3489f |
* Description: Unit tests for the io_uring_register system call.
|
|
Packit |
d3489f |
*
|
|
Packit |
d3489f |
* Copyright 2019, Red Hat, Inc.
|
|
Packit |
d3489f |
* Author: Jeff Moyer <jmoyer@redhat.com>
|
|
Packit |
d3489f |
*/
|
|
Packit |
d3489f |
#include <stdio.h>
|
|
Packit |
d3489f |
#include <fcntl.h>
|
|
Packit |
d3489f |
#include <string.h>
|
|
Packit |
d3489f |
#include <stdlib.h>
|
|
Packit |
d3489f |
#include <unistd.h>
|
|
Packit |
d3489f |
#include <errno.h>
|
|
Packit |
d3489f |
#include <sys/sysinfo.h>
|
|
Packit |
d3489f |
#include <poll.h>
|
|
Packit |
d3489f |
#include <assert.h>
|
|
Packit |
d3489f |
#include <sys/uio.h>
|
|
Packit |
d3489f |
#include <sys/mman.h>
|
|
Packit |
d3489f |
#include <linux/mman.h>
|
|
Packit |
d3489f |
#include <sys/time.h>
|
|
Packit |
d3489f |
#include <sys/resource.h>
|
|
Packit |
d3489f |
#include <limits.h>
|
|
Packit |
d3489f |
#include "liburing.h"
|
|
Packit |
d3489f |
#include "../src/syscall.h"
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
static int pagesize;
|
|
Packit |
d3489f |
static rlim_t mlock_limit;
|
|
Packit |
d3489f |
static int devnull;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
int
|
|
Packit |
d3489f |
expect_fail(int fd, unsigned int opcode, void *arg,
|
|
Packit |
d3489f |
unsigned int nr_args, int error)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
int ret;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
printf("io_uring_register(%d, %u, %p, %u)\n",
|
|
Packit |
d3489f |
fd, opcode, arg, nr_args);
|
|
Packit |
d3489f |
ret = __sys_io_uring_register(fd, opcode, arg, nr_args);
|
|
Packit |
d3489f |
if (ret != -1) {
|
|
Packit |
d3489f |
int ret2 = 0;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
printf("expected %s, but call succeeded\n", strerror(error));
|
|
Packit |
d3489f |
if (opcode == IORING_REGISTER_BUFFERS) {
|
|
Packit |
d3489f |
ret2 = __sys_io_uring_register(fd,
|
|
Packit |
d3489f |
IORING_UNREGISTER_BUFFERS, 0, 0);
|
|
Packit |
d3489f |
} else if (opcode == IORING_REGISTER_FILES) {
|
|
Packit |
d3489f |
ret2 = __sys_io_uring_register(fd,
|
|
Packit |
d3489f |
IORING_UNREGISTER_FILES, 0, 0);
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
if (ret2) {
|
|
Packit |
d3489f |
printf("internal error: failed to unregister\n");
|
|
Packit |
d3489f |
exit(1);
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (errno != error) {
|
|
Packit |
d3489f |
printf("expected %d, got %d\n", error, errno);
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
return 0;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
int
|
|
Packit |
d3489f |
new_io_uring(int entries, struct io_uring_params *p)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
int fd;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
fd = __sys_io_uring_setup(entries, p);
|
|
Packit |
d3489f |
if (fd < 0) {
|
|
Packit |
d3489f |
perror("io_uring_setup");
|
|
Packit |
d3489f |
exit(1);
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
return fd;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
#define MAXFDS (UINT_MAX * sizeof(int))
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
void *
|
|
Packit |
d3489f |
map_filebacked(size_t size)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
int fd, ret;
|
|
Packit |
d3489f |
void *addr;
|
|
Packit |
d3489f |
char template[32] = "io_uring_register-test-XXXXXXXX";
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
fd = mkstemp(template);
|
|
Packit |
d3489f |
if (fd < 0) {
|
|
Packit |
d3489f |
perror("mkstemp");
|
|
Packit |
d3489f |
return NULL;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
unlink(template);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = ftruncate(fd, size);
|
|
Packit |
d3489f |
if (ret < 0) {
|
|
Packit |
d3489f |
perror("ftruncate");
|
|
Packit |
d3489f |
close(fd);
|
|
Packit |
d3489f |
return NULL;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
addr = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
|
|
Packit |
d3489f |
if (addr == MAP_FAILED) {
|
|
Packit |
d3489f |
perror("mmap");
|
|
Packit |
d3489f |
close(fd);
|
|
Packit |
d3489f |
return NULL;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
close(fd);
|
|
Packit |
d3489f |
return addr;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
/*
|
|
Packit |
d3489f |
* NOTE: this is now limited by SCM_MAX_FD (253). Keep the code for now,
|
|
Packit |
d3489f |
* but probably should augment it to test 253 and 254, specifically.
|
|
Packit |
d3489f |
*/
|
|
Packit |
d3489f |
int
|
|
Packit |
d3489f |
test_max_fds(int uring_fd)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
int status = 1;
|
|
Packit |
d3489f |
int ret;
|
|
Packit |
d3489f |
void *fd_as; /* file descriptor address space */
|
|
Packit |
d3489f |
int fdtable_fd; /* fd for the file that will be mapped over and over */
|
|
Packit |
d3489f |
int io_fd; /* the valid fd for I/O -- /dev/null */
|
|
Packit |
d3489f |
int *fds; /* used to map the file into the address space */
|
|
Packit |
d3489f |
char template[32] = "io_uring_register-test-XXXXXXXX";
|
|
Packit |
d3489f |
unsigned long long i, nr_maps, nr_fds;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
/*
|
|
Packit |
d3489f |
* First, mmap anonymous the full size. That will guarantee the
|
|
Packit |
d3489f |
* mapping will fit in the memory area selected by mmap. Then,
|
|
Packit |
d3489f |
* over-write that mapping using a file-backed mapping, 128MiB at
|
|
Packit |
d3489f |
* a time using MAP_FIXED.
|
|
Packit |
d3489f |
*/
|
|
Packit |
d3489f |
fd_as = mmap(NULL, UINT_MAX * sizeof(int), PROT_READ|PROT_WRITE,
|
|
Packit |
d3489f |
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
|
|
Packit |
d3489f |
if (fd_as == MAP_FAILED) {
|
|
Packit |
d3489f |
if (errno == ENOMEM) {
|
|
Packit |
d3489f |
printf("Not enough memory for this test, skipping\n");
|
|
Packit |
d3489f |
return 0;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
perror("mmap fd_as");
|
|
Packit |
d3489f |
exit(1);
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
printf("allocated %zu bytes of address space\n", UINT_MAX * sizeof(int));
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
fdtable_fd = mkstemp(template);
|
|
Packit |
d3489f |
if (fdtable_fd < 0) {
|
|
Packit |
d3489f |
perror("mkstemp");
|
|
Packit |
d3489f |
exit(1);
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
unlink(template);
|
|
Packit |
d3489f |
ret = ftruncate(fdtable_fd, 128*1024*1024);
|
|
Packit |
d3489f |
if (ret < 0) {
|
|
Packit |
d3489f |
perror("ftruncate");
|
|
Packit |
d3489f |
exit(1);
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
io_fd = open("/dev/null", O_RDWR);
|
|
Packit |
d3489f |
if (io_fd < 0) {
|
|
Packit |
d3489f |
perror("open /dev/null");
|
|
Packit |
d3489f |
exit(1);
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
fds = mmap(fd_as, 128*1024*1024, PROT_READ|PROT_WRITE,
|
|
Packit |
d3489f |
MAP_SHARED|MAP_FIXED, fdtable_fd, 0);
|
|
Packit |
d3489f |
if (fds == MAP_FAILED) {
|
|
Packit |
d3489f |
perror("mmap fdtable");
|
|
Packit |
d3489f |
exit(1);
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
/* fill the fd table */
|
|
Packit |
d3489f |
nr_fds = 128*1024*1024 / sizeof(int);
|
|
Packit |
d3489f |
for (i = 0; i < nr_fds; i++)
|
|
Packit |
d3489f |
fds[i] = io_fd;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
/* map the file through the rest of the address space */
|
|
Packit |
d3489f |
nr_maps = (UINT_MAX * sizeof(int)) / (128*1024*1024);
|
|
Packit |
d3489f |
for (i = 0; i < nr_maps; i++) {
|
|
Packit |
d3489f |
fds = &fds[nr_fds]; /* advance fds by 128MiB */
|
|
Packit |
d3489f |
fds = mmap(fds, 128*1024*1024, PROT_READ|PROT_WRITE,
|
|
Packit |
d3489f |
MAP_SHARED|MAP_FIXED, fdtable_fd, 0);
|
|
Packit |
d3489f |
if (fds == MAP_FAILED) {
|
|
Packit |
d3489f |
printf("mmap failed at offset %lu\n",
|
|
Packit |
d3489f |
(unsigned long)((char *)fd_as - (char *)fds));
|
|
Packit |
d3489f |
exit(1);
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
/* Now fd_as points to the file descriptor array. */
|
|
Packit |
d3489f |
/*
|
|
Packit |
d3489f |
* We may not be able to map all of these files. Let's back off
|
|
Packit |
d3489f |
* until success.
|
|
Packit |
d3489f |
*/
|
|
Packit |
d3489f |
nr_fds = UINT_MAX;
|
|
Packit |
d3489f |
while (nr_fds) {
|
|
Packit |
d3489f |
ret = __sys_io_uring_register(uring_fd, IORING_REGISTER_FILES,
|
|
Packit |
d3489f |
fd_as, nr_fds);
|
|
Packit |
d3489f |
if (ret != 0) {
|
|
Packit |
d3489f |
nr_fds /= 2;
|
|
Packit |
d3489f |
continue;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
printf("io_uring_register(%d, IORING_REGISTER_FILES, %p, %llu)"
|
|
Packit |
d3489f |
"...succeeded\n", uring_fd, fd_as, nr_fds);
|
|
Packit |
d3489f |
status = 0;
|
|
Packit |
d3489f |
printf("io_uring_register(%d, IORING_UNREGISTER_FILES, 0, 0)...",
|
|
Packit |
d3489f |
uring_fd);
|
|
Packit |
d3489f |
ret = __sys_io_uring_register(uring_fd, IORING_UNREGISTER_FILES,
|
|
Packit |
d3489f |
0, 0);
|
|
Packit |
d3489f |
if (ret < 0) {
|
|
Packit |
d3489f |
ret = errno;
|
|
Packit |
d3489f |
printf("failed\n");
|
|
Packit |
d3489f |
errno = ret;
|
|
Packit |
d3489f |
perror("io_uring_register UNREGISTER_FILES");
|
|
Packit |
d3489f |
exit(1);
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
printf("succeeded\n");
|
|
Packit |
d3489f |
break;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
close(io_fd);
|
|
Packit |
d3489f |
close(fdtable_fd);
|
|
Packit |
d3489f |
ret = munmap(fd_as, UINT_MAX * sizeof(int));
|
|
Packit |
d3489f |
if (ret != 0) {
|
|
Packit |
d3489f |
printf("munmap(%zu) failed\n", UINT_MAX * sizeof(int));
|
|
Packit |
d3489f |
exit(1);
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
return status;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
int
|
|
Packit |
d3489f |
test_memlock_exceeded(int fd)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
int ret;
|
|
Packit |
d3489f |
void *buf;
|
|
Packit |
d3489f |
struct iovec iov;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
/* if limit is larger than 2gb, just skip this test */
|
|
Packit |
d3489f |
if (mlock_limit >= 2 * 1024 * 1024 * 1024ULL)
|
|
Packit |
d3489f |
return 0;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
iov.iov_len = mlock_limit * 2;
|
|
Packit |
d3489f |
buf = malloc(iov.iov_len);
|
|
Packit |
d3489f |
assert(buf);
|
|
Packit |
d3489f |
iov.iov_base = buf;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
while (iov.iov_len) {
|
|
Packit |
d3489f |
ret = __sys_io_uring_register(fd, IORING_REGISTER_BUFFERS, &iov, 1);
|
|
Packit |
d3489f |
if (ret < 0) {
|
|
Packit |
d3489f |
if (errno == ENOMEM) {
|
|
Packit |
d3489f |
printf("io_uring_register of %zu bytes failed "
|
|
Packit |
d3489f |
"with ENOMEM (expected).\n", iov.iov_len);
|
|
Packit |
d3489f |
iov.iov_len /= 2;
|
|
Packit |
d3489f |
continue;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
printf("expected success or EFAULT, got %d\n", errno);
|
|
Packit |
d3489f |
free(buf);
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
printf("successfully registered %zu bytes (%d).\n",
|
|
Packit |
d3489f |
iov.iov_len, ret);
|
|
Packit |
d3489f |
ret = __sys_io_uring_register(fd, IORING_UNREGISTER_BUFFERS,
|
|
Packit |
d3489f |
NULL, 0);
|
|
Packit |
d3489f |
if (ret != 0) {
|
|
Packit |
d3489f |
printf("error: unregister failed with %d\n", errno);
|
|
Packit |
d3489f |
free(buf);
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
break;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
if (!iov.iov_len)
|
|
Packit |
d3489f |
printf("Unable to register buffers. Check memlock rlimit.\n");
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
free(buf);
|
|
Packit |
d3489f |
return 0;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
int
|
|
Packit |
d3489f |
test_iovec_nr(int fd)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
int i, ret, status = 0;
|
|
Packit |
d3489f |
unsigned int nr = UIO_MAXIOV + 1;
|
|
Packit |
d3489f |
struct iovec *iovs;
|
|
Packit |
d3489f |
void *buf;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
buf = malloc(pagesize);
|
|
Packit |
d3489f |
assert(buf);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
iovs = malloc(nr * sizeof(struct iovec));
|
|
Packit |
d3489f |
assert(iovs);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
for (i = 0; i < nr; i++) {
|
|
Packit |
d3489f |
iovs[i].iov_base = buf;
|
|
Packit |
d3489f |
iovs[i].iov_len = pagesize;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
status |= expect_fail(fd, IORING_REGISTER_BUFFERS, iovs, nr, EINVAL);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
/* reduce to UIO_MAXIOV */
|
|
Packit |
d3489f |
nr--;
|
|
Packit |
d3489f |
printf("io_uring_register(%d, %u, %p, %u)\n",
|
|
Packit |
d3489f |
fd, IORING_REGISTER_BUFFERS, iovs, nr);
|
|
Packit |
d3489f |
ret = __sys_io_uring_register(fd, IORING_REGISTER_BUFFERS, iovs, nr);
|
|
Packit |
d3489f |
if (ret != 0) {
|
|
Packit |
d3489f |
printf("expected success, got %d\n", errno);
|
|
Packit |
d3489f |
status = 1;
|
|
Packit |
d3489f |
} else
|
|
Packit |
d3489f |
__sys_io_uring_register(fd, IORING_UNREGISTER_BUFFERS, 0, 0);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
free(buf);
|
|
Packit |
d3489f |
free(iovs);
|
|
Packit |
d3489f |
return status;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
/*
|
|
Packit |
d3489f |
* io_uring limit is 1G. iov_len limit is ~OUL, I think
|
|
Packit |
d3489f |
*/
|
|
Packit |
d3489f |
int
|
|
Packit |
d3489f |
test_iovec_size(int fd)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
unsigned int status = 0;
|
|
Packit |
d3489f |
int ret;
|
|
Packit |
d3489f |
struct iovec iov;
|
|
Packit |
d3489f |
void *buf;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
/* NULL pointer for base */
|
|
Packit |
d3489f |
iov.iov_base = 0;
|
|
Packit |
d3489f |
iov.iov_len = 4096;
|
|
Packit |
d3489f |
status |= expect_fail(fd, IORING_REGISTER_BUFFERS, &iov, 1, EFAULT);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
/* valid base, 0 length */
|
|
Packit |
d3489f |
iov.iov_base = &buf;
|
|
Packit |
d3489f |
iov.iov_len = 0;
|
|
Packit |
d3489f |
status |= expect_fail(fd, IORING_REGISTER_BUFFERS, &iov, 1, EFAULT);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
/* valid base, length exceeds size */
|
|
Packit |
d3489f |
/* this requires an unampped page directly after buf */
|
|
Packit |
d3489f |
buf = mmap(NULL, 2 * pagesize, PROT_READ|PROT_WRITE,
|
|
Packit |
d3489f |
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
|
Packit |
d3489f |
assert(buf != MAP_FAILED);
|
|
Packit |
d3489f |
ret = munmap(buf + pagesize, pagesize);
|
|
Packit |
d3489f |
assert(ret == 0);
|
|
Packit |
d3489f |
iov.iov_base = buf;
|
|
Packit |
d3489f |
iov.iov_len = 2 * pagesize;
|
|
Packit |
d3489f |
status |= expect_fail(fd, IORING_REGISTER_BUFFERS, &iov, 1, EFAULT);
|
|
Packit |
d3489f |
munmap(buf, pagesize);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
/* huge page */
|
|
Packit |
d3489f |
buf = mmap(NULL, 2*1024*1024, PROT_READ|PROT_WRITE,
|
|
Packit |
d3489f |
MAP_PRIVATE | MAP_HUGETLB | MAP_HUGE_2MB | MAP_ANONYMOUS,
|
|
Packit |
d3489f |
-1, 0);
|
|
Packit |
d3489f |
if (buf == MAP_FAILED) {
|
|
Packit |
d3489f |
printf("Unable to map a huge page. Try increasing "
|
|
Packit |
d3489f |
"/proc/sys/vm/nr_hugepages by at least 1.\n");
|
|
Packit |
d3489f |
printf("Skipping the hugepage test\n");
|
|
Packit |
d3489f |
} else {
|
|
Packit |
d3489f |
/*
|
|
Packit |
d3489f |
* This should succeed, so long as RLIMIT_MEMLOCK is
|
|
Packit |
d3489f |
* not exceeded
|
|
Packit |
d3489f |
*/
|
|
Packit |
d3489f |
iov.iov_base = buf;
|
|
Packit |
d3489f |
iov.iov_len = 2*1024*1024;
|
|
Packit |
d3489f |
ret = __sys_io_uring_register(fd, IORING_REGISTER_BUFFERS, &iov, 1);
|
|
Packit |
d3489f |
if (ret < 0) {
|
|
Packit |
d3489f |
if (errno == ENOMEM)
|
|
Packit |
d3489f |
printf("Unable to test registering of a huge "
|
|
Packit |
d3489f |
"page. Try increasing the "
|
|
Packit |
d3489f |
"RLIMIT_MEMLOCK resource limit by at "
|
|
Packit |
d3489f |
"least 2MB.");
|
|
Packit |
d3489f |
else {
|
|
Packit |
d3489f |
printf("expected success, got %d\n", errno);
|
|
Packit |
d3489f |
status = 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
} else {
|
|
Packit |
d3489f |
printf("Success!\n");
|
|
Packit |
d3489f |
ret = __sys_io_uring_register(fd,
|
|
Packit |
d3489f |
IORING_UNREGISTER_BUFFERS, 0, 0);
|
|
Packit |
d3489f |
if (ret < 0) {
|
|
Packit |
d3489f |
perror("io_uring_unregister");
|
|
Packit |
d3489f |
status = 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
ret = munmap(iov.iov_base, iov.iov_len);
|
|
Packit |
d3489f |
assert(ret == 0);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
/* file-backed buffers -- not supported */
|
|
Packit |
d3489f |
buf = map_filebacked(2*1024*1024);
|
|
Packit |
d3489f |
if (!buf)
|
|
Packit |
d3489f |
status = 1;
|
|
Packit |
d3489f |
iov.iov_base = buf;
|
|
Packit |
d3489f |
iov.iov_len = 2*1024*1024;
|
|
Packit |
d3489f |
printf("reserve file-backed buffers\n");
|
|
Packit |
d3489f |
status |= expect_fail(fd, IORING_REGISTER_BUFFERS, &iov, 1, EOPNOTSUPP);
|
|
Packit |
d3489f |
munmap(buf, 2*1024*1024);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
/* bump up against the soft limit and make sure we get EFAULT
|
|
Packit |
d3489f |
* or whatever we're supposed to get. NOTE: this requires
|
|
Packit |
d3489f |
* running the test as non-root. */
|
|
Packit |
d3489f |
if (getuid() != 0)
|
|
Packit |
d3489f |
status |= test_memlock_exceeded(fd);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
return status;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
void
|
|
Packit |
d3489f |
dump_sqe(struct io_uring_sqe *sqe)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
printf("\topcode: %d\n", sqe->opcode);
|
|
Packit |
d3489f |
printf("\tflags: 0x%.8x\n", sqe->flags);
|
|
Packit |
d3489f |
printf("\tfd: %d\n", sqe->fd);
|
|
Packit |
d3489f |
if (sqe->opcode == IORING_OP_POLL_ADD)
|
|
Packit |
d3489f |
printf("\tpoll_events: 0x%.8x\n", sqe->poll_events);
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
int
|
|
Packit |
d3489f |
ioring_poll(struct io_uring *ring, int fd, int fixed)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
int ret;
|
|
Packit |
d3489f |
struct io_uring_sqe *sqe;
|
|
Packit |
d3489f |
struct io_uring_cqe *cqe;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
sqe = io_uring_get_sqe(ring);
|
|
Packit |
d3489f |
memset(sqe, 0, sizeof(*sqe));
|
|
Packit |
d3489f |
sqe->opcode = IORING_OP_POLL_ADD;
|
|
Packit |
d3489f |
if (fixed)
|
|
Packit |
d3489f |
sqe->flags = IOSQE_FIXED_FILE;
|
|
Packit |
d3489f |
sqe->fd = fd;
|
|
Packit |
d3489f |
sqe->poll_events = POLLIN|POLLOUT;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
printf("io_uring_submit:\n");
|
|
Packit |
d3489f |
dump_sqe(sqe);
|
|
Packit |
d3489f |
ret = io_uring_submit(ring);
|
|
Packit |
d3489f |
if (ret != 1) {
|
|
Packit |
d3489f |
printf("failed to submit poll sqe: %d.\n", errno);
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = io_uring_wait_cqe(ring, &cqe);
|
|
Packit |
d3489f |
if (ret < 0) {
|
|
Packit |
d3489f |
printf("io_uring_wait_cqe failed with %d\n", ret);
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
ret = 0;
|
|
Packit |
d3489f |
if (cqe->res != POLLOUT) {
|
|
Packit |
d3489f |
printf("io_uring_wait_cqe: expected 0x%.8x, got 0x%.8x\n",
|
|
Packit |
d3489f |
POLLOUT, cqe->res);
|
|
Packit |
d3489f |
ret = 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
io_uring_cqe_seen(ring, cqe);
|
|
Packit |
d3489f |
return ret;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
int
|
|
Packit |
d3489f |
test_poll_ringfd(void)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
int status = 0;
|
|
Packit |
d3489f |
int ret;
|
|
Packit |
d3489f |
int fd;
|
|
Packit |
d3489f |
struct io_uring ring;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = io_uring_queue_init(1, &ring, 0);
|
|
Packit |
d3489f |
if (ret) {
|
|
Packit |
d3489f |
perror("io_uring_queue_init");
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
fd = ring.ring_fd;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
/* try polling the ring fd */
|
|
Packit |
d3489f |
status = ioring_poll(&ring, fd, 0);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
/*
|
|
Packit |
d3489f |
* now register the ring fd, and try the poll again. This should
|
|
Packit |
d3489f |
* fail, because the kernel does not allow registering of the
|
|
Packit |
d3489f |
* ring_fd.
|
|
Packit |
d3489f |
*/
|
|
Packit |
d3489f |
status |= expect_fail(fd, IORING_REGISTER_FILES, &fd, 1, EBADF);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
/* tear down queue */
|
|
Packit |
d3489f |
io_uring_queue_exit(&ring);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
return status;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
int
|
|
Packit |
d3489f |
main(int argc, char **argv)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
int fd, ret;
|
|
Packit |
d3489f |
unsigned int status = 0;
|
|
Packit |
d3489f |
struct io_uring_params p;
|
|
Packit |
d3489f |
struct rlimit rlim;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (argc > 1)
|
|
Packit |
d3489f |
return 0;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
/* setup globals */
|
|
Packit |
d3489f |
pagesize = getpagesize();
|
|
Packit |
d3489f |
ret = getrlimit(RLIMIT_MEMLOCK, &rlim);
|
|
Packit |
d3489f |
if (ret < 0) {
|
|
Packit |
d3489f |
perror("getrlimit");
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
mlock_limit = rlim.rlim_cur;
|
|
Packit |
d3489f |
printf("RELIMIT_MEMLOCK: %lu (%lu)\n", rlim.rlim_cur, rlim.rlim_max);
|
|
Packit |
d3489f |
devnull = open("/dev/null", O_RDWR);
|
|
Packit |
d3489f |
if (devnull < 0) {
|
|
Packit |
d3489f |
perror("open /dev/null");
|
|
Packit |
d3489f |
exit(1);
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
/* invalid fd */
|
|
Packit |
d3489f |
status |= expect_fail(-1, 0, NULL, 0, EBADF);
|
|
Packit |
d3489f |
/* valid fd that is not an io_uring fd */
|
|
Packit |
d3489f |
status |= expect_fail(devnull, 0, NULL, 0, EOPNOTSUPP);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
/* invalid opcode */
|
|
Packit |
d3489f |
memset(&p, 0, sizeof(p));
|
|
Packit |
d3489f |
fd = new_io_uring(1, &p);
|
|
Packit |
d3489f |
ret = expect_fail(fd, ~0U, NULL, 0, EINVAL);
|
|
Packit |
d3489f |
if (ret) {
|
|
Packit |
d3489f |
/* if this succeeds, tear down the io_uring instance
|
|
Packit |
d3489f |
* and start clean for the next test. */
|
|
Packit |
d3489f |
close(fd);
|
|
Packit |
d3489f |
fd = new_io_uring(1, &p);
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
/* IORING_REGISTER_BUFFERS */
|
|
Packit |
d3489f |
status |= test_iovec_size(fd);
|
|
Packit |
d3489f |
status |= test_iovec_nr(fd);
|
|
Packit |
d3489f |
/* IORING_REGISTER_FILES */
|
|
Packit |
d3489f |
status |= test_max_fds(fd);
|
|
Packit |
d3489f |
close(fd);
|
|
Packit |
d3489f |
/* uring poll on the uring fd */
|
|
Packit |
d3489f |
status |= test_poll_ringfd();
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (!status)
|
|
Packit |
d3489f |
printf("PASS\n");
|
|
Packit |
d3489f |
else
|
|
Packit |
d3489f |
printf("FAIL\n");
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
return status;
|
|
Packit |
d3489f |
}
|