io_uring_setup(2)
Create an io_uring instance: shared submission/completion rings for async I/O.
- Since
- Linux 5.1check your kernel →
- glibc
- none (use syscall(2))
- System
- Linux
- Man page
- io_uring_setup(2)
Synopsis
#include <liburing.h>
int io_uring_setup(u32 entries, struct io_uring_params *params);Rod's take
Every older async I/O interface on Linux is broken somehow: aio(7) really wants O_DIRECT, and POSIX AIO is a thread pool in a trench coat. io_uring is two rings in shared memory, and with SQPOLL you submit without a syscall at all. The design is right. The implementation has also coughed up a pile of kernel escapes, and a seccomp filter only ever sees io_uring_enter, never what's in the ring. Use it on trusted servers you control. For everything else, turn it off with io_uring_disabled or restrict the ring before the fd leaves your hands.
Flags and arguments that matter
| Name | What it does |
|---|---|
| IORING_SETUP_IOPOLL | Busy-poll for completions instead of IRQs; storage only, typically O_DIRECT on a polling-capable device. |
| IORING_SETUP_SQPOLL | A kernel thread polls the SQ ring so submission needs no syscall; it parks after sq_thread_idle ms. |
| IORING_SETUP_SQ_AFF | Pin the SQPOLL thread to sq_thread_cpu. Only meaningful with IORING_SETUP_SQPOLL. |
| IORING_SETUP_CQSIZE | Size the completion queue from params.cq_entries instead of the default. |
| IORING_SETUP_R_DISABLEDLinux 5.10 | Start the ring disabled so restrictions can be registered before submissions are allowed. |
| IORING_SETUP_SUBMIT_ALLLinux 5.18 | Keep submitting the batch even if one request errors during submission. |
| IORING_SETUP_COOP_TASKRUNLinux 5.19 | Don't interrupt the task with IPIs on completion; process at the next kernel transition. |
| IORING_SETUP_SINGLE_ISSUERLinux 6.0 | Only one task may submit; violations fail with -EEXIST. |
| IORING_SETUP_DEFER_TASKRUNLinux 6.1 | Defer completion work until io_uring_enter(2) with GETEVENTS. Requires SINGLE_ISSUER. |
| IORING_SETUP_NO_MMAPLinux 6.5 | Use caller-allocated memory for the rings instead of mmap(2)-ing kernel memory. |
Errors you will actually hit
| errno | Meaning |
|---|---|
| EPERM | io_uring_disabled is 2, or 1 and caller lacks CAP_SYS_ADMIN and isn't in io_uring_group. |
| EPERM | IORING_SETUP_SQPOLL requested without sufficient privileges. |
| EINVAL | Non-zero resv, unsupported flag, entries out of bounds, or inconsistent flag combination. |
| ENOMEM | Insufficient kernel resources. |
| ENXIO | IORING_SETUP_ATTACH_WQ given but wq_fd isn't a live io_uring instance. |
Example
struct io_uring ring;
int ret = io_uring_queue_init(32, &ring, 0); /* io_uring_setup(2) */
if (ret < 0)
errx(1, "io_uring_queue_init: %s", strerror(-ret));
struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);
io_uring_prep_read(sqe, fd, buf, sizeof(buf), 0);
io_uring_submit(&ring);
struct io_uring_cqe *cqe;
if (io_uring_wait_cqe(&ring, &cqe) == 0) {
printf("res = %d\n", cqe->res); /* bytes or -errno */
io_uring_cqe_seen(&ring, cqe);
}
io_uring_queue_exit(&ring);Trimmed from the full walkthrough in io_uring: why Jens Axboe's ring buffer is annoying to disagree with.
BSD equivalent
FreeBSD: aio(4) (kernel option since FreeBSD 3.0, in all kernels since FreeBSD 11.0), with completions deliverable through kqueue(2) EVFILT_AIO. OpenBSD: none.
The long version, from the console

io_uring: why Jens Axboe's ring buffer is annoying to disagree with
io_uring landed in Linux 5.1 with a deceptively simple idea, two lock-free ring buffers shared between kernel and userspace. No syscall per operation. Zero copy. You can hate how many CVEs it's generated and still use it correctly. Here's the model.

io_uring's attack surface and IORING_REGISTER_RESTRICTIONS
io_uring has been a CVE factory since it shipped in Linux 5.1. IORING_REGISTER_RESTRICTIONS locks down what a ring instance can do, but it only works if you apply it before handing the fd to untrusted code. Here is the actual API.
Sources
- io_uring_setup(2), man7.org
- io_uring(7), man7.org
- Linux 5.1, kernelnewbies.org
- aio(4), FreeBSD
- kqueue(2), FreeBSD
Version numbers are read off the cited man pages, not memory. Spot one that's wrong? Tell Rod.