package: liburing-devel

title: io_uring(7)

summary: Asynchronous I/O facility

text:
# io_uring(7)

## NAME
io_uring - Asynchronous I/O facility

## SYNOPSIS

```
#include <linux/io_uring.h>
```

## DESCRIPTION
io_uring is a Linux-specific API for asynchronous I/O. It allows the user to submit one or more I/O requests, which are processed asynchronously without blocking the calling process. io_uring gets its name from ring buffers which are shared between user space and kernel space. This arrangement allows for efficient I/O, while avoiding the overhead of copying buffers between them, where possible. This interface makes io_uring different from other UNIX I/O APIs, wherein, rather than just communicate between kernel and user space with system calls, ring buffers are used as the main mode of communication. This arrangement has various performance benefits which are discussed in a separate section below. This man page uses the terms shared buffers, shared ring buffers and queues interchangeably.

The general programming model you need to follow for io_uring is outlined below

- Set up shared buffers with io_uring_setup(2) and mmap(2), mapping into user space shared buffers for the submission queue (SQ) and the completion queue (CQ). You place I/O requests you want to make on the SQ, while the kernel places the results of those operations on the CQ.

- For every I/O request you need to make (like to read a file, write a file, accept a socket connection, etc), you create a submission queue entry, or SQE, describe the I/O operation you need to get done and add it to the tail of the submission queue (SQ). Each I/O operation is, in essence, the equivalent of a system call you would have made otherwise, if you were not using io_uring. For instance, a SQE with the opcode set to IORING_OP_READ will request a read operation to be issued that is similar to the read(2) system call. Refer to the opcode documentation in io_uring_enter(2) for all supported opcodes and their properties. You can add more than one SQE to the queue depending on the number of operations you want to request.

- After you add one or more SQEs, you need to call io_uring_enter(2) to tell the kernel to dequeue your I/O requests off the SQ and begin processing them.

- For each SQE you submit, once it is done processing the request, the kernel places a completion queue event or CQE at the tail of the completion queue or CQ. The kernel places exactly one matching CQE in the CQ for every SQE you submit on the SQ. After you retrieve a CQE, minimally, you might be interested in checking the res field of the CQE structure, which corresponds to the return value of the system call's equivalent, had you used it directly without using io_uring. Given that io_uring is an async interface, errno is never used for passing back error information. Instead, res will contain what the equivalent system call would have returned in case of success, and in case of error res will contain -errno. For example, if the normal read system call would have returned -1 and set errno to EINVAL, then res would contain -EINVAL. If the normal system call would have returned a read size of 1024, then res would contain 1024.

- Optionally, io_uring_enter(2) can also wait for a specified number of requests to be processed by the kernel before it returns. If you specified a certain number of completions to wait for, the kernel would have placed at least those many number of CQEs on the CQ, which you can then readily read, right after the return from io_uring_enter(2).

- It is important to remember that I/O requests submitted to the kernel can complete in any order. It is not necessary for the kernel to process one request after another, in the order you placed them. Given that the interface is a ring, the requests are attempted in order, however that doesn't imply any sort of ordering on their execution or completion. When more than one request is in flight, it is not possible to determine which one will execute or complete first. When you dequeue CQEs off the CQ, you should always check which submitted request it corresponds to. The most common method for doing so is utilizing the user_data field in the request, which is passed back on the completion side.

- Concretely, for operations where strict ordering is required, such as for sends and receives on a stream-oriented TCP socket, it is generally unsafe to have more than one outstanding send, or more than one outstanding receive (the two directions are independent) on a given socket at a time, as the kernel may reorder their execution if poll arming or other background kernel activities are involved. However, io_uring provides various facilities to enable applications to efficiently pipeline their operations safely. If the requests are submitted in a single batch, the application may use IOSQE_IO_LINK to enforce an execution order in the kernel. Otherwise, io_uring provides advanced features like multi shot and send/receive bundles to allow applications to provide more data in fewer, more efficient trips to the kernel. Even if these features are used, applications must still ensure they do not overlap different sends or different receives on a given file.

Adding to and reading from the queues:

- You add SQEs to the tail of the SQ. The kernel reads SQEs off the head of the queue.

- The kernel adds CQEs to the tail of the CQ. You read CQEs off the head of the queue.

It should be noted that depending on the configuration io_uring's behavior can deviate from the behavior outlined above, like not posting a CQE for every SQE when setting IOSQE_CQE_SKIP_SUCCESS in the SQE or posting multiple CQEs for a single SQE for multi shot operations or requiring an io_uring_enter(2) syscall to make the kernel begin processing newly added SQEs when using submission queue polling.

### Submission queue polling
One of the goals of io_uring is to provide a means for efficient I/O. To this end, io_uring supports a polling mode that lets you avoid the call to io_uring_enter(2), which you use to inform the kernel that you have queued SQEs on to the SQ. With SQ Polling, io_uring starts a kernel thread that polls the submission queue for any I/O requests you submit by adding SQEs. With SQ Polling enabled, there is no need for you to call io_uring_enter(2), letting you avoid the overhead of system calls. A designated kernel thread dequeues SQEs off the SQ as you add them and dispatches them for asynchronous processing.

### Setting up io_uring
The main steps in setting up io_uring consist of mapping in the shared buffers with mmap(2) calls. In the example program included in this man page, the function app_setup_uring() sets up io_uring with a QUEUE_DEPTH deep submission queue. Pay attention to the 2 mmap(2) calls that set up the shared submission and completion queues. If your kernel is older than version 5.4, three mmap(2) calls are required.

### Submitting I/O requests
The process of submitting a request consists of describing the I/O operation you need to get done using an io_uring_sqe structure instance. These details describe the equivalent system call and its parameters. Because the range of I/O operations Linux supports are very varied and the io_uring_sqe structure needs to be able to describe them, it has several fields, some packed into unions for space efficiency. Here is a simplified version of struct io_uring_sqe with some of the most often used fields:

```
struct io_uring_sqe {

        __u8    opcode;         /* type of operation for this sqe */

        __s32   fd;             /* file descriptor to do IO on */

        __u64   off;            /* offset into file */

        __u64   addr;           /* pointer to buffer or iovecs */

        __u32   len;            /* buffer size or number of iovecs */

        __u64   user_data;      /* data to be passed back at completion time */

        __u8    flags;          /* IOSQE_ flags */

        ...
};
```

Here is struct io_uring_sqe in full:

```
struct io_uring_sqe {
	__u8	opcode;		/* type of operation for this sqe */
	__u8	flags;		/* IOSQE_ flags */
	__u16	ioprio;		/* ioprio for the request */
	__s32	fd;		/* file descriptor to do IO on */
	union {
		__u64	off;	/* offset into file */
		__u64	addr2;
		struct {
			__u32	cmd_op;
			__u32	__pad1;
		};
	};
	union {
		__u64	addr;	/* pointer to buffer or iovecs */
		__u64	splice_off_in;
		struct {
			__u32	level;
			__u32	optname;
		};
	};
	__u32	len;		/* buffer size or number of iovecs */
	union {
		__kernel_rwf_t	rw_flags;
		__u32		fsync_flags;
		__u16		poll_events;	/* compatibility */
		__u32		poll32_events;	/* word-reversed for BE */
		__u32		sync_range_flags;
		__u32		msg_flags;
		__u32		timeout_flags;
		__u32		accept_flags;
		__u32		cancel_flags;
		__u32		open_flags;
		__u32		statx_flags;
		__u32		fadvise_advice;
		__u32		splice_flags;
		__u32		rename_flags;
		__u32		unlink_flags;
		__u32		hardlink_flags;
		__u32		xattr_flags;
		__u32		msg_ring_flags;
		__u32		uring_cmd_flags;
		__u32		waitid_flags;
		__u32		futex_flags;
		__u32		install_fd_flags;
		__u32		nop_flags;
	};
	__u64	user_data;	/* data to be passed back at completion time */
	/* pack this to avoid bogus arm OABI complaints */
	union {
		/* index into fixed buffers, if used */
		__u16	buf_index;
		/* for grouped buffer selection */
		__u16	buf_group;
	} __attribute__((packed));
	/* personality to use, if used */
	__u16	personality;
	union {
		__s32	splice_fd_in;
		__u32	file_index;
		__u32	optlen;
		struct {
			__u16	addr_len;
			__u16	__pad3[1];
		};
	};
	union {
		struct {
			__u64	addr3;
			__u64	__pad2[1];
		};
		__u64	optval;
		/*
		 * If the ring is initialized with IORING_SETUP_SQE128, then
		 * this field is used for 80 bytes of arbitrary command data
		 */
		__u8	cmd[0];
	};
};
```

To submit an I/O request to io_uring, you need to acquire a submission queue entry (SQE) from the submission queue (SQ), fill it up with details of the operation you want to submit and call io_uring_enter(2). There are helper functions of the form io_uring_prep_X to enable proper setup of the SQE. If you want to avoid calling io_uring_enter(2), you have the option of setting up Submission Queue Polling.

SQEs are added to the tail of the submission queue. The kernel picks up SQEs off the head of the SQ. The general algorithm to get the next available SQE and update the tail is as follows.

```
struct io_uring_sqe *sqe;
unsigned tail, index;
tail = *sqring->tail;
index = tail & (*sqring->ring_mask);
sqe = &sqring->sqes[index];
/* fill up details about this I/O request */
describe_io(sqe);
/* fill the sqe index into the SQ ring array */
sqring->array[index] = index;
tail++;
atomic_store_explicit(sqring->tail, tail, memory_order_release);
```

To get the index of an entry, the application must mask the current tail index with the size mask of the ring. This holds true for both SQs and CQs. Once the SQE is acquired, the necessary fields are filled in, describing the request. While the CQ ring directly indexes the shared array of CQEs, the submission side has an indirection array between them. The submission side ring buffer is an index into this array, which in turn contains the index into the SQEs.

The following code snippet demonstrates how a read operation, an equivalent of a preadv2(2) system call is described by filling up an SQE with the necessary parameters.

```
struct iovec iovecs[16];

 ...
sqe->opcode = IORING_OP_READV;
sqe->fd = fd;
sqe->addr = (unsigned long) iovecs;
sqe->len = 16;
sqe->off = offset;
sqe->flags = 0;
```

Memory ordering

Modern compilers and CPUs freely reorder reads and writes without affecting the program's outcome to optimize performance. Some aspects of this need to be kept in mind on SMP systems since io_uring involves buffers shared between kernel and user space. These buffers are both visible and modifiable from kernel and user space. As heads and tails belonging to these shared buffers are updated by kernel and user space, changes need to be coherently visible on either side, irrespective of whether a CPU switch took place after the kernel-user mode switch happened. We use memory barriers to enforce this coherency. Being significantly large subjects on their own, memory barriers are out of scope for further discussion on this man page. For more information on modern memory models the reader may refer to the Documentation/memory-barriers.txt in the kernel tree or to the documentation of the formal C11 or kernel memory model.

Letting the kernel know about I/O submissions

Once you place one or more SQEs on to the SQ, you need to let the kernel know that you've done so. You can do this by calling the io_uring_enter(2) system call. This system call is also capable of waiting for a specified count of events to complete. This way, you can be sure to find completion events in the completion queue without having to poll it for events later.

### SQE pointer lifetimes & data stability
Due to the fixed size of the submission queue entry (SQE) some data you provide in order to perform a desired operation will be passed in the form of a pointer rather than value. In this situation, you may free the memory backing the pointer once the succeeding io_uring_enter(2) call has completed; providing it is only required by the operation when submitting.

When IORING_SETUP_SQPOLL is not enabled, this is done when you call io_uring_submit(3) In The event IORING_SETUP_SQPOLL is enabled, you must ensure any provided pointers remain valid until completion.

However, very early kernels (5.4 and earlier) required state to be

stable until the completion occurred regardless. Applications can test for this

behavior by inspecting the IORING_FEAT_SUBMIT_STABLE flag passed back from io_uring_queue_init_params(3).

As an example, the IORING_OP_TIMEOUT operation takes a pointer to a __kernel_timespec struct. This struct is then read by the kernel when you submit the submission queue entries, once submitted, you may free the backing memory of the __kernel_timespec as it will not be read again by the kernel.

It should be noted that this behaviour does not apply to data that is read or written while the operation is inflight. For example, the pointers to a buffer used as part of a IORING_OP_WRITE or IORING_OP_READ operation must remain valid until completion.

### Reading completion events
Similar to the submission queue (SQ), the completion queue (CQ) is a shared buffer between the kernel and user space. Whereas you placed submission queue entries on the tail of the SQ and the kernel read off the head, when it comes to the CQ, the kernel places completion queue events or CQEs on the tail of the CQ and you read off its head.

Submission is flexible (and thus a bit more complicated) since it needs to be able to encode different types of system calls that take various parameters. Completion, on the other hand is simpler since we're looking only for a return value back from the kernel. This is easily understood by looking at the completion queue event structure, struct io_uring_cqe:

```
struct io_uring_cqe {
	__u64	user_data;  /* sqe->data submission passed back */
	__s32	res;        /* result code for this event */
	__u32	flags;
};
```

Here, user_data is custom data that is passed unchanged from submission to completion. That is, from SQEs to CQEs. This field can be used to set context, uniquely identifying submissions that got completed. Given that I/O requests can complete in any order, this field can be used to correlate a submission with a completion. res is the result from the system call that was performed as part of the submission; its return value.

The flags field carries request-specific information. As of the 6.12 kernel, the following flags are defined:

IORING_CQE_F_BUFFER

If set, the upper 16 bits of the flags field carries the buffer ID that was chosen for this request. The request must have been issued with IOSQE_BUFFER_SELECT set, and used with a request type that supports buffer selection. Additionally, buffers must have been provided upfront either via the IORING_OP_PROVIDE_BUFFERS or the IORING_REGISTER_PBUF_RING methods.

IORING_CQE_F_MORE

If set, the application should expect more completions from the request. This is used for requests that can generate multiple completions, such as multi-shot requests, receive, or accept.

IORING_CQE_F_SOCK_NONEMPTY

If set, upon receiving the data from the socket in the current request, the socket still had data left on completion of this request.

IORING_CQE_F_NOTIF

Set for notification CQEs, as seen with the zero-copy networking send and receive support.

IORING_CQE_F_BUF_MORE

If set, the buffer ID set in the completion will get more completions. This means that the provided buffer has been partially consumed and there's more buffer space left, and hence the application should expect more completions with this buffer ID. Each completion will continue where the previous one left off. This can only happen if the provided buffer ring has been setup with IOU_PBUF_RING_INC to allow for incremental / partial consumption of buffers.

IORING_CQE_F_SKIP

If the ring has been configured with IORING_SETUP_CQE_MIXED , then CQEs may be posted which has this flag set. This can happen if the ring is a single 16b CQE entry away from wrapping, but someone needs to post a 32b CQE. As CQEs must be contiguous in memory, a filler/pad CQE needs to get posted to allow posting of the 32b CQE. CQEs with this flag set should simply be skipped and ignored, they serve no other purpose than to fill a gap in the CQ ring.

IORING_CQE_F_32

If the ring has been configured with IORING_SETUP_CQE_MIXED , this flag is set when the CQE is of the 32b type. This tells the application that there's an extra 16b of space in this CQE, and that to get to the next CQE the CQ ring must be advanced by twice as much as for a normal 16b CQE.

The general sequence to read completion events off the completion queue is as follows:

```
unsigned head;
head = *cqring->head;
if (head != atomic_load_acquire(cqring->tail)) {

    struct io_uring_cqe *cqe;

    unsigned index;

    index = head & (cqring->mask);

    cqe = &cqring->cqes[index];

    /* process completed CQE */

    process_cqe(cqe);

    /* CQE consumption complete */

    head++;
}
atomic_store_explicit(cqring->head, head, memory_order_release);
```

It helps to be reminded that the kernel adds CQEs to the tail of the CQ, while you need to dequeue them off the head. To get the index of an entry at the head, the application must mask the current head index with the size mask of the ring. Once the CQE has been consumed or processed, the head needs to be updated to reflect the consumption of the CQE. Attention should be paid to the read and write barriers to ensure successful read and update of the head.

### io_uring performance
Because of the shared ring buffers between kernel and user space, io_uring can be a zero-copy system. Copying buffers to and from becomes necessary when system calls that transfer data between kernel and user space are involved. But since the bulk of the communication in io_uring is via buffers shared between the kernel and user space, this huge performance overhead is completely avoided.

While system calls may not seem like a significant overhead, in high performance applications, making a lot of them will begin to matter. While workarounds the operating system has in place to deal with Spectre and Meltdown are ideally best done away with, unfortunately, some of these workarounds are around the system call interface, making system calls not as cheap as before on affected hardware. While newer hardware should not need these workarounds, hardware with these vulnerabilities can be expected to be in the wild for a long time. While using synchronous programming interfaces or even when using asynchronous programming interfaces under Linux, there is at least one system call involved in the submission of each request. In io_uring, on the other hand, you can batch several requests in one go, simply by queueing up multiple SQEs, each describing an I/O operation you want and make a single call to io_uring_enter(2). This is possible due to io_uring's shared buffers based design.

While this batching in itself can avoid the overhead associated with potentially multiple and frequent system calls, you can reduce even this overhead further with Submission Queue Polling, by having the kernel poll and pick up your SQEs for processing as you add them to the submission queue. This avoids the io_uring_enter(2) call you need to make to tell the kernel to pick SQEs up. For high-performance applications, this means even fewer system call overheads.

## CONFORMING TO
io_uring is Linux-specific.

## EXAMPLES
The following example uses io_uring to copy stdin to stdout. Using shell redirection, you should be able to copy files with this example. Because it uses a queue depth of only one, this example processes I/O requests one after the other. It is purposefully kept this way to aid understanding. In real-world scenarios however, you'll want to have a larger queue depth to parallelize I/O request processing so as to gain the kind of performance benefits io_uring provides with its asynchronous processing of requests.

```
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/syscall.h>
#include <sys/mman.h>
#include <sys/uio.h>
#include <linux/fs.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdatomic.h>
#include <errno.h>
#include <linux/io_uring.h>
#define QUEUE_DEPTH 1
#define BLOCK_SZ    1024
/* Macros for barriers needed by io_uring */
#define io_uring_smp_store_release(p, v)            \

    atomic_store_explicit((_Atomic typeof(*(p)) *)(p), (v), \

                  memory_order_release)
#define io_uring_smp_load_acquire(p)                \

    atomic_load_explicit((_Atomic typeof(*(p)) *)(p),   \

                 memory_order_acquire)
int ring_fd;
unsigned *sring_tail, *sring_mask, *sring_array,

            *cring_head, *cring_tail, *cring_mask;
struct io_uring_sqe *sqes;
struct io_uring_cqe *cqes;
char buff[BLOCK_SZ];
off_t offset;
/*

 * System call wrappers provided since glibc does not yet

 * provide wrappers for io_uring system calls.
* */
int io_uring_setup(unsigned entries, struct io_uring_params *p)
{

    int ret;

    ret = syscall(__NR_io_uring_setup, entries, p);

    return (ret < 0) ? -errno : ret;
}
int io_uring_enter(int ring_fd, unsigned int to_submit,

                   unsigned int min_complete, unsigned int flags)
{

    int ret;

    ret = syscall(__NR_io_uring_enter, ring_fd, to_submit,

                  min_complete, flags, NULL, 0);

    return (ret < 0) ? -errno : ret;
}
int app_setup_uring(void) {

    struct io_uring_params p;

    void *sq_ptr, *cq_ptr;

    /* See io_uring_setup(2) for io_uring_params.flags you can set */

    memset(&p, 0, sizeof(p));

    ring_fd = io_uring_setup(QUEUE_DEPTH, &p);

    if (ring_fd < 0) {

        perror("io_uring_setup");

        return 1;

    }

    /*

     * io_uring communication happens via 2 shared kernel-user space ring

     * buffers, which can be jointly mapped with a single mmap() call in

     * kernels >= 5.4.

     */

    int sring_sz = p.sq_off.array + p.sq_entries * sizeof(unsigned);

    int cring_sz = p.cq_off.cqes + p.cq_entries * sizeof(struct io_uring_cqe);

    /* Rather than check for kernel version, the recommended way is to

     * check the features field of the io_uring_params structure, which is a

     * bitmask. If IORING_FEAT_SINGLE_MMAP is set, we can do away with the

     * second mmap() call to map in the completion ring separately.

     */

    if (p.features & IORING_FEAT_SINGLE_MMAP) {

        if (cring_sz > sring_sz)

            sring_sz = cring_sz;

        cring_sz = sring_sz;

    }

    /* Map in the submission and completion queue ring buffers.

     *  Kernels < 5.4 only map in the submission queue, though.

     */

    sq_ptr = mmap(0, sring_sz, PROT_READ | PROT_WRITE,

                  MAP_SHARED | MAP_POPULATE,

                  ring_fd, IORING_OFF_SQ_RING);

    if (sq_ptr == MAP_FAILED) {

        perror("mmap");

        return 1;

    }

    if (p.features & IORING_FEAT_SINGLE_MMAP) {

        cq_ptr = sq_ptr;

    } else {

        /* Map in the completion queue ring buffer in older kernels separately */

        cq_ptr = mmap(0, cring_sz, PROT_READ | PROT_WRITE,

                      MAP_SHARED | MAP_POPULATE,

                      ring_fd, IORING_OFF_CQ_RING);

        if (cq_ptr == MAP_FAILED) {

            perror("mmap");

            return 1;

        }

    }

    /* Save useful fields for later easy reference */

    sring_tail = sq_ptr + p.sq_off.tail;

    sring_mask = sq_ptr + p.sq_off.ring_mask;

    sring_array = sq_ptr + p.sq_off.array;

    /* Map in the submission queue entries array */

    sqes = mmap(0, p.sq_entries * sizeof(struct io_uring_sqe),

                   PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE,

                   ring_fd, IORING_OFF_SQES);

    if (sqes == MAP_FAILED) {

        perror("mmap");

        return 1;

    }

    /* Save useful fields for later easy reference */

    cring_head = cq_ptr + p.cq_off.head;

    cring_tail = cq_ptr + p.cq_off.tail;

    cring_mask = cq_ptr + p.cq_off.ring_mask;

    cqes = cq_ptr + p.cq_off.cqes;

    return 0;
}
/*
* Read from completion queue.
* In this function, we read completion events from the completion queue.
* We dequeue the CQE, update and head and return the result of the operation.
* */
int read_from_cq() {

    struct io_uring_cqe *cqe;

    unsigned head;

    /* Read barrier */

    head = io_uring_smp_load_acquire(cring_head);

    /*

    * Remember, this is a ring buffer. If head == tail, it means that the

    * buffer is empty.

    * */

    if (head == *cring_tail)

        return -1;

    /* Get the entry */

    cqe = &cqes[head & (*cring_mask)];

    if (cqe->res < 0)

        fprintf(stderr, "Error: %s\n", strerror(abs(cqe->res)));

    head++;

    /* Write barrier so that update to the head are made visible */

    io_uring_smp_store_release(cring_head, head);

    return cqe->res;
}
/*
* Submit a read or a write request to the submission queue.
* */
int submit_to_sq(int fd, int op) {

    unsigned index, tail;

    /* Add our submission queue entry to the tail of the SQE ring buffer */

    tail = *sring_tail;

    index = tail & *sring_mask;

    struct io_uring_sqe *sqe = &sqes[index];

    /* Fill in the parameters required for the read or write operation */

    sqe->opcode = op;

    sqe->fd = fd;

    sqe->addr = (unsigned long) buff;

    if (op == IORING_OP_READ) {

        memset(buff, 0, sizeof(buff));

        sqe->len = BLOCK_SZ;

    }

    else {

        sqe->len = strlen(buff);

    }

    sqe->off = offset;

    sring_array[index] = index;

    tail++;

    /* Update the tail */

    io_uring_smp_store_release(sring_tail, tail);

    /*

    * Tell the kernel we have submitted events with the io_uring_enter()

    * system call. We also pass in the IORING_ENTER_GETEVENTS flag which

    * causes the io_uring_enter() call to wait until min_complete

    * (the 3rd param) events complete.

    * */

    int ret =  io_uring_enter(ring_fd, 1,1,

                              IORING_ENTER_GETEVENTS);

    if(ret < 0) {

        perror("io_uring_enter");

        return -1;

    }

    return ret;
}
int main(int argc, char *argv[]) {

    int res;

    /* Setup io_uring for use */

    if(app_setup_uring()) {

        fprintf(stderr, "Unable to setup uring!\n");

        return 1;

    }

    /*

    * A while loop that reads from stdin and writes to stdout.

    * Breaks on EOF.

    */

    while (1) {

        /* Initiate read from stdin and wait for it to complete */

        submit_to_sq(STDIN_FILENO, IORING_OP_READ);

        /* Read completion queue entry */

        res = read_from_cq();

        if (res > 0) {

            /* Read successful. Write to stdout. */

            submit_to_sq(STDOUT_FILENO, IORING_OP_WRITE);

            read_from_cq();

        } else if (res == 0) {

            /* reached EOF */

            break;

        }

        else if (res < 0) {

            /* Error reading file */

            fprintf(stderr, "Error: %s\n", strerror(abs(res)));

            break;

        }

        offset += res;

    }

    return 0;
}
```

## SEE ALSO
io_uring_enter(2) io_uring_register(2) io_uring_setup(2)


title: io_uring_cancelation(7)

summary: io_uring request cancelation overview

text:
# io_uring_cancelation(7)

## NAME
io_uring_cancelation - io_uring request cancelation overview

## DESCRIPTION
io_uring provides mechanisms to cancel in-flight requests before they complete naturally. This is useful for implementing timeouts, handling connection drops, closing connections that go away, or when a request is no longer needed.

### Why cancel requests?
Common scenarios requiring cancelation:

- Timeouts: Cancel a read or accept that has been pending too long

- Connection management: Cancel pending operations when a connection is closed

- Resource cleanup: Cancel operations on a file descriptor being closed

- Multishot termination: Stop a multishot operation that is no longer needed

### Basic cancelation
The primary cancelation mechanism is IORING_OP_ASYNC_CANCEL (set up with io_uring_prep_cancel(3) or related functions). By default, it cancels a request matching a specific user_data:

```
/* Submit a read with user_data = 1234 */
sqe = io_uring_get_sqe(ring);
io_uring_prep_read(sqe, fd, buf, len, 0);
io_uring_sqe_set_data64(sqe, 1234);
io_uring_submit(ring);
/* Later, cancel it */
sqe = io_uring_get_sqe(ring);
io_uring_prep_cancel64(sqe, 1234, 0);
io_uring_submit(ring);
```

### Cancelation results
When a cancelation is submitted, two CQEs are generated:

The canceled request's CQE:

- res is set to -ECANCELED (or occasionally -EINTR if the operation was already in progress)

- The user_data identifies which request was canceled

The cancel request's CQE:

- res is 0 on success (request was found and canceled)

- res is -ENOENT if no matching request was found

- res is -EALREADY if the request was found but already completing

The order of these CQEs is not guaranteed. The application may receive the cancel CQE before or after the canceled request's CQE.

### Cancelation flags
Various flags modify cancelation behavior:

IORING_ASYNC_CANCEL_ALL

Cancel all matching requests, not just the first one found. The cancel CQE's res indicates how many requests were canceled.

IORING_ASYNC_CANCEL_FD

Match requests by file descriptor instead of user_data. Cancels requests operating on the specified fd:

```
io_uring_prep_cancel_fd(sqe, fd, IORING_ASYNC_CANCEL_FD);
```

IORING_ASYNC_CANCEL_ANY

Cancel any single request, ignoring user_data matching. Useful for draining a ring of all pending requests when combined with IORING_ASYNC_CANCEL_ALL.

IORING_ASYNC_CANCEL_FD_FIXED

The file descriptor is a fixed file (registered file index) rather than a regular fd.

Flags can be combined:

```
/* Cancel all requests on a specific fd */
io_uring_prep_cancel_fd(sqe, fd,

    IORING_ASYNC_CANCEL_FD | IORING_ASYNC_CANCEL_ALL);
/* Cancel all pending requests in the ring */
io_uring_prep_cancel(sqe, NULL,

    IORING_ASYNC_CANCEL_ANY | IORING_ASYNC_CANCEL_ALL);
```

### Race conditions
Cancelation is inherently racy. Between submitting the cancel request and the kernel processing it:

- The target request may complete successfully

- The target request may fail for another reason

- The target request may already be in a non-cancelable state

Applications must handle these cases:

```
io_uring_wait_cqe(ring, &cqe);
if (cqe->user_data == cancel_user_data) {

    /* This is the cancel operation's result */

    if (cqe->res == -ENOENT) {

        /* Request already completed or not found */

    } else if (cqe->res == -EALREADY) {

        /* Request was found but completing */

    } else if (cqe->res >= 0) {

        /* Successfully canceled res requests */

    }
} else {

    /* This is the original request's result */

    if (cqe->res == -ECANCELED) {

        /* Request was canceled */

    } else {

        /* Request completed normally (or with error) */

    }
}
```

### Link timeouts
For timing out a single operation, link timeouts are often simpler than explicit cancelation. See io_uring_linked_requests(7) for details:

```
sqe = io_uring_get_sqe(ring);
io_uring_prep_read(sqe, fd, buf, len, 0);
sqe->flags |= IOSQE_IO_LINK;
sqe = io_uring_get_sqe(ring);
io_uring_prep_link_timeout(sqe, &timeout, 0);
```

The kernel handles the cancelation automatically if the timeout expires.

### Canceling multishot requests
Multishot requests (see io_uring_multishot(7)) continue generating completions until canceled or an error occurs. To stop a multishot request:

```
/* Cancel a multishot accept */
io_uring_prep_cancel64(sqe, accept_user_data, 0);
```

After cancelation:

- The multishot generates a final CQE with -ECANCELED

- The IORING_CQE_F_MORE flag is not set on this final CQE

- The cancel CQE indicates success

### Canceling by file descriptor
When a file descriptor is closed (either via close(2) or IORING_OP_CLOSE), pending requests operating on that fd are not automatically canceled. This differs from synchronous I/O behavior and is a common source of confusion.

In synchronous I/O, closing a file descriptor is typically the last reference to the underlying file, so the close completes any pending operations. However, io_uring holds its own reference to the file for each pending request. Closing the application's fd does not release these references — the pending read, recv, or other operation continues to hold a reference and will not automatically complete or fail.

If an application expects a pending read on an fd to post a completion when the fd is closed, that will not happen. The request must be explicitly canceled:

```
/* Cancel all operations on fd before closing */
sqe = io_uring_get_sqe(ring);
io_uring_prep_cancel_fd(sqe, fd,

    IORING_ASYNC_CANCEL_FD | IORING_ASYNC_CANCEL_ALL);
io_uring_submit(ring);
/* Wait for cancelations, then close */
```

### Shutdown cancelation
When an io_uring instance is closed (via io_uring_queue_exit(3) or closing the ring file descriptor), all pending requests are automatically canceled. Manual cancelation before shutdown is not required.

However, if the application needs to ensure all requests are completed before proceeding (e.g., to process their results or free associated resources), explicit cancelation can be used:

```
/* Cancel everything */
sqe = io_uring_get_sqe(ring);
io_uring_prep_cancel(sqe, NULL,

    IORING_ASYNC_CANCEL_ANY | IORING_ASYNC_CANCEL_ALL);
io_uring_submit(ring);
/* Wait for all CQEs */
while (pending_count > 0) {

    io_uring_wait_cqe(ring, &cqe);

    pending_count--;

    io_uring_cqe_seen(ring, cqe);
}
```

### Synchronous cancelation
For cases where the application needs to cancel requests and wait for the cancelation to complete in a single blocking call, io_uring_register_sync_cancel(3) provides a synchronous interface:

```
struct io_uring_sync_cancel_reg reg = {

    .addr = user_data,

    .timeout.tv_sec = 5,
};
ret = io_uring_register_sync_cancel(ring, &reg);
```

This blocks until the matching request is canceled or the timeout expires. It is useful when the application cannot easily integrate asynchronous cancelation into its event loop.

## NOTES

- Not all operations are cancelable. Operations that have already been submitted to hardware (e.g., disk I/O in progress) typically cannot be canceled.

- Cancelation is asynchronous. The cancel request itself may take time to complete.

- When using IORING_ASYNC_CANCEL_ALL, the cancel CQE's res field contains the count of canceled requests.

- Fixed files can be canceled using IORING_ASYNC_CANCEL_FD_FIXED with the file index instead of a regular fd.

- Poll operations and multishot requests are generally good candidates for cancelation. Completed disk I/O is not.

## SEE ALSO
io_uring(7), io_uring_linked_requests(7), io_uring_multishot(7), io_uring_prep_cancel(3), io_uring_prep_cancel64(3), io_uring_prep_cancel_fd(3), io_uring_prep_link_timeout(3), io_uring_register_sync_cancel(3)


title: io_uring_linked_requests(7)

summary: io_uring linked requests overview

text:
# io_uring_linked_requests(7)

## NAME
io_uring_linked_requests - io_uring linked requests overview

## DESCRIPTION
Linked requests allow applications to chain multiple io_uring operations together, creating dependencies between them. When requests are linked, they execute sequentially rather than concurrently, with each request starting only after the previous one in the chain completes.

### Why use linked requests?
Normal io_uring submissions are independent and may execute in any order or concurrently. However, some operations have natural dependencies:

- Read from one file, then write to another

- Connect to a server, then send a request

- Accept a connection, then receive data

- Perform an operation with a timeout

Without linking, applications would need to wait for completions and submit follow-up requests manually. Linked requests allow the entire chain to be submitted at once, reducing round trips between user space and the kernel.

Linked requests are most beneficial when:

- Operations must execute in a specific order

- Later operations depend on earlier ones succeeding

- You want to attach a timeout to an operation

- Reducing submission latency is important

### Creating linked requests
Requests are linked by setting the IOSQE_IO_LINK flag on a request. This links it to the next request in the submission. The chain continues until a request without the link flag is encountered.

```
struct io_uring_sqe *sqe;
/* First request in chain */
sqe = io_uring_get_sqe(ring);
io_uring_prep_read(sqe, fd_in, buf, len, 0);
sqe->flags |= IOSQE_IO_LINK;
/* Second request, linked to first */
sqe = io_uring_get_sqe(ring);
io_uring_prep_write(sqe, fd_out, buf, len, 0);
sqe->flags |= IOSQE_IO_LINK;
/* Third request, end of chain (no link flag) */
sqe = io_uring_get_sqe(ring);
io_uring_prep_fsync(sqe, fd_out, 0);
io_uring_submit(ring);
```

In this example, the read completes first, then the write, then the fsync. Each operation waits for the previous one to complete before starting.

### Soft links vs hard links
There are two types of links, which differ in how they handle errors:

Soft links (IOSQE_IO_LINK)

If a request in the chain fails (returns a negative error code), all subsequent requests in the chain are canceled with -ECANCELED. This is useful when later operations depend on earlier ones succeeding.

Hard links (IOSQE_IO_HARDLINK)

The chain continues executing even if a request fails. Each request runs regardless of the outcome of previous requests. This is useful when you want to attempt all operations even if some fail.

```
/* Soft link: write is canceled if read fails */
sqe = io_uring_get_sqe(ring);
io_uring_prep_read(sqe, fd, buf, len, 0);
sqe->flags |= IOSQE_IO_LINK;
sqe = io_uring_get_sqe(ring);
io_uring_prep_write(sqe, fd2, buf, len, 0);
/* Hard link: write runs even if read fails */
sqe = io_uring_get_sqe(ring);
io_uring_prep_read(sqe, fd, buf, len, 0);
sqe->flags |= IOSQE_IO_HARDLINK;
sqe = io_uring_get_sqe(ring);
io_uring_prep_write(sqe, fd2, buf, len, 0);
```

### Link timeouts
A common use of linked requests is to add a timeout to an operation. The IORING_OP_LINK_TIMEOUT operation (set up with io_uring_prep_link_timeout(3)) is designed specifically for this:

```
struct __kernel_timespec ts = { .tv_sec = 5, .tv_nsec = 0 };
/* The operation to be timed */
sqe = io_uring_get_sqe(ring);
io_uring_prep_read(sqe, fd, buf, len, 0);
sqe->flags |= IOSQE_IO_LINK;
/* The timeout, linked to the read */
sqe = io_uring_get_sqe(ring);
io_uring_prep_link_timeout(sqe, &ts, 0);
io_uring_submit(ring);
```

If the read completes before the timeout:

- The read CQE has the actual result

- The timeout CQE has -ECANCELED

If the timeout expires first:

- The read CQE has -ECANCELED (or -EINTR if it was in progress)

- The timeout CQE has -ETIME

Link timeouts only apply to the immediately preceding request in the chain. To timeout an entire chain, the timeout must be linked after the last operation.

### Completion ordering
Each request in a linked chain generates its own CQE. Completions for linked requests are ordered — the CQE for an earlier request in the chain will be posted before the CQE for a later request.

Applications can rely on this ordering when processing completions. However, if other unlinked requests are in flight, their completions may be interleaved with the chain's completions.

### Error handling
For soft-linked chains, error handling is straightforward:

- Check each CQE's result

- If a request failed, all subsequent requests will have -ECANCELED

- The first non-canceled error indicates where the chain broke

```
/* Processing a linked chain's completions */
for (int i = 0; i < chain_length; i++) {

    io_uring_wait_cqe(ring, &cqe);

    if (cqe->res == -ECANCELED) {

        /* Previous request in chain failed */

    } else if (cqe->res < 0) {

        /* This request failed, caused chain break */

        handle_error(cqe->res);

    } else {

        /* Success */

        handle_success(cqe->res);

    }

    io_uring_cqe_seen(ring, cqe);
}
```

### Common patterns
Copy with sync:

Read data, write it elsewhere, then sync:

```
io_uring_prep_read(sqe1, src_fd, buf, len, 0);
sqe1->flags |= IOSQE_IO_LINK;
io_uring_prep_write(sqe2, dst_fd, buf, len, 0);
sqe2->flags |= IOSQE_IO_LINK;
io_uring_prep_fsync(sqe3, dst_fd, 0);
```

Connect with timeout:

Attempt connection with a time limit:

```
io_uring_prep_connect(sqe1, sockfd, addr, addrlen);
sqe1->flags |= IOSQE_IO_LINK;
io_uring_prep_link_timeout(sqe2, &timeout, 0);
```

Send after connect:

Connect then immediately send data:

```
io_uring_prep_connect(sqe1, sockfd, addr, addrlen);
sqe1->flags |= IOSQE_IO_LINK;
io_uring_prep_send(sqe2, sockfd, data, len, 0);
```

## NOTES

- Linked requests must be submitted together in the same io_uring_submit(3) call. The chain is defined by the order of SQEs in the submission.

- The link flag on the last request in a chain is ignored (it has nothing to link to).

- Chains can be arbitrarily long, limited only by SQ ring size.

- Mixing IOSQE_IO_LINK and IOSQE_IO_HARDLINK in the same chain is allowed. Each link's type determines what happens if that specific request fails.

- Linked requests share the same personality if set, allowing credential inheritance through the chain.

- If a request in a chain is canceled (e.g., via io_uring_prep_cancel(3)), the chain breaks as if that request had failed.

- Linked requests have performance implications: they force sequential execution, preventing the kernel from optimizing or parallelizing operations. Use links only when ordering is required. For independent operations, submitting them without links allows the kernel to execute them concurrently or reorder them for better performance.

## SEE ALSO
io_uring(7), io_uring_prep_link_timeout(3), io_uring_prep_cancel(3), io_uring_sqe_set_flags(3)


title: io_uring_multishot(7)

summary: io_uring multishot requests overview

text:
# io_uring_multishot(7)

## NAME
io_uring_multishot - io_uring multishot requests overview

## DESCRIPTION
Multishot requests are a class of io_uring operations where a single submission queue entry (SQE) can generate multiple completion queue entries (CQEs). This is in contrast to normal "oneshot" operations where each SQE produces exactly one CQE.

### Why use multishot requests?
Traditional I/O operations require submitting a new request for each operation. For high-frequency operations like accepting connections or receiving data, this creates overhead:

- CPU cycles spent preparing and submitting SQEs

- Memory bandwidth for SQE/CQE processing

- Potential for gaps between completions and new submissions

Multishot requests eliminate this overhead by keeping the operation active after each completion. The kernel automatically re-arms the operation, generating a new CQE when the next event occurs. Additionally, the internal poll mechanism remains persistent for the request, avoiding the need to manipulate poll state for each operation.

Multishot operations are most beneficial for:

- Network servers accepting many connections

- Applications receiving data on long-lived connections

- Event monitoring with poll

- Any scenario with repeated identical operations

### How multishot works
When a multishot operation completes, the CQE has the IORING_CQE_F_MORE flag set in cqe->flags. This indicates that the operation remains active and more completions will follow. The operation continues until:

- An error occurs (the final CQE will not have IORING_CQE_F_MORE set)

- The operation is explicitly canceled

- A termination condition specific to the operation is met (e.g., buffer exhaustion for receives)

The final CQE for a multishot operation will not have IORING_CQE_F_MORE set, indicating the operation has terminated.

### Multishot accept
io_uring_prep_multishot_accept(3) and io_uring_prep_multishot_accept_direct(3) set up a multishot accept operation. Each incoming connection generates a CQE with the new file descriptor in cqe->res.

```
struct io_uring_sqe *sqe = io_uring_get_sqe(ring);
io_uring_prep_multishot_accept(sqe, listen_fd, NULL, NULL, 0);
```

The operation continues accepting connections until an error occurs or it is canceled. Using the direct variant with IORING_FILE_INDEX_ALLOC allows accepted sockets to be placed directly into the fixed file table.

### Multishot receive
io_uring_prep_recv_multishot(3) sets up a multishot receive operation. Each time data arrives on the socket, a CQE is generated. This is typically used with provided buffers (see io_uring_provided_buffers(7)):

```
struct io_uring_sqe *sqe = io_uring_get_sqe(ring);
io_uring_prep_recv_multishot(sqe, sockfd, NULL, 0, 0);
sqe->buf_group = bgid;
sqe->flags |= IOSQE_BUFFER_SELECT;
```

Each completion includes:

- IORING_CQE_F_MORE if more completions will follow

- IORING_CQE_F_BUFFER indicating a buffer was selected

- The buffer ID in the upper bits of cqe->flags

- The number of bytes received in cqe->res

The multishot receive terminates when an error occurs, the connection closes, or the buffer ring is exhausted.

### Multishot recvmsg
io_uring_prep_recvmsg_multishot(3) is similar to multishot receive but uses the msghdr structure for scatter/gather I/O and ancillary data. A provided buffer is used for each message, with the kernel writing a struct io_uring_recvmsg_out header at the start of the buffer containing the actual message parameters.

### Multishot read
io_uring_prep_read_multishot(3) sets up a multishot read operation, typically used with pipes or other stream-oriented file descriptors. Like multishot receive, this is used with provided buffers:

```
struct io_uring_sqe *sqe = io_uring_get_sqe(ring);
io_uring_prep_read_multishot(sqe, fd, 0, 0, bgid);
```

The operation generates a CQE each time data becomes available to read.

### Multishot poll
io_uring_prep_poll_multishot(3) sets up a multishot poll operation, or it can be done manually by setting the IORING_POLL_ADD_MULTI flag:

```
struct io_uring_sqe *sqe = io_uring_get_sqe(ring);
io_uring_prep_poll_multishot(sqe, fd, POLLIN);
/* or equivalently: */
io_uring_prep_poll_add(sqe, fd, POLLIN);
sqe->len |= IORING_POLL_ADD_MULTI;
```

Each time the polled condition becomes true, a CQE is generated with the triggered events in cqe->res. Unlike oneshot poll which is automatically removed after triggering, multishot poll remains active.

For level-triggered events, the application should be careful to handle the event (e.g., read all available data) before the next poll completion, or spurious wakeups may occur.

### Multishot waitid
io_uring_prep_waitid(3) can operate in multishot mode by setting IORING_ACCEPT_MULTISHOT in the flags. This allows waiting for multiple child process state changes with a single SQE.

### Handling multishot completions
Applications must check for IORING_CQE_F_MORE to determine if the operation is still active:

```
struct io_uring_cqe *cqe;
while (io_uring_peek_cqe(ring, &cqe) == 0) {

    if (cqe->res < 0) {

        /* Error occurred, operation terminated */

        handle_error(cqe->res);

    } else {

        process_completion(cqe);

    }

    if (!(cqe->flags & IORING_CQE_F_MORE)) {

        /* Operation terminated, may need to resubmit */

        rearm_if_needed();

    }

    io_uring_cqe_seen(ring, cqe);
}
```

### Canceling multishot operations
Multishot operations can be canceled using io_uring_prep_cancel(3) or related functions. The cancellation request generates its own CQE, and the multishot operation generates a final CQE (typically with -ECANCELED) without IORING_CQE_F_MORE set.

```
struct io_uring_sqe *sqe = io_uring_get_sqe(ring);
io_uring_prep_cancel64(sqe, user_data, 0);
```

### Integration with provided buffers
Multishot receive and read operations are designed to work with provided buffer rings (see io_uring_provided_buffers(7)). Each completion consumes a buffer from the ring, and the application must return buffers to the ring to keep the operation running.

If the buffer ring becomes empty, the multishot operation terminates with -ENOBUFS. Applications should ensure adequate buffers are available and promptly return used buffers to the ring.

## NOTES

- Always check IORING_CQE_F_MORE to know if a multishot operation is still active.

- Multishot operations may generate many CQEs quickly. Ensure the CQ ring is large enough to avoid overflow.

- When using provided buffers with multishot receives, monitor buffer availability to prevent premature termination.

- Multishot operations are edge-triggered conceptually — they generate completions when events occur, not continuously while conditions are true.

- Error completions from multishot operations do not have IORING_CQE_F_MORE set, indicating termination.

## SEE ALSO
io_uring(7), io_uring_provided_buffers(7), io_uring_prep_multishot_accept(3), io_uring_prep_recv_multishot(3), io_uring_prep_recvmsg_multishot(3), io_uring_prep_read_multishot(3), io_uring_prep_poll_add(3), io_uring_prep_poll_multishot(3), io_uring_prep_cancel(3)


title: io_uring_provided_buffers(7)

summary: io_uring provided buffer rings overview

text:
# io_uring_provided_buffers(7)

## NAME
io_uring_provided_buffers - io_uring provided buffer rings overview

## DESCRIPTION
Provided buffer rings allow applications to supply a pool of buffers to the kernel that can be dynamically selected at operation completion time. This is particularly useful for operations where the buffer requirements are not known upfront, such as receiving data from network sockets or reading from pipes.

### Why use provided buffers?
Traditional I/O operations require the application to specify a buffer when submitting the request. For receive operations on sockets or reads from pipes, this presents a challenge: the application doesn't know how much data will arrive, so it must either:

- Allocate a large buffer for each pending operation, wasting memory

- Use small buffers and potentially require multiple operations

- Limit the number of pending operations to control memory usage

Provided buffer rings solve this by letting the kernel select an appropriately-sized buffer from a shared pool at completion time. Multiple operations can share the same buffer pool, and buffers are only consumed when data actually arrives.

Provided buffers are most beneficial for:

- Network servers with many concurrent connections

- Applications receiving variable-length messages

- Scenarios where memory efficiency is important

### Buffer ring concepts
A provided buffer ring is a circular buffer shared between the application and kernel:

- The application adds buffers to the ring by writing entries and advancing the tail

- The kernel consumes buffers from the ring by reading entries and advancing the head

- Each buffer has a unique buffer ID (bid) within its buffer group

- Buffer groups are identified by a buffer group ID (bgid)

Multiple buffer rings can exist simultaneously, each with a different buffer group ID. Operations specify which buffer group to use.

### Setting up a buffer ring
Buffer rings are set up using io_uring_setup_buf_ring(3), which handles allocation, registration, and initialization:

```
struct io_uring_buf_ring *br;
int bgid = 1;  /* buffer group ID */
int err;
br = io_uring_setup_buf_ring(ring, 128, bgid, 0, &err);
if (!br) {

    fprintf(stderr, "buffer ring setup failed: %d\n", err);

    return err;
}
```

The ring must have a power-of-two number of entries, up to a maximum of 32768 (2^15).

Alternatively, applications can use io_uring_register_buf_ring(3) for more control over the setup process, including kernel-allocated rings using the IOU_PBUF_RING_MMAP flag.

### Adding buffers to the ring
Buffers are added using io_uring_buf_ring_add(3) and made visible to the kernel with io_uring_buf_ring_advance(3):

```
int mask = io_uring_buf_ring_mask(128);
for (int i = 0; i < 128; i++) {

    void *buf = malloc(4096);

    io_uring_buf_ring_add(br, buf, 4096, i, mask, i);
}
io_uring_buf_ring_advance(br, 128);
```

Each buffer is assigned a buffer ID (the third parameter). Buffer IDs should be unique within the buffer group but can be reused after a buffer is returned.

### Using provided buffers in operations
To use provided buffers, set the IOSQE_BUFFER_SELECT flag on the SQE and specify the buffer group ID:

```
struct io_uring_sqe *sqe = io_uring_get_sqe(ring);
io_uring_prep_recv(sqe, sockfd, NULL, 4096, 0);
io_uring_sqe_set_flags(sqe, IOSQE_BUFFER_SELECT);
io_uring_sqe_set_buf_group(sqe, bgid);
```

Note that addr is set to NULL (or ignored) since the kernel will select the buffer. The len field specifies the maximum amount of data to receive.

Operations that support provided buffers include:

- IORING_OP_READ / IORING_OP_RECV

- IORING_OP_READV (single vector only)

- IORING_OP_RECVMSG

### Handling completions
When an operation using provided buffers completes, the CQE indicates which buffer was used:

- IORING_CQE_F_BUFFER is set in cqe->flags

- The buffer ID is in the upper 16 bits of cqe->flags, extractable via cqe->flags >> IORING_CQE_BUFFER_SHIFT

- cqe->res contains the number of bytes transferred

```
struct io_uring_cqe *cqe;
io_uring_wait_cqe(ring, &cqe);
if (cqe->flags & IORING_CQE_F_BUFFER) {

    int bid = cqe->flags >> IORING_CQE_BUFFER_SHIFT;

    void *buf = buffers[bid];  /* application's buffer tracking */

    int len = cqe->res;

    /* process data in buf */

    process_data(buf, len);

    /* return buffer to ring for reuse */

    io_uring_buf_ring_add(br, buf, 4096, bid, mask, 0);

    io_uring_buf_ring_advance(br, 1);
}
io_uring_cqe_seen(ring, cqe);
```

If no buffer was available when the operation completed, the operation fails with -ENOBUFS.

### Multishot operations
Provided buffers are particularly powerful with multishot operations like io_uring_prep_recv_multishot(3). A single SQE can generate multiple completions, each consuming a buffer from the ring:

```
struct io_uring_sqe *sqe = io_uring_get_sqe(ring);
io_uring_prep_recv_multishot(sqe, sockfd, NULL, 0, 0);
io_uring_sqe_set_flags(sqe, IOSQE_BUFFER_SELECT);
io_uring_sqe_set_buf_group(sqe, bgid);
```

Completions with IORING_CQE_F_MORE set indicate more completions will follow. The multishot operation continues until an error occurs, the buffer ring is exhausted, or the operation is canceled.

### Incremental buffer consumption
Buffer rings can be set up with the IOU_PBUF_RING_INC flag to enable incremental consumption. With this mode, large buffers can be partially consumed across multiple operations:

- Completions with IORING_CQE_F_BUF_MORE indicate the buffer will be used for more completions

- Each completion picks up where the previous left off

- The buffer is only returned when consumed completely or on error

This is useful for registering large buffer regions that are consumed in smaller chunks.

### Returning buffers
When finished with a buffer, return it to the ring using io_uring_buf_ring_add(3) followed by io_uring_buf_ring_advance(3). For efficiency when processing multiple CQEs, use io_uring_buf_ring_cq_advance(3) to advance both the CQ and buffer ring in a single operation.

### Buffer ring status
Applications can query how many buffers are available using io_uring_buf_ring_available(3), which returns the number of buffers the kernel has not yet consumed. The current kernel head position can be retrieved with io_uring_buf_ring_head(3).

### Cleaning up
Buffer rings are freed using io_uring_free_buf_ring(3), which unregisters the ring and frees the ring memory (if it was allocated by io_uring_setup_buf_ring(3)). Applications must free the individual buffers themselves.

## NOTES

- Buffer ring entries must be a power of two, maximum 32768.

- Buffer IDs are 16-bit values (0-65535).

- If no buffer is available when an operation needs one, the operation fails with -ENOBUFS. Applications should ensure the ring is adequately stocked.

- Provided buffers cannot be used with registered (fixed) buffers. These are separate mechanisms.

- For multishot receives, ensure buffers are returned to the ring promptly to avoid running out.

### Legacy provided buffers
Earlier kernels supported provided buffers via IORING_OP_PROVIDE_BUFFERS and IORING_OP_REMOVE_BUFFERS. This mechanism required submitting SQEs to add or remove buffers, adding latency and overhead. The ring-based mechanism described above supersedes this approach and should be used for all new applications. The legacy interface remains for backwards compatibility.

## SEE ALSO
io_uring(7), io_uring_setup_buf_ring(3), io_uring_free_buf_ring(3), io_uring_register_buf_ring(3), io_uring_unregister_buf_ring(3), io_uring_buf_ring_add(3), io_uring_buf_ring_advance(3), io_uring_buf_ring_cq_advance(3), io_uring_buf_ring_available(3), io_uring_prep_recv_multishot(3)


title: io_uring_registered_buffers(7)

summary: io_uring registered buffers overview

text:
# io_uring_registered_buffers(7)

## NAME
io_uring_registered_buffers - io_uring registered buffers overview

## DESCRIPTION
Registered buffers are a performance optimization feature of io_uring that allows applications to pre-register a set of buffers with the kernel. When buffers are registered, the kernel pins the memory and creates long-term mappings, eliminating the overhead of mapping and unmapping buffer memory for each I/O operation.

### Why use registered buffers?
For every I/O operation that transfers data between user space and the kernel, the kernel must perform several operations on the buffer memory:

- Verify the memory is accessible to the process

- Pin the pages in memory to prevent them from being swapped out

- Set up kernel mappings to access the memory

These operations, while individually fast, add up when performing many small I/O operations. By registering buffers once upfront, these costs are paid only once, and subsequent I/O operations can use the pre-mapped buffers directly.

Registered buffers are most beneficial for applications that:

- Perform many small I/O operations

- Reuse the same buffers repeatedly

- Need the lowest possible per-I/O overhead

### Registering buffers
Buffers are registered using io_uring_register_buffers(3) or io_uring_register_buffers_tags(3). The buffers are described using an array of struct iovec structures:

```
struct iovec iovecs[2];
iovecs[0].iov_base = buf1;
iovecs[0].iov_len = 4096;
iovecs[1].iov_base = buf2;
iovecs[1].iov_len = 8192;
ret = io_uring_register_buffers(ring, iovecs, 2);
```

The buffers must be anonymous memory (allocated via malloc(3), mmap(2) with MAP_ANONYMOUS, or similar). File-backed memory is not supported.

There is a limit of 1 GiB per individual buffer. Huge pages are supported and the entire huge page will be pinned even if only part of it is used.

The buffers are charged against the user's RLIMIT_MEMLOCK resource limit on kernels before 5.12. On kernel 5.12 and later with IORING_FEAT_NATIVE_WORKERS support, cgroup memory accounting is used instead and no memlock limit applies.

Unless running as root, if buffer registration fails with ENOMEM, the memlock limit may need to be increased. The current limit can be checked with:

```
ulimit -l
```

The limit can be increased for the current shell session with:

```
ulimit -l unlimited
```

For a permanent change, edit /etc/security/limits.conf or use setrlimit(2) programmatically with RLIMIT_MEMLOCK.

### Using registered buffers
To use a registered buffer in an I/O operation, use the fixed buffer variants of the prep functions:

- io_uring_prep_read_fixed(3) instead of io_uring_prep_read(3)

- io_uring_prep_write_fixed(3) instead of io_uring_prep_write(3)

- io_uring_prep_readv_fixed(3) instead of io_uring_prep_readv(3)

- io_uring_prep_writev_fixed(3) instead of io_uring_prep_writev(3)

Zero-copy send operations can also use registered buffers:

- io_uring_prep_send_zc(3) with IORING_RECVSEND_FIXED_BUF

- io_uring_prep_sendmsg_zc(3) with IORING_RECVSEND_FIXED_BUF

These functions take a buf_index parameter that specifies which registered buffer to use (0-indexed into the array passed to io_uring_register_buffers(3)).

The memory range used for the I/O operation must fall within the bounds of the registered buffer. It is valid to use only a portion of a registered buffer for an operation.

```
/* Use first 1024 bytes of registered buffer 0 */
io_uring_prep_read_fixed(sqe, fd, buf1, 1024, offset, 0);
/* Use registered buffer 1 */
io_uring_prep_write_fixed(sqe, fd, buf2, 2048, offset, 1);
```

### Sparse buffer registration
Applications can register a sparse buffer table using io_uring_register_buffers_sparse(3). This creates a table with empty slots that can be filled in later using io_uring_register_buffers_update_tag(3). This is useful when the full set of buffers is not known at registration time.

```
/* Create sparse table with 10 slots */
ret = io_uring_register_buffers_sparse(ring, 10);
/* Later, fill in slot 3 */
struct iovec iov = { .iov_base = buf, .iov_len = 4096 };
ret = io_uring_register_buffers_update_tag(ring, 3, &iov, NULL, 1);
```

### Buffer tagging
When using io_uring_register_buffers_tags(3) or io_uring_register_buffers_update_tag(3), each buffer can be associated with a tag value. When a buffer is unregistered (either explicitly or by replacing it), and there are no more in-flight operations using that buffer, a completion queue entry is posted with user_data set to the tag value and all other fields zeroed.

This allows applications to know when it is safe to free or reuse the buffer memory.

### Updating registered buffers
Registered buffers can be updated in place using io_uring_register_buffers_update_tag(3). This can:

- Replace an existing buffer with a new one

- Fill in a sparse slot

- Remove a buffer by setting the iovec to zero length

Updating buffers does not immediately free resources. The old buffer remains valid until all in-flight operations complete.

### Unregistering buffers
Buffers are unregistered using io_uring_unregister_buffers(3). This releases all registered buffers. Buffers are also automatically unregistered when the io_uring instance is destroyed.

Applications do not need to explicitly unregister buffers before shutting down the ring. However, page unpinning may happen asynchronously, so pages may not be immediately available after ring destruction.

### Cloning buffers
Registered buffers can be cloned from one ring to another using io_uring_clone_buffers(3) or io_uring_clone_buffers_offset(3). This allows multiple rings to share the same set of registered buffers without re-registering them.

## NOTES

- Registered buffers provide the most benefit for small, frequent I/O operations where the per-operation overhead is significant.

- For large I/O operations, the buffer mapping overhead is small relative to the actual I/O time, so registered buffers may not provide much benefit.

- The maximum number of registered buffers is limited by available kernel memory and the RLIMIT_MEMLOCK limit (on older kernels).

- Registered buffers cannot be used with provided buffer rings (IOSQE_BUFFER_SELECT). These are separate mechanisms for different use cases.

## SEE ALSO
io_uring(7), io_uring_registered_files(7), setrlimit(2), io_uring_register_buffers(3), io_uring_register_buffers_tags(3), io_uring_register_buffers_sparse(3), io_uring_register_buffers_update_tag(3), io_uring_unregister_buffers(3), io_uring_prep_read_fixed(3), io_uring_prep_write_fixed(3), io_uring_prep_send_zc(3), io_uring_prep_sendmsg_zc(3), io_uring_clone_buffers(3)


title: io_uring_registered_files(7)

summary: io_uring registered files overview

text:
# io_uring_registered_files(7)

## NAME
io_uring_registered_files - io_uring registered files overview

## DESCRIPTION
Registered files (also known as fixed files) are a performance optimization feature of io_uring that allows applications to pre-register a set of file descriptors with the kernel. When files are registered, the kernel takes a reference to each file, avoiding the overhead of looking up file descriptors and taking references for each I/O operation.

### Why use registered files?
For every I/O operation that uses a file descriptor, the kernel must:

- Look up the file descriptor in the process's file descriptor table

- Take a reference to the file to ensure it remains valid during the operation

- Release the reference when the operation completes

For applications performing many I/O operations, especially on threaded applications where the file table is shared (making reference counting more expensive), these overheads accumulate. By registering files once, the reference is held for the lifetime of the registration, and operations can use the file directly without per-operation lookups or reference counting.

Registered files are most beneficial for applications that:

- Perform many I/O operations on the same set of files

- Are multi-threaded (where file table operations are more expensive)

- Need the lowest possible per-I/O overhead

### Registering files
Files are registered using io_uring_register_files(3) or io_uring_register_files_tags(3). The files are described using an array of file descriptors:

```
int fds[3];
fds[0] = open("file1", O_RDONLY);
fds[1] = open("file2", O_RDONLY);
fds[2] = open("file3", O_WRONLY | O_CREAT, 0644);
ret = io_uring_register_files(ring, fds, 3);
```

Once registered, the original file descriptors can be closed if desired. The kernel holds its own references to the underlying files.

### Using registered files
To use a registered file in an I/O operation, set the IOSQE_FIXED_FILE flag in the SQE's flags field, and use the index into the registered file array (not the original file descriptor) in the fd field:

```
struct io_uring_sqe *sqe = io_uring_get_sqe(ring);
io_uring_prep_read(sqe, 0, buf, len, offset);  /* index 0, not fd */
io_uring_sqe_set_flags(sqe, IOSQE_FIXED_FILE);
```

The index is 0-based into the array passed to io_uring_register_files(3).

### Sparse file registration
The file array can be sparse, meaning some slots can be empty. Empty slots are indicated by setting the file descriptor to -1. Applications can create a fully sparse table using io_uring_register_files_sparse(3) and fill in slots later:

```
/* Create sparse table with 100 slots */
ret = io_uring_register_files_sparse(ring, 100);
/* Later, fill in slot 5 */
int fd = open("file", O_RDONLY);
ret = io_uring_register_files_update(ring, 5, &fd, 1);
```

### Updating registered files
Registered files can be updated using io_uring_register_files_update(3) or io_uring_register_files_update_tag(3). This can:

- Replace an existing file with a new one

- Fill in an empty slot

- Remove a file by setting the descriptor to -1

To skip updating certain slots while updating others, use the special value IORING_REGISTER_FILES_SKIP.

```
int fds[3];
fds[0] = new_fd;                        /* replace slot 0 */
fds[1] = IORING_REGISTER_FILES_SKIP;    /* leave slot 1 unchanged */
fds[2] = -1;                            /* remove slot 2 */
ret = io_uring_register_files_update(ring, 0, fds, 3);
```

Updates do not require the ring to be idle on kernels 5.13 and later. On older kernels, updates would wait for in-flight operations to complete.

### File tagging
When using io_uring_register_files_tags(3) or io_uring_register_files_update_tag(3), each file can be associated with a tag value. When a file is unregistered (either explicitly or by replacement), and there are no more in-flight operations using that file, a completion queue entry is posted with user_data set to the tag value and all other fields zeroed.

This notification mechanism allows applications to know when it is safe to perform cleanup actions associated with the file.

### Direct file descriptors
Some io_uring operations can allocate file descriptors directly into the registered file table, avoiding the regular file descriptor table entirely. This is done by setting the file_index field in the SQE (using io_uring_sqe_set_target_fixed_file(3)) to the desired slot, or using IORING_FILE_INDEX_ALLOC to have io_uring allocate the next available slot.

Operations that support direct descriptors include:

- IORING_OP_OPENAT / IORING_OP_OPENAT2

- IORING_OP_ACCEPT

- IORING_OP_SOCKET

- IORING_OP_PIPE

When using IORING_FILE_INDEX_ALLOC, the application should use io_uring_register_file_alloc_range(3) to specify which range of the file table should be used for allocations.

```
/* Reserve slots 50-99 for dynamic allocation */
io_uring_register_file_alloc_range(ring, 50, 50);
/* Accept with direct descriptor allocation */
struct io_uring_sqe *sqe = io_uring_get_sqe(ring);
io_uring_prep_accept_direct(sqe, listen_fd, NULL, NULL, 0,

                            IORING_FILE_INDEX_ALLOC);
```

The allocated slot index is returned in the CQE res field on success.

### Closing direct descriptors
Direct descriptors (files that exist only in the registered file table) can be closed using IORING_OP_CLOSE with the IOSQE_FIXED_FILE flag set, or by updating the slot to -1 using io_uring_register_files_update(3).

### Unregistering files
Files are unregistered using io_uring_unregister_files(3). This releases all registered files. Files are also automatically unregistered when the io_uring instance is destroyed.

Applications do not need to explicitly unregister files before shutting down the ring.

## NOTES

- Registered files provide the most benefit for applications performing many operations on the same files, especially multi-threaded applications.

- Direct descriptors (files that only exist in the registered table) are not visible to operations outside io_uring, such as read(2) or write(2).

- The IOSQE_FIXED_FILE flag must be set when using a registered file index; without it, the fd field is interpreted as a regular file descriptor.

- It is an error to use IOSQE_FIXED_FILE with an index that does not correspond to a registered file.

## SEE ALSO
io_uring(7), io_uring_registered_buffers(7), io_uring_register_files(3), io_uring_register_files_tags(3), io_uring_register_files_sparse(3), io_uring_register_files_update(3), io_uring_register_files_update_tag(3), io_uring_unregister_files(3), io_uring_register_file_alloc_range(3)


title: io_uring_setup_flags(7)

summary: io_uring ring setup flags overview

text:
# io_uring_setup_flags(7)

## NAME
io_uring_setup_flags - io_uring ring setup flags overview

## DESCRIPTION
When creating an io_uring instance with io_uring_queue_init_params(3) or io_uring_setup(2), various flags control the ring's behavior. These flags are set in the flags field of struct io_uring_params.

Choosing the right flags can significantly impact performance. This page provides an overview of available flags, their purposes, and common combinations.

### Polling flags
These flags control how I/O completion and submission polling works.

IORING_SETUP_IOPOLL

Enable I/O polling mode for file descriptors that support it. Instead of relying on interrupts, the kernel polls for completions. This reduces latency for high-performance storage devices (NVMe, etc.) but requires:

- Files opened with O_DIRECT

- Hardware and drivers that support polling

- The application to call io_uring_enter(2) to reap completions (busy-polling)

- Storage device configuration for polling support

IOPOLL rings cannot use IRQ-driven completion; the application must poll. Only request types that support polling may be issued on an IOPOLL ring. This mode is commonly used for scenarios that purely do polled I/O on storage devices like NVMe.

Using IOPOLL generally requires storage device setup. For NVMe devices, the kernel parameter nvme.poll_queues=X must be set, where X is the number of completion queues on the NVMe device to set aside for polling operations.

IORING_SETUP_SQPOLL

Create a kernel thread that polls the submission queue. Eliminates the need for system calls to submit I/O. See io_uring_sqpoll(7) for details.

IORING_SETUP_SQ_AFF

Pin the SQPOLL thread to a specific CPU. Requires IORING_SETUP_SQPOLL. The CPU is specified in sq_thread_cpu of struct io_uring_params.

IORING_SETUP_HYBRID_IOPOLL

Enable hybrid polling mode. Instead of pure busy-polling, the kernel uses an adaptive approach that may sleep briefly, reducing CPU usage while still providing low latency. This is a middle ground between interrupt-driven and pure polling modes.

### Task run flags
These flags control when and how completion processing runs.

IORING_SETUP_COOP_TASKRUN

Disable interrupting the application for completion processing. Normally, the kernel signals the application when completions are ready, which can interrupt system calls. With this flag, completions are only processed when the application returns to userspace from any system call, not just io_uring-related ones. This means completions may be processed after read(2), write(2), poll(2), or any other syscall returns. This improves performance by eliminating asynchronous interrupts but requires the application to regularly enter the kernel to process completions. Recommended for most applications that have an event loop.

IORING_SETUP_TASKRUN_FLAG

When completions are pending, set IORING_SQ_TASKRUN in the SQ ring flags. This allows applications to check if there is completion work to process without making a system call. Typically used with IORING_SETUP_COOP_TASKRUN.

IORING_SETUP_DEFER_TASKRUN

Defer completion task work to when the application explicitly enters the kernel via io_uring_enter(2). Unlike IORING_SETUP_COOP_TASKRUN, completions are only processed during io_uring-related syscalls, not on return from arbitrary syscalls. This provides the tightest and most predictable control over when completion processing occurs, as well as optimal cache behavior since work runs in the application's context. This flag should be considered the default mode for applications setting up a ring. It requires IORING_SETUP_SINGLE_ISSUER and a ring created per-thread. The application must regularly call io_uring_enter(2) (via io_uring_submit(3), io_uring_wait_cqe(3), or similar) to process deferred work; failing to do so will stall completions.

Some features require this flag:

- Ring resizing (io_uring_register_resize_rings(3))

- Zero-copy receive (IORING_OP_RECV_ZC)

IORING_SETUP_SINGLE_ISSUER

Hint that only one task will submit requests to this ring. Enables internal optimizations including reduced locking overhead. The first task to submit a request becomes the designated submitter; others attempting to submit will get -EEXIST. Each thread or task having its own ring is the idiomatic use case for io_uring. Sharing a ring between multiple threads or tasks is discouraged as it requires additional synchronization and prevents many optimizations. Applications should create a ring per thread rather than sharing rings.

### Ring sizing flags
These flags control the size and layout of the submission and completion queues.

IORING_SETUP_CQSIZE

Override the default completion queue size. By default, the CQ has twice as many entries as the SQ. Set cq_entries in struct io_uring_params to specify a custom CQ size. Must be a power of 2. Larger CQ sizes are useful when the application may submit many requests before processing completions, avoiding CQ overflow.

IORING_SETUP_CLAMP

Clamp the SQ and CQ sizes to the maximum allowed values instead of returning -EINVAL if the requested sizes are too large. Useful when the application wants the largest possible rings without querying limits.

IORING_SETUP_SQE128

Use 128-byte SQEs instead of the default 64 bytes. Required for some operations that need extra space, such as IORING_OP_URING_CMD passthrough commands.

IORING_SETUP_CQE32

Use 32-byte CQEs instead of the default 16 bytes. Required for operations that return extra data, such as some passthrough commands or when using IORING_OP_MSG_RING.

IORING_SETUP_NO_SQARRAY

Do not create the SQ array. The SQ array is a level of indirection that allows SQEs to be submitted in a different order than they appear in the ring. Most applications submit SQEs in order and do not need this. This flag saves memory and is required for some modes like IORING_SETUP_REGISTERED_FD_ONLY.

IORING_SETUP_SQ_REWIND

Use non-circular submission queue mode. The kernel ignores the SQ head and tail pointers and instead fetches SQEs starting from index 0 on each submit. The application places all SQEs at the beginning of the ring before calling io_uring_enter(2), and the sq_entries parameter determines how many SQEs are submitted. Requires IORING_SETUP_NO_SQARRAY. Not compatible with IORING_SETUP_SQPOLL.

This mode keeps SQEs hot in cache by always accessing the same memory locations at the start of the ring, improving performance for workloads that submit small batches frequently.

IORING_SETUP_CQE_MIXED

Allow the ring to return a mix of 16-byte and 32-byte CQEs, controlled per-request. When a request needs a 32-byte CQE, it sets IOSQE_BIG_CQE in its flags. Otherwise, a 16-byte CQE is used. Requires IORING_SETUP_CQE32. This is useful when certain operations require 32-byte CQEs (such as some passthrough commands) but most operations do not. Using mixed mode instead of IORING_SETUP_CQE32 alone provides efficiency benefits in terms of memory bandwidth and usage, since the smaller 16-byte CQEs are used for operations that do not need the extra space.

IORING_SETUP_SQE_MIXED

Allow the ring to accept a mix of 64-byte and 128-byte SQEs. When a request needs a 128-byte SQE, it sets IOSQE_BIG_SQE in its flags. Requires IORING_SETUP_SQE128. This is useful when certain operations require 128-byte SQEs (such as IORING_OP_URING_CMD) but most operations do not. Using mixed mode instead of IORING_SETUP_SQE128 alone provides efficiency benefits in terms of memory bandwidth and usage, since the smaller 64-byte SQEs are used for operations that do not need the extra space.

### Memory and setup flags
These flags control memory allocation and ring initialization.

IORING_SETUP_NO_MMAP

The application provides its own memory for the rings instead of the kernel allocating and the application mmap'ing it. The application fills in sq_off.user_addr, cq_off.user_addr, and sq_sqes.user_addr in struct io_uring_params with addresses of application-allocated memory. This is useful for placing rings in specific memory (huge pages, shared memory, etc.) or for creating rings without mmap.

IORING_SETUP_REGISTERED_FD_ONLY

The ring file descriptor is not installed in the process's file descriptor table. Instead, a "registered ring" index is returned in ring_fd that can be used with io_uring_enter(2) when IORING_ENTER_REGISTERED_RING is set. This reduces per-operation overhead. Requires IORING_SETUP_NO_SQARRAY. The application must use io_uring_register_ring_fd(3) to use the ring or access it via the registered index.

IORING_SETUP_R_DISABLED

Create the ring in a disabled state. The ring will not accept submissions until it is enabled via io_uring_enable_rings(3). This is useful when setting up restrictions or registered resources before allowing I/O. See io_uring_register_restrictions(3).

### Submission flags
These flags control submission behavior.

IORING_SETUP_SUBMIT_ALL

Continue processing submissions even if one fails. Normally, if an SQE fails during submission (not execution), subsequent SQEs in the same submit call are not processed. With this flag, all SQEs are processed regardless of earlier failures. The failed SQE still generates a CQE with the error; this flag only affects whether subsequent SQEs are submitted. This is probably the behavior most applications expect, since CQEs are generated for failed submissions anyway and the application must handle them regardless.

### Workqueue flags
These flags control the async worker threads.

IORING_SETUP_ATTACH_WQ

Share the async worker thread pool with another ring. Set wq_fd in struct io_uring_params to the file descriptor of the ring to share with. This reduces resource usage when an application uses multiple rings. When combined with IORING_SETUP_SQPOLL, the SQPOLL thread is also shared.

### Common flag combinations
High-performance single-threaded application:

```
        IORING_SETUP_DEFER_TASKRUN |

        IORING_SETUP_COOP_TASKRUN
```

This combination provides the best latency and throughput for applications where each thread has its own ring and processes completions in a dedicated event loop.

Low-latency storage with polling:

```
        IORING_SETUP_SINGLE_ISSUER |

        IORING_SETUP_DEFER_TASKRUN
```

For NVMe or other devices that support polling, this eliminates interrupt overhead. Combined with DEFER_TASKRUN for optimal completion handling.

System call-free submission:

```
        IORING_SETUP_SQ_AFF
```

For workloads that benefit from eliminating submission syscall overhead. See io_uring_sqpoll(7).

Multiple rings sharing resources:

```
/* First ring */
p1.flags = IORING_SETUP_SQPOLL;
/* Subsequent rings */
p2.flags = IORING_SETUP_SQPOLL | IORING_SETUP_ATTACH_WQ;
p2.wq_fd = ring1_fd;
```

Reduces kernel thread and workqueue overhead when using multiple rings.

## NOTES

- Not all flag combinations are valid. The kernel returns -EINVAL for incompatible combinations.

- Some flags require specific kernel versions. Check io_uring_setup(2) for version requirements.

- The io_uring_queue_init_params(3) function handles the complexity of ring setup. Using the raw io_uring_setup(2) syscall requires careful mmap setup.

- For most applications with a proper event loop, IORING_SETUP_DEFER_TASKRUN combined with IORING_SETUP_SINGLE_ISSUER is the recommended default. This provides the best control over when completion work runs and optimal cache locality.

## SEE ALSO
io_uring(7), io_uring_sqpoll(7), io_uring_setup(2), io_uring_queue_init_params(3), io_uring_register_restrictions(3), io_uring_enable_rings(3)


title: io_uring_sqpoll(7)

text:
# io_uring_sqpoll(7)

## NAME
io_uring_sqpoll - io_uring submission queue polling overview

## DESCRIPTION
Submission queue polling (SQPOLL) is a mode of operation where an io_uring created userspace thread that remains in the kernel monitors the submission queue and submits requests on behalf of the application. This eliminates the need for the application to make system calls to submit I/O, reducing latency and CPU overhead for high-throughput workloads.

### Why use SQPOLL?
In normal io_uring operation, applications must call io_uring_enter(2) (typically via io_uring_submit(3)) to notify the kernel of new submissions. While efficient, this still incurs system call overhead.

With SQPOLL enabled, the kernel thread continuously polls the submission queue for new entries. As soon as the application writes an SQE to the ring, the kernel thread picks it up and submits it. This provides:

- Elimination of submission system call overhead

- Lower and more predictable latency

- Better CPU utilization for high-IOPS workloads

SQPOLL is most beneficial for:

- High-throughput storage workloads (NVMe, etc.)

- Latency-sensitive applications

- Workloads with continuous I/O streams

- Applications already running at high CPU utilization

### When SQPOLL may not help
SQPOLL is not universally beneficial and each use case should be benchmarked to determine if it provides value. Situations where SQPOLL may not help or may hurt performance:

- Low-IOPS workloads: If the application submits I/O infrequently, the system call overhead being saved is negligible, and the polling thread wastes CPU cycles.

- CPU-constrained systems: The polling thread consumes CPU. If the system is already CPU-bound, adding a polling thread may compete with the application for CPU resources, reducing overall performance.

- Bursty workloads: If I/O comes in bursts with idle periods, the polling thread may frequently sleep and wake, adding latency when it needs to wake up. Regular submission may be more efficient.

- Single-threaded applications on single-CPU systems: The polling thread and application will compete for the same CPU, potentially causing context switches that negate any benefits.

- Workloads dominated by completion handling: SQPOLL only optimizes submissions. If the application spends most of its time processing completions, SQPOLL provides little benefit.

Always benchmark with and without SQPOLL under realistic conditions. The performance difference can vary significantly based on hardware, kernel version, and workload characteristics.

### Enabling SQPOLL
SQPOLL is enabled by setting the IORING_SETUP_SQPOLL flag when creating the ring:

```
struct io_uring ring;
struct io_uring_params params = {

    .flags = IORING_SETUP_SQPOLL,

    .sq_thread_idle = 2000,  /* 2 seconds */
};
ret = io_uring_queue_init_params(entries, &ring, &params);
```

The sq_thread_idle field specifies how long (in milliseconds) the kernel thread will poll before going to sleep if no submissions are pending. A value of 0 means the thread never sleeps (uses more CPU but provides lowest latency).

### The polling thread lifecycle
When the ring is created with SQPOLL, a kernel thread is spawned to service it. The thread's behavior is:

1.

Poll the submission queue for new entries

2.

Submit any new requests found

3.

If no new entries are found for sq_thread_idle milliseconds, go to sleep

4.

Wake up when signaled by the application

The application can check if the thread is sleeping by examining sq->kflags for the IORING_SQ_NEED_WAKEUP flag using io_uring_sq_ready(3). If set, the application must call io_uring_enter(2) with IORING_ENTER_SQ_WAKEUP to wake the thread:

```
/* After adding SQEs */
io_uring_smp_store_release(ring->sq.ktail, tail);
if (IO_URING_READ_ONCE(*ring->sq.kflags) & IORING_SQ_NEED_WAKEUP)

    io_uring_enter(ring->ring_fd, 0, 0, IORING_ENTER_SQ_WAKEUP, NULL);
```

The io_uring_submit(3) function handles this automatically.

### CPU affinity
By default, the kernel schedules the polling thread on any available CPU. For better cache locality and reduced latency, the thread can be pinned to a specific CPU:

```
struct io_uring_params params = {

    .flags = IORING_SETUP_SQPOLL | IORING_SETUP_SQ_AFF,

    .sq_thread_cpu = 3,  /* pin to CPU 3 */

    .sq_thread_idle = 1000,
};
```

The IORING_SETUP_SQ_AFF flag enables CPU affinity, and sq_thread_cpu specifies which CPU to use.

### Credential requirements
Creating an SQPOLL ring traditionally required elevated privileges because the kernel thread runs on behalf of the application. The requirements have evolved:

- Kernel 5.11 and earlier: requires CAP_SYS_ADMIN or CAP_SYS_NICE

- Kernel 5.12 and later: unprivileged users can create SQPOLL rings, but the polling thread runs with reduced capabilities

- The IORING_SETUP_NO_SQARRAY flag (kernel 6.6+) can simplify setup for SQPOLL-only rings

### Sharing the polling thread
Multiple rings can share a single polling thread using IORING_SETUP_ATTACH_WQ. This reduces resource usage when an application uses multiple rings:

```
/* Create first ring with SQPOLL */
struct io_uring_params p1 = { .flags = IORING_SETUP_SQPOLL };
io_uring_queue_init_params(entries, &ring1, &p1);
/* Create second ring, attach to first ring's thread */
struct io_uring_params p2 = {

    .flags = IORING_SETUP_SQPOLL | IORING_SETUP_ATTACH_WQ,

    .wq_fd = ring1.ring_fd,
};
io_uring_queue_init_params(entries, &ring2, &p2);
```

### Completion handling
SQPOLL only affects submissions. Completions are still handled normally — the application must either:

- Poll the completion queue directly (busy-wait)

- Use io_uring_enter(2) with IORING_ENTER_GETEVENTS to wait for completions

- Use an eventfd for notification

For full polling on both submission and completion, combine SQPOLL with completion queue polling using io_uring_peek_cqe(3) or similar functions.

### Performance considerations

- CPU usage: The polling thread consumes CPU while active. If I/O is sporadic, the thread may waste cycles polling an empty queue. Set sq_thread_idle appropriately for your workload.

- Idle timeout tradeoff: A shorter idle timeout saves CPU but may increase latency when the thread needs to wake up. A longer timeout (or 0 for never sleeping) uses more CPU but provides consistent low latency.

- Batching: Even with SQPOLL, batching submissions by adding multiple SQEs before updating the tail pointer can improve throughput.

- CPU affinity: Pinning the polling thread to a CPU near the application's CPU can improve cache behavior and reduce cross-CPU communication.

## NOTES

- The polling thread is per-ring (unless shared via IORING_SETUP_ATTACH_WQ). Creating many SQPOLL rings without sharing can consume significant kernel resources.

- SQPOLL rings still require system calls for:

- Waiting for completions (unless busy-polling the CQ)

- Waking the thread when it has gone idle

- Registration operations

- The polling thread inherits resource limits and cgroup membership from the creating process.

- If the polling thread encounters an error it cannot recover from, IORING_SQ_CQ_OVERFLOW may be set in sq->kflags.

- SQPOLL works well in combination with registered files and buffers, which further reduce per-I/O overhead.

## SEE ALSO
io_uring(7), io_uring_setup(2), io_uring_enter(2), io_uring_queue_init_params(3), io_uring_register_files(3), io_uring_registered_buffers(7)


package: libvirt-client

title: virkeycode-atset1(7)

summary: Key code values for atset1

text:
# virkeycode-atset1(7)

## NAME
virkeycode-atset1 - Key code values for atset1

## DESCRIPTION
List of atset1 key code values, with corresponding linux key code names

- 1 (0x1) Key name KEY_ESC

- 2 (0x2) Key name KEY_1

- 3 (0x3) Key name KEY_2

- 4 (0x4) Key name KEY_3

- 5 (0x5) Key name KEY_4

- 6 (0x6) Key name KEY_5

- 7 (0x7) Key name KEY_6

- 8 (0x8) Key name KEY_7

- 9 (0x9) Key name KEY_8

- 10 (0xa) Key name KEY_9

- 11 (0xb) Key name KEY_0

- 12 (0xc) Key name KEY_MINUS

- 13 (0xd) Key name KEY_EQUAL

- 14 (0xe) Key name KEY_BACKSPACE

- 15 (0xf) Key name KEY_TAB

- 16 (0x10) Key name KEY_Q

- 17 (0x11) Key name KEY_W

- 18 (0x12) Key name KEY_E

- 19 (0x13) Key name KEY_R

- 20 (0x14) Key name KEY_T

- 21 (0x15) Key name KEY_Y

- 22 (0x16) Key name KEY_U

- 23 (0x17) Key name KEY_I

- 24 (0x18) Key name KEY_O

- 25 (0x19) Key name KEY_P

- 26 (0x1a) Key name KEY_LEFTBRACE

- 27 (0x1b) Key name KEY_RIGHTBRACE

- 28 (0x1c) Key name KEY_ENTER

- 29 (0x1d) Key name KEY_LEFTCTRL

- 30 (0x1e) Key name KEY_A

- 31 (0x1f) Key name KEY_S

- 32 (0x20) Key name KEY_D

- 33 (0x21) Key name KEY_F

- 34 (0x22) Key name KEY_G

- 35 (0x23) Key name KEY_H

- 36 (0x24) Key name KEY_J

- 37 (0x25) Key name KEY_K

- 38 (0x26) Key name KEY_L

- 39 (0x27) Key name KEY_SEMICOLON

- 40 (0x28) Key name KEY_APOSTROPHE

- 41 (0x29) Key name KEY_GRAVE

- 42 (0x2a) Key name KEY_LEFTSHIFT

- 43 (0x2b) Key name KEY_BACKSLASH

- 44 (0x2c) Key name KEY_Z

- 45 (0x2d) Key name KEY_X

- 46 (0x2e) Key name KEY_C

- 47 (0x2f) Key name KEY_V

- 48 (0x30) Key name KEY_B

- 49 (0x31) Key name KEY_N

- 50 (0x32) Key name KEY_M

- 51 (0x33) Key name KEY_COMMA

- 52 (0x34) Key name KEY_DOT

- 53 (0x35) Key name KEY_SLASH

- 54 (0x36) Key name KEY_RIGHTSHIFT

- 55 (0x37) Key name KEY_KPASTERISK

- 56 (0x38) Key name KEY_LEFTALT

- 57 (0x39) Key name KEY_SPACE

- 58 (0x3a) Key name KEY_CAPSLOCK

- 59 (0x3b) Key name KEY_F1

- 60 (0x3c) Key name KEY_F2

- 61 (0x3d) Key name KEY_F3

- 62 (0x3e) Key name KEY_F4

- 63 (0x3f) Key name KEY_F5

- 64 (0x40) Key name KEY_F6

- 65 (0x41) Key name KEY_F7

- 66 (0x42) Key name KEY_F8

- 67 (0x43) Key name KEY_F9

- 68 (0x44) Key name KEY_F10

- 69 (0x45) Key name KEY_NUMLOCK

- 70 (0x46) Key name KEY_SCROLLLOCK

- 71 (0x47) Key name KEY_KP7

- 72 (0x48) Key name KEY_KP8

- 73 (0x49) Key name KEY_KP9

- 74 (0x4a) Key name KEY_KPMINUS

- 75 (0x4b) Key name KEY_KP4

- 76 (0x4c) Key name KEY_KP5

- 77 (0x4d) Key name KEY_KP6

- 78 (0x4e) Key name KEY_KPPLUS

- 79 (0x4f) Key name KEY_KP1

- 80 (0x50) Key name KEY_KP2

- 81 (0x51) Key name KEY_KP3

- 82 (0x52) Key name KEY_KP0

- 83 (0x53) Key name KEY_KPDOT

- 84 (0x54) Key name KEY_SYSRQ

- 85 (0x55) Key name KEY_F16

- 86 (0x56) Key name KEY_102ND

- 87 (0x57) Key name KEY_F11

- 88 (0x58) Key name KEY_F12

- 89 (0x59) Key name KEY_KPEQUAL

- 90 (0x5a) Key name KEY_F20

- 91 (0x5b) Key name KEY_LINEFEED

- 92 (0x5c) Key name KEY_KPJPCOMMA

- 93 (0x5d) Key name KEY_F13

- 94 (0x5e) Key name KEY_F14

- 95 (0x5f) Key name KEY_F15

- 99 (0x63) Key name KEY_PHONE

- 100 (0x64) Key name KEY_OPEN

- 101 (0x65) Key name KEY_PASTE

- 102 (0x66) Key name KEY_SETUP

- 103 (0x67) Key name KEY_FILE

- 104 (0x68) Key name KEY_SENDFILE

- 105 (0x69) Key name KEY_DELETEFILE

- 106 (0x6a) Key name KEY_MSDOS

- 107 (0x6b) Key name KEY_DIRECTION

- 108 (0x6c) Key name KEY_EJECTCD

- 109 (0x6d) Key name KEY_F23

- 111 (0x6f) Key name KEY_F24

- 112 (0x70) Key name KEY_KATAKANAHIRAGANA

- 115 (0x73) Key name KEY_RO

- 116 (0x74) Key name KEY_F21

- 117 (0x75) Key name KEY_SCROLLUP

- 118 (0x76) Key name KEY_ZENKAKUHANKAKU

- 119 (0x77) Key name KEY_HIRAGANA

- 120 (0x78) Key name KEY_KATAKANA

- 121 (0x79) Key name KEY_HENKAN

- 123 (0x7b) Key name KEY_MUHENKAN

- 125 (0x7d) Key name KEY_YEN

- 126 (0x7e) Key name KEY_KPCOMMA

- 241 (0xf1) Key name KEY_HANJA

- 242 (0xf2) Key name KEY_HANGEUL

- 57345 (0xe001) Key name KEY_CONFIG

- 57346 (0xe002) Key name KEY_WWW

- 57347 (0xe003) Key name KEY_F17

- 57348 (0xe004) Key name KEY_F19

- 57349 (0xe005) Key name KEY_AGAIN

- 57350 (0xe006) Key name KEY_PROPS

- 57351 (0xe007) Key name KEY_UNDO

- 57352 (0xe008) Key name KEY_EDIT

- 57353 (0xe009) Key name KEY_NEW

- 57354 (0xe00a) Key name KEY_REDO

- 57355 (0xe00b) Key name KEY_SCALE

- 57356 (0xe00c) Key name KEY_FRONT

- 57358 (0xe00e) Key name KEY_FORWARDMAIL

- 57359 (0xe00f) Key name KEY_SCROLLDOWN

- 57360 (0xe010) Key name KEY_PREVIOUSSONG

- 57362 (0xe012) Key name KEY_SCREENLOCK

- 57363 (0xe013) Key name KEY_XFER

- 57364 (0xe014) Key name KEY_ALTERASE

- 57365 (0xe015) Key name unnamed

- 57366 (0xe016) Key name unnamed

- 57367 (0xe017) Key name KEY_PROG2

- 57368 (0xe018) Key name KEY_REWIND

- 57369 (0xe019) Key name KEY_NEXTSONG

- 57370 (0xe01a) Key name unnamed

- 57371 (0xe01b) Key name unnamed

- 57372 (0xe01c) Key name KEY_KPENTER

- 57373 (0xe01d) Key name KEY_RIGHTCTRL

- 57374 (0xe01e) Key name KEY_MENU

- 57375 (0xe01f) Key name KEY_PROG1

- 57376 (0xe020) Key name KEY_MUTE

- 57377 (0xe021) Key name KEY_CALC

- 57378 (0xe022) Key name KEY_PLAYPAUSE

- 57379 (0xe023) Key name KEY_CLOSECD

- 57380 (0xe024) Key name KEY_STOPCD

- 57381 (0xe025) Key name KEY_SUSPEND

- 57382 (0xe026) Key name KEY_CYCLEWINDOWS

- 57383 (0xe027) Key name unnamed

- 57384 (0xe028) Key name KEY_PLAYCD

- 57385 (0xe029) Key name KEY_PAUSECD

- 57387 (0xe02b) Key name KEY_PROG3

- 57388 (0xe02c) Key name KEY_PROG4

- 57389 (0xe02d) Key name KEY_DASHBOARD

- 57390 (0xe02e) Key name KEY_VOLUMEDOWN

- 57391 (0xe02f) Key name KEY_CLOSE

- 57392 (0xe030) Key name KEY_VOLUMEUP

- 57393 (0xe031) Key name KEY_RECORD

- 57394 (0xe032) Key name KEY_HOMEPAGE

- 57395 (0xe033) Key name KEY_PLAY

- 57396 (0xe034) Key name KEY_FASTFORWARD

- 57397 (0xe035) Key name KEY_KPSLASH

- 57398 (0xe036) Key name KEY_BASSBOOST

- 57400 (0xe038) Key name KEY_RIGHTALT

- 57401 (0xe039) Key name KEY_PRINT

- 57402 (0xe03a) Key name KEY_HP

- 57403 (0xe03b) Key name KEY_CAMERA

- 57404 (0xe03c) Key name KEY_CUT

- 57405 (0xe03d) Key name KEY_SOUND

- 57406 (0xe03e) Key name KEY_QUESTION

- 57407 (0xe03f) Key name KEY_EMAIL

- 57408 (0xe040) Key name KEY_CHAT

- 57409 (0xe041) Key name KEY_FIND

- 57410 (0xe042) Key name KEY_CONNECT

- 57411 (0xe043) Key name KEY_FINANCE

- 57412 (0xe044) Key name KEY_SPORT

- 57413 (0xe045) Key name KEY_SHOP

- 57414 (0xe046) Key name KEY_PAUSE

- 57415 (0xe047) Key name KEY_HOME

- 57416 (0xe048) Key name KEY_UP

- 57417 (0xe049) Key name KEY_PAGEUP

- 57418 (0xe04a) Key name KEY_CANCEL

- 57419 (0xe04b) Key name KEY_LEFT

- 57420 (0xe04c) Key name KEY_BRIGHTNESSDOWN

- 57421 (0xe04d) Key name KEY_RIGHT

- 57422 (0xe04e) Key name KEY_KPPLUSMINUS

- 57423 (0xe04f) Key name KEY_END

- 57424 (0xe050) Key name KEY_DOWN

- 57425 (0xe051) Key name KEY_PAGEDOWN

- 57426 (0xe052) Key name KEY_INSERT

- 57427 (0xe053) Key name KEY_DELETE

- 57428 (0xe054) Key name KEY_BRIGHTNESSUP

- 57429 (0xe055) Key name KEY_SAVE

- 57430 (0xe056) Key name KEY_SWITCHVIDEOMODE

- 57431 (0xe057) Key name KEY_KBDILLUMTOGGLE

- 57432 (0xe058) Key name KEY_KBDILLUMDOWN

- 57433 (0xe059) Key name KEY_KBDILLUMUP

- 57434 (0xe05a) Key name KEY_SEND

- 57435 (0xe05b) Key name KEY_LEFTMETA

- 57436 (0xe05c) Key name KEY_RIGHTMETA

- 57437 (0xe05d) Key name KEY_COMPOSE

- 57438 (0xe05e) Key name KEY_POWER

- 57439 (0xe05f) Key name KEY_SLEEP

- 57443 (0xe063) Key name KEY_WAKEUP

- 57444 (0xe064) Key name KEY_REPLY

- 57445 (0xe065) Key name KEY_SEARCH

- 57446 (0xe066) Key name KEY_BOOKMARKS

- 57447 (0xe067) Key name KEY_REFRESH

- 57448 (0xe068) Key name KEY_STOP

- 57449 (0xe069) Key name KEY_FORWARD

- 57450 (0xe06a) Key name KEY_BACK

- 57451 (0xe06b) Key name KEY_COMPUTER

- 57452 (0xe06c) Key name KEY_MAIL

- 57453 (0xe06d) Key name KEY_MEDIA

- 57455 (0xe06f) Key name KEY_MACRO

- 57456 (0xe070) Key name KEY_DOCUMENTS

- 57457 (0xe071) Key name KEY_BATTERY

- 57458 (0xe072) Key name KEY_BLUETOOTH

- 57459 (0xe073) Key name KEY_WLAN

- 57460 (0xe074) Key name KEY_UWB

- 57461 (0xe075) Key name KEY_HELP

- 57462 (0xe076) Key name KEY_KPLEFTPAREN

- 57463 (0xe077) Key name KEY_F18

- 57464 (0xe078) Key name KEY_COPY

- 57465 (0xe079) Key name KEY_F22

- 57467 (0xe07b) Key name KEY_KPRIGHTPAREN

- 57469 (0xe07d) Key name KEY_EJECTCLOSECD