Skip to content

Instantly share code, notes, and snippets.

@3v1n0
Last active April 27, 2020 14:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 3v1n0/5a2f789407e741e742d30b63a1701b59 to your computer and use it in GitHub Desktop.
Save 3v1n0/5a2f789407e741e742d30b63a1701b59 to your computer and use it in GitHub Desktop.
Dynamically check for io_uring operations availability
#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