Last active
April 27, 2020 14:34
-
-
Save 3v1n0/5a2f789407e741e742d30b63a1701b59 to your computer and use it in GitHub Desktop.
Dynamically check for io_uring operations availability
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <errno.h> | |
#include <stdio.h> | |
#include <unistd.h> | |
#include <stdlib.h> | |
#include <fcntl.h> | |
#include <liburing.h> | |
typedef enum { | |
TEST_URING_OP_NONE = 0, | |
TEST_URING_OP_NOP = (1 << 0), | |
TEST_URING_OP_READV = (1 << 1), | |
TEST_URING_OP_WRITEV = (1 << 2), | |
TEST_URING_OP_FSYNC = (1 << 3), | |
TEST_URING_OP_POLL_ADD = (1 << 4), | |
TEST_URING_OP_POLL_REMOVE = (1 << 5), | |
TEST_URING_OP_SENDMSG = (1 << 6), | |
TEST_URING_OP_RECVMSG = (1 << 7), | |
TEST_URING_OP_ACCEPT = (1 << 8), | |
TEST_URING_OP_CONNECT = (1 << 9), | |
TEST_URING_OP_OPENAT = (1 << 10), | |
TEST_URING_OP_CLOSE = (1 << 11), | |
TEST_URING_OP_STATX = (1 << 12), | |
TEST_URING_OP_READ = (1 << 13), | |
TEST_URING_OP_WRITE = (1 << 14), | |
TEST_URING_OP_SEND = (1 << 15), | |
TEST_URING_OP_RECV = (1 << 16), | |
TEST_URING_OP_SPLICE = (1 << 17), | |
TEST_URING_OP_CANCEL = (1 << 18), | |
/* Keep this at the end, updating the value */ | |
TEST_URING_OP_LAST = (1 << 19), | |
} TestUringOp; | |
static TestUringOp ring_ops = TEST_URING_OP_NONE; | |
int main (void) | |
{ | |
TestUringOp ring_ops = TEST_URING_OP_NONE; | |
struct io_uring test_ring; | |
struct io_uring_params ring_params = {0}; | |
struct io_uring_sqe *sqe; | |
unsigned n_ops = 0; | |
unsigned n_cqes = 0; | |
unsigned i; | |
int opened_fd; | |
int toclose_fd; | |
int res; | |
do | |
n_ops++; | |
while ((TEST_URING_OP_LAST >> n_ops) != 1); | |
printf ("We have %d features\n", n_ops); | |
io_uring_queue_init_params (n_ops + 1, &test_ring, &ring_params); | |
opened_fd = open ("/dev/null", O_RDWR); | |
toclose_fd = open ("/proc/self/cmdline", O_RDONLY); | |
for (i = 0; i < n_ops; ++i) | |
{ | |
unsigned feature = (1 << i); | |
sqe = io_uring_get_sqe (&test_ring); | |
if (!sqe) | |
continue; | |
printf ("CHecking feature %u\n",feature); | |
switch (feature) | |
{ | |
case TEST_URING_OP_NOP: | |
io_uring_prep_nop (sqe); | |
sqe->user_data = TEST_URING_OP_NOP; | |
break; | |
case TEST_URING_OP_READV: | |
io_uring_prep_readv (sqe, opened_fd, NULL, 0, 0); | |
sqe->user_data = TEST_URING_OP_READV; | |
break; | |
case TEST_URING_OP_WRITEV: | |
io_uring_prep_writev (sqe, opened_fd, NULL, 0, 0); | |
sqe->user_data = TEST_URING_OP_WRITEV; | |
break; | |
case TEST_URING_OP_FSYNC: | |
io_uring_prep_fsync (sqe, opened_fd, 0); | |
sqe->user_data = TEST_URING_OP_FSYNC; | |
break; | |
case TEST_URING_OP_POLL_ADD: | |
/* Adding a poll would block if we don't also read, but we can | |
safely assume that in if we can remove it, then we can add it */ | |
io_uring_prep_poll_remove (sqe, NULL); | |
sqe->user_data = TEST_URING_OP_POLL_ADD; | |
break; | |
case TEST_URING_OP_POLL_REMOVE: | |
io_uring_prep_poll_remove (sqe, NULL); | |
sqe->user_data = TEST_URING_OP_POLL_REMOVE; | |
break; | |
case TEST_URING_OP_SENDMSG: | |
io_uring_prep_sendmsg (sqe, opened_fd, NULL, 0); | |
sqe->user_data = TEST_URING_OP_SENDMSG; | |
break; | |
case TEST_URING_OP_RECVMSG: | |
io_uring_prep_recvmsg (sqe, opened_fd, NULL, 0); | |
sqe->user_data = TEST_URING_OP_RECVMSG; | |
break; | |
case TEST_URING_OP_ACCEPT: | |
io_uring_prep_accept (sqe, opened_fd, NULL, NULL, 0); | |
sqe->user_data = TEST_URING_OP_ACCEPT; | |
break; | |
case TEST_URING_OP_CONNECT: | |
io_uring_prep_connect (sqe, opened_fd, NULL, 0); | |
sqe->user_data = TEST_URING_OP_CONNECT; | |
break; | |
case TEST_URING_OP_OPENAT: | |
io_uring_prep_openat (sqe, AT_FDCWD, "/proc/self/not-a-real-file", | |
O_RDONLY, 0600); | |
sqe->user_data = TEST_URING_OP_OPENAT; | |
break; | |
case TEST_URING_OP_CLOSE: | |
io_uring_prep_close (sqe, toclose_fd); | |
sqe->user_data = TEST_URING_OP_CLOSE; | |
break; | |
case TEST_URING_OP_STATX: | |
io_uring_prep_statx (sqe, -1, "/proc/self/not-a-real-file", | |
0, 0, NULL); | |
sqe->user_data = TEST_URING_OP_STATX; | |
break; | |
case TEST_URING_OP_READ: | |
io_uring_prep_read (sqe, opened_fd, NULL, 0, 0); | |
sqe->user_data = TEST_URING_OP_READ; | |
break; | |
case TEST_URING_OP_WRITE: | |
io_uring_prep_write (sqe, opened_fd, NULL, 0, 0); | |
sqe->user_data = TEST_URING_OP_WRITE; | |
break; | |
case TEST_URING_OP_SEND: | |
io_uring_prep_send (sqe, opened_fd, NULL, 0, 0); | |
sqe->user_data = TEST_URING_OP_SEND; | |
break; | |
case TEST_URING_OP_RECV: | |
io_uring_prep_recv (sqe, opened_fd, NULL, 0, 0); | |
sqe->user_data = TEST_URING_OP_RECV; | |
break; | |
case TEST_URING_OP_SPLICE: | |
io_uring_prep_splice (sqe, opened_fd, 0, opened_fd, 0, 0, 0); | |
sqe->user_data = TEST_URING_OP_SPLICE; | |
break; | |
case TEST_URING_OP_CANCEL: | |
io_uring_prep_cancel (sqe, NULL, 0); | |
sqe->user_data = TEST_URING_OP_CANCEL; | |
break; | |
} | |
} | |
res = io_uring_submit_and_wait (&test_ring, n_ops); | |
if (res > 0) | |
{ | |
struct io_uring_cqe *cqe; | |
unsigned head; | |
#if 0 | |
/* Splice is different, if we submit with the others would break the submission */ | |
sqe = io_uring_get_sqe (&test_ring); | |
if (sqe) | |
{ | |
io_uring_prep_splice (sqe, opened_fd, 0, opened_fd, 0, 0, 0); | |
sqe->user_data = TEST_URING_OP_SPLICE; | |
io_uring_submit_and_wait (&test_ring, 1); | |
} | |
#endif | |
io_uring_for_each_cqe (&test_ring, head, cqe) | |
{ | |
const char *feature_str = NULL; | |
if (cqe->user_data == TEST_URING_OP_NONE) | |
feature_str = "TEST_URING_OP_NONE"; | |
if (cqe->user_data == TEST_URING_OP_NOP) | |
feature_str = "TEST_URING_OP_NOP"; | |
if (cqe->user_data == TEST_URING_OP_READV) | |
feature_str = "TEST_URING_OP_READV"; | |
if (cqe->user_data == TEST_URING_OP_WRITEV) | |
feature_str = "TEST_URING_OP_WRITEV"; | |
if (cqe->user_data == TEST_URING_OP_FSYNC) | |
feature_str = "TEST_URING_OP_FSYNC"; | |
if (cqe->user_data == TEST_URING_OP_POLL_ADD) | |
feature_str = "TEST_URING_OP_POLL_ADD"; | |
if (cqe->user_data == TEST_URING_OP_POLL_REMOVE) | |
feature_str = "TEST_URING_OP_POLL_REMOVE"; | |
if (cqe->user_data == TEST_URING_OP_SENDMSG) | |
feature_str = "TEST_URING_OP_SENDMSG"; | |
if (cqe->user_data == TEST_URING_OP_RECVMSG) | |
feature_str = "TEST_URING_OP_RECVMSG"; | |
if (cqe->user_data == TEST_URING_OP_ACCEPT) | |
feature_str = "TEST_URING_OP_ACCEPT"; | |
if (cqe->user_data == TEST_URING_OP_CONNECT) | |
feature_str = "TEST_URING_OP_CONNECT"; | |
if (cqe->user_data == TEST_URING_OP_OPENAT) | |
feature_str = "TEST_URING_OP_OPENAT"; | |
if (cqe->user_data == TEST_URING_OP_CLOSE) | |
feature_str = "TEST_URING_OP_CLOSE"; | |
if (cqe->user_data == TEST_URING_OP_STATX) | |
feature_str = "TEST_URING_OP_STATX"; | |
if (cqe->user_data == TEST_URING_OP_READ) | |
feature_str = "TEST_URING_OP_READ"; | |
if (cqe->user_data == TEST_URING_OP_WRITE) | |
feature_str = "TEST_URING_OP_WRITE"; | |
if (cqe->user_data == TEST_URING_OP_SEND) | |
feature_str = "TEST_URING_OP_SEND"; | |
if (cqe->user_data == TEST_URING_OP_RECV) | |
feature_str = "TEST_URING_OP_RECV"; | |
if (cqe->user_data == TEST_URING_OP_SPLICE) | |
feature_str = "TEST_URING_OP_SPLICE"; | |
if (cqe->user_data == TEST_URING_OP_CANCEL) | |
feature_str = "TEST_URING_OP_CANCEL"; | |
if (cqe->res != -EINVAL && cqe->res != -EBADF) | |
ring_ops |= cqe->user_data; | |
printf ("Got %d cqe %lld (%s) res: %d, have it: %d\n", n_cqes + 1, | |
cqe->user_data, feature_str, cqe->res, (ring_ops & cqe->user_data) != 0); | |
n_cqes++; | |
} | |
} | |
io_uring_cq_advance (&test_ring, n_cqes); | |
sqe = io_uring_get_sqe (&test_ring); | |
close (opened_fd); | |
close (toclose_fd); | |
io_uring_queue_exit (&test_ring); | |
printf ("Ring ops: %u\n", ring_ops); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment