io_uring_setup(2)
π« io_uring_setup(2) β Linux Programmer's Manual π«
okay so bestie, welcome to the Linux Programmer's Manual, and honestly? this page is a whole vibe. it's io_uring_setup, and it's giving main character energy in the async I/O world. let's get into it π«Ά
β¨ NAME β¨
io_uring_setup β setup a context for performing asynchronous I/O π
π¦ SYNOPSIS π¦
#include <liburing.h>
int io_uring_setup(u32 entries, struct io_uring_params *params);
πΈ DESCRIPTION πΈ
so the io_uring_setup(2) system call sets up a submission queue (SQ) and a completion queue (CQ) with at least entries entries, and then it returns a file descriptor that you can use to perform subsequent operations on the io_uring instance. and here's the iconic part: the submission and completion queues are shared between userspace and the kernel, which literally eliminates the need to copy data when initiating and completing I/O. no cap, that's the whole magic trick β¨
params is used by the application to pass options to the kernel, and by the kernel to convey information about the ring buffers. teamwork, bestie π€
struct io_uring_params {
__u32 sq_entries;
__u32 cq_entries;
__u32 flags;
__u32 sq_thread_cpu;
__u32 sq_thread_idle;
__u32 features;
__u32 wq_fd;
__u32 resv[3];
struct io_sqring_offsets sq_off;
struct io_cqring_offsets cq_off;
};
the flags, sq_thread_cpu, and sq_thread_idle fields are the ones you use to configure the io_uring instance. flags is a bit mask of 0 or more of the following values ORed together β yes, ORed, go off π
π₯ the flags (a whole mood) π₯
IORING_SETUP_IOPOLLβ perform busy-waiting for an I/O completion, as opposed to getting notifications via an asynchronous IRQ (Interrupt Request). the file system (if any) and block device must support polling in order for this to work. busy-waiting provides lower latency, but may consume more CPU resources than interrupt driven I/O. currently, this feature is usable only on a file descriptor opened using theO_DIRECTflag. when a read or write is submitted to a polled context, the application must poll for completions on the CQ ring by callingio_uring_enter(2). it is illegal to mix and match polled and non-polled I/O on anio_uringinstance π€
this is only applicable for storage devices for now, and the storage device must be configured for polling. how to do that depends on the device type in question. for NVMe devices, the nvme driver must be loaded with the poll_queues parameter set to the desired number of polling queues. the polling queues will be shared appropriately between the CPUs in the system, if the number is less than the number of online CPU threads.
IORING_SETUP_HYBRID_IOPOLLβ this flag must be used withIORING_SETUP_IOPOLL. hybrid io polling is a feature based on iopoll, and the way it differs from strict polling is that it will delay a bit before doing completion side polling, to avoid wasting too much CPU resources. likeIOPOLL, it requires that devices support polling π§IORING_SETUP_SQPOLLβ when this flag is specified, a kernel thread is created to perform submission queue polling. anio_uringinstance configured this way enables an application to issue I/O without ever context switching into the kernel. by using the submission queue to fill in new submission queue entries and watching for completions on the completion queue, the application can submit and reap I/Os without doing a single system call. literally zero syscalls, obsessed π
if the kernel thread is idle for more than sq_thread_idle milliseconds, it will set the IORING_SQ_NEED_WAKEUP bit in the flags field of the struct io_sq_ring. when this happens, the application must call io_uring_enter(2) to wake the kernel thread. if I/O is kept busy, the kernel thread will never sleep. an application making use of this feature will need to guard the io_uring_enter(2) call with the following code sequence:
/*
* Ensure that the wakeup flag is read after the tail pointer
* has been written. It's important to use memory load acquire
* semantics for the flags read, as otherwise the application
* and the kernel might not agree on the consistency of the
* wakeup flag.
*/
unsigned flags = atomic_load_relaxed(sq_ring->flags);
if (flags & IORING_SQ_NEED_WAKEUP)
io_uring_enter(fd, 0, 0, IORING_ENTER_SQ_WAKEUP);
where sq_ring is a submission queue ring setup using the struct io_sqring_offsets described below.
bestie, note that when using a ring setup withIORING_SETUP_SQPOLL, you never directly call theio_uring_enter(2)system call. that is usually taken care of by liburing'sio_uring_submit(3)function. it automatically determines if you are using polling mode or not and deals with when your program needs to callio_uring_enter(2)without you having to bother about it π«Ά
also note that while this may sound immediately appealing as an automatic "go faster" flag, evaluations should be done on a case-by-case basis to check if it makes sense for the application. it's not a free slay, unfortunately π
before version 5.11 of the Linux kernel, to successfully use this feature, the application must register a set of files to be used for IO through io_uring_register(2) using the IORING_REGISTER_FILES opcode. failure to do so will result in submitted IO being errored with EBADF. the presence of this feature can be detected by the IORING_FEAT_SQPOLL_NONFIXED feature flag. in version 5.11 and later, it is no longer necessary to register files to use this feature. 5.11 also allows using this as non-root, if the user has the CAP_SYS_NICE capability. in 5.13 this requirement was also relaxed, and no special privileges are needed for SQPOLL in newer kernels. certain stable kernels older than 5.13 may also support unprivileged SQPOLL.
IORING_SETUP_SQ_AFFβ if this flag is specified, then the poll thread will be bound to the cpu set in thesq_thread_cpufield of thestruct io_uring_params. this flag is only meaningful whenIORING_SETUP_SQPOLLis specified. when cgroup settingcpuset.cpuschanges (typically in container environment), the bounded cpu set may be changed as well πIORING_SETUP_CQSIZEβ create the completion queue withstruct io_uring_params.cq_entriesentries. the value must be greater thanentries, and may be rounded up to the next power-of-two.IORING_SETUP_CLAMPβ if this flag is specified, and ifentriesexceedsIORING_MAX_ENTRIES, thenentrieswill be clamped atIORING_MAX_ENTRIES. if the flagIORING_SETUP_CQSIZEis set, and if the value ofstruct io_uring_params.cq_entriesexceedsIORING_MAX_CQ_ENTRIES, then it will be clamped atIORING_MAX_CQ_ENTRIES.IORING_SETUP_ATTACH_WQβ this flag should be set in conjunction withstruct io_uring_params.wq_fdbeing set to an existingio_uringring file descriptor. when set, theio_uringinstance being created will share the asynchronous worker thread backend of the specifiedio_uringring, rather than create a new separate thread pool. additionally the sq polling thread will be shared, ifIORING_SETUP_SQPOLLis set. sharing is caring fr fr π«ΆIORING_SETUP_R_DISABLEDβ if this flag is specified, theio_uringring starts in a disabled state. in this state, restrictions can be registered, but submissions are not allowed. seeio_uring_register(2)for details on how to enable the ring. available since 5.10.IORING_SETUP_SUBMIT_ALLβ normallyio_uringstops submitting a batch of requests, if one of these requests results in an error. this can cause submission of less than what is expected, if a request ends in error while being submitted. if the ring is created with this flag,io_uring_enter(2)will continue submitting requests even if it encounters an error submitting a request. CQEs are still posted for errored request regardless of whether or not this flag is set at ring creation time β the only difference is if the submit sequence is halted or continued when an error is observed. available since 5.18.IORING_SETUP_COOP_TASKRUNβ by default,io_uringwill interrupt a task running in userspace when a completion event comes in. this is to ensure that completions run in a timely manner. for a lot of use cases, this is overkill and can cause reduced performance from both the inter-processor interrupt used to do this, the kernel/user transition, the needless interruption of the task's userspace activities, and reduced batching if completions come in at a rapid rate. most applications don't need the forceful interruption, as the events are processed at any kernel/user transition. the exception are setups where the application uses multiple threads operating on the same ring, where the application waiting on completions isn't the one that submitted them. for most other use cases, setting this flag will improve performance. available since 5.19 β¨IORING_SETUP_TASKRUN_FLAGβ used in conjunction withIORING_SETUP_COOP_TASKRUN, this provides a flag,IORING_SQ_TASKRUN, which is set in the SQ ring flags whenever completions are pending that should be processed. liburing will check for this flag even when doingio_uring_peek_cqe(3)and enter the kernel to process them, and applications can do the same. this makesIORING_SETUP_TASKRUN_FLAGsafe to use even when applications rely on a peek style operation on the CQ ring to see if anything might be pending to reap. available since 5.19.IORING_SETUP_SQE128β if set,io_uringwill use 128-byte SQEs rather than the normal 64-byte sized variant. this is a requirement for using certain request types β as of 5.19 only theIORING_OP_URING_CMDpassthrough command for NVMe passthrough needs this. available since 5.19.IORING_SETUP_CQE32β if set,io_uringwill use 32-byte CQEs rather than the normal 16-byte sized variant. this is a requirement for using certain request types β as of 5.19 only theIORING_OP_URING_CMDpassthrough command for NVMe passthrough needs this. also seeIORING_SETUP_CQE_MIXEDfor an alternative to this flag. available since 5.19 πIORING_SETUP_SINGLE_ISSUERβ okay so this one's a little hint to the kernel that only one single task (or thread) is gonna submit requests β¨ and the kernel uses that to do some internal optimisations. Who counts as "the submission task"? Bestie, it's either the task that created the ring, or β ifIORING_SETUP_R_DISABLEDis specified β it's the task that enables the ring throughio_uring_register(2). And the kernel actually enforces this rule, failing requests with-EEXISTif you violate it π. Small caveat though: whenIORING_SETUP_SQPOLLis set, the polling task is considered to be doing all submissions on behalf of userspace, so it always complies with the rule, no matter how many userspace tasks doio_uring_enter(2)π. Available since 6.0.IORING_SETUP_DEFER_TASKRUNβ default behaviour is that io_uring processes all outstanding work at the end of any system call or thread interrupt, which can delay your app from making other progress π. Setting this flag hints to io_uring that it should defer work until anio_uring_enter(2)call with theIORING_ENTER_GETEVENTSflag set. The vibe? Your app gets to request work to run just before it wants to process completions π . This flag requires theIORING_SETUP_SINGLE_ISSUERflag to be set, and it also enforces that the call toio_uring_enter(2)is made from the same thread that submitted requests. Heads up fr fr: if this flag is set, it's the application's responsibility to periodically trigger work (for example via any of the CQE waiting functions) or else completions may not be delivered π. Available since 6.1.IORING_SETUP_NO_MMAPβ by default io_uring allocates kernel memory that callers must subsequentlymmap(2). If this flag is set, io_uring instead uses caller-allocated buffers;p->cq_off.user_addrmust point to the memory for the sq/cq rings, andp->sq_off.user_addrmust point to the memory for the sqes π«Ά. Each allocation must be contiguous memory. Typically, callers should allocate this memory by usingmmap(2)to allocate a huge page. And if this flag is set, a subsequent attempt tommap(2)the io_uring file descriptor will fail π. Available since 6.5.IORING_SETUP_REGISTERED_FD_ONLYβ with this flag set, io_uring registers the ring file descriptor and returns the registered descriptor index, without ever allocating an unregistered file descriptor β¨. The caller will need to useIORING_REGISTER_USE_REGISTERED_RINGwhen callingio_uring_register(2). This flag only makes sense when used alongsideIORING_SETUP_NO_MMAP, which also needs to be set π. Available since 6.5.IORING_SETUP_NO_SQARRAYβ if this flag is set, entries in the submission queue will be submitted in order, wrapping around to the first entry after reaching the end of the queue π. In other words, there's no more indirection via the array of submission entries, and the queue is indexed directly by the submission queue tail and the range of indexed represented by it modulo queue size. Subsequently, the user should not map the array of submission queue entries, and the corresponding offset instruct io_sqring_offsetswill be set to zero π«Ά. Available since 6.6.IORING_SETUP_CQE_MIXEDβ bestie this is the new hotness π₯. If this flag is set, the ring supports posting both the normal 16b sized CQEs as well as the larger 32b CQEs. Normally a ring is set up to only allow posting of normal 16b CQEs, or ifIORING_SETUP_CQE32has been set, only 32b CQEs. With this flag, the kernel may post both types π. If a 32b CQE is posted, the CQEflagsmember will haveIORING_CQE_F_32set to indicate that this CQE is of the larger type. The kernel may also setIORING_CQE_F_SKIPin the CQEflagsmember, in case it needs to post a filler 16b CQE π . This will happen if a 32b CQE needs posting and the current CQ ring offset is such that the 32b CQE would wrap around. Applications should simply skip/ignore such CQEs β they contain no information and serve only as placeholder to facilitate advancing the CQ ring such that a large CQE can get posted. The use case of theIORING_SETUP_CQE_MIXEDflag is to allow more efficient setups of workloads that may post both normal and large CQEs β¨. Opcodes that previously required the use ofIORING_SETUP_CQE32may now use this flag instead, and potentially save some memory for the CQ ring π. If used with liburing 2.12 or newer, the handling of variably sized CQEs is fully transparent. Available since 6.18.
So if you specify no flags at all, the io_uring instance is set up for interrupt driven I/O π. I/O may be submitted using io_uring_enter(2) and can be reaped by polling the completion queue.
The resv array must be initialized to zero. No cap, just zero it out π«Ά.
β¨ the features field, aka the kernel telling you what it can do β¨
features is filled in by the kernel, and it specifies the various features supported by the current kernel version π. Here's the whole menu, babes:
IORING_FEAT_SINGLE_MMAPβ if this flag is set, the two SQ and CQ rings can be mapped with a singlemmap(2)call π. The SQEs must still be allocated separately. This brings the necessarymmap(2)calls down from three to two. Available since kernel 5.4.IORING_FEAT_NODROPβ if this flag is set, io_uring supports almost never dropping completion events π . A dropped event can only occur if the kernel runs out of memory, in which case you have worse problems than a lost event β your app and others will likely get OOM killed anyway π. If a completion event occurs and the CQ ring is full, the kernel stores the event internally until such a time that the CQ ring has room for more entries. In earlier kernels, if this overflow condition is entered, attempting to submit more IO would fail with the-EBUSYerror value, if it can't flush the overflown events to the CQ ring. If that happens, the application must reap events from the CQ ring and attempt the submit again π. If the kernel has no free memory to store the event internally, it will be visible by an increase in the overflow value on the cqring. Available since kernel 5.5. Additionallyio_uring_enter(2)will return-EBADRthe next time it would otherwise sleep waiting for completions (since kernel 5.19) π.IORING_FEAT_SUBMIT_STABLEβ if this flag is set, applications can be certain that any data for async offload has been consumed when the kernel has consumed the SQE β . Available since kernel 5.5.IORING_FEAT_RW_CUR_POSβ if this flag is set, applications can specifyoffset == -1withIORING_OP_{READV,WRITEV},IORING_OP_{READ,WRITE}_FIXED, andIORING_OP_{READ,WRITE}to mean current file position, which behaves likepreadv2(2)andpwritev2(2)withoffset == -1π«Ά. It'll use (and update) the current file position. This obviously comes with the caveat that if the application has multiple reads or writes in flight, then the end result will not be as expected π β it's similar to threads sharing a file descriptor and doing IO using the current file position. Available since kernel 5.6.IORING_FEAT_CUR_PERSONALITYβ if this flag is set, io_uring guarantees that both sync and async execution of a request assumes the credentials of the task that calledio_uring_enter(2)to queue the requests π. If this flag isn't set, then requests are issued with the credentials of the task that originally registered the io_uring. If only one task is using a ring, then this flag doesn't matter, since the credentials will always be the same π. Note that this is the default behavior β tasks can still register different personalities throughio_uring_register(2)withIORING_REGISTER_PERSONALITYand specify the personality to use in the sqe. Available since kernel 5.6.IORING_FEAT_FAST_POLLβ if this flag is set, io_uring supports using an internal poll mechanism to drive data/space readiness β¨. This means requests that cannot read or write data to a file no longer need to be punted to an async thread for handling β instead they begin operation when the file is ready π¦. It's similar to doing poll + read/write in userspace, but it eliminates the need to do so. If this flag is set, requests waiting on space/data consume a lot less resources doing so, since they're not blocking a thread. Available since kernel 5.7.IORING_FEAT_POLL_32BITSβ if this flag is set, theIORING_OP_POLL_ADDcommand accepts the full 32-bit range of epoll based flags π₯. Most notablyEPOLLEXCLUSIVE, which allows exclusive (waking single waiters) behavior. Available since kernel 5.9.IORING_FEAT_SQPOLL_NONFIXEDβ if this flag is set, theIORING_SETUP_SQPOLLfeature no longer requires the use of fixed files π . Any normal file descriptor can be used for IO commands without needing registration. Available since kernel 5.11.IORING_FEAT_EXT_ARGβ if this flag is set, then theio_uring_enter(2)system call supports passing in an extended argument instead of just thesigset_tof earlier kernels π«Ά. This extended argument is of typestruct io_uring_getevents_argand allows the caller to pass in both asigset_tand a timeout argument for waiting on events. The struct layout is as follows:
struct io_uring_getevents_arg {
__u64 sigmask;
__u32 sigmask_sz;
__u32 pad;
__u64 ts;
};
and a pointer to this struct must be passed in if IORING_ENTER_EXT_ARG is set in the flags for the enter system call β¨. Available since kernel 5.11.
IORING_FEAT_NATIVE_WORKERSβ if this flag is set, io_uring is using native workers for its async helpers π. Previous kernels used kernel threads that assumed the identity of the original io_uring owning task, but later kernels will actively create what looks more like regular process threads instead π§. Available since kernel 5.12.IORING_FEAT_RSRC_TAGSβ if this flag is set, then io_uring supports a variety of features related to fixed files and buffers πΈ. In particular, it indicates that registered buffers can be updated in-place, whereas before the full set would have to be unregistered first π. Available since kernel 5.13.
okay bestie, last batch of feature flags β these are the little βwhat can my kernel actually doβ receipts, and you're gonna read every single one π β¨
IORING_FEAT_CQE_SKIPβ if this flag is set, then io_uring supports settingIOSQE_CQE_SKIP_SUCCESSin the SQE you submit, which means no CQE should be generated for that SQE if it executes normally. but bestie, if an error happens while processing the SQE, a CQE with the appropriate error value will still be generated. available since kernel 5.17 π«ΆIORING_FEAT_LINKED_FILEβ if this flag is set, then io_uring supports sane assignment of files for SQEs that have dependencies. example: if a chain of SQEs are submitted withIOSQE_IO_LINK, then kernels without this flag will prepare the file for each link upfront. if a previous link opens a file with a known index β eg if direct descriptors are used withopenoracceptβ then file assignment needs to happen post execution of that SQE. if this flag is set, then the kernel will defer file assignment until execution of a given request is started. available since kernel 5.17 πIORING_FEAT_REG_REG_RINGβ if this flag is set, then io_uring supports callingio_uring_register(2)using a registered ring fd, viaIORING_REGISTER_USE_REGISTERED_RING. available since kernel 6.3 πIORING_FEAT_MIN_TIMEOUTβ if this flag is set, then io_uring supports passing in a minimum batch wait timeout. seeio_uring_submit_and_wait_min_timeout(3)for more details β°IORING_FEAT_RECVSEND_BUNDLEβ if this flag is set, then io_uring supports bundled send and recv operations. seeio_uring_prep_send_bundle(3)for more information. also implies support for provided buffers in send operations π
okay so the rest of the fields in struct io_uring_params are filled in by the kernel, and they give you the info you need to memory map the submission queue, the completion queue, and the array of submission queue entries. sq_entries specifies the number of submission queue entries allocated. sq_off describes the offsets of various ring buffer fields:
struct io_sqring_offsets {
__u32 head;
__u32 tail;
__u32 ring_mask;
__u32 ring_entries;
__u32 flags;
__u32 dropped;
__u32 array;
__u32 resv1;
__u64 user_addr;
};
taken together, sq_entries and sq_off provide all of the information necessary for accessing the submission queue ring buffer and the submission queue entry array. the submission queue can be mapped with a call like:
ptr = mmap(0, sq_off.array + sq_entries * sizeof(__u32),
PROT_READ|PROT_WRITE, MAP_SHARED|MAP_POPULATE,
ring_fd, IORING_OFF_SQ_RING);
where sq_off is the io_sqring_offsets structure, and ring_fd is the file descriptor returned from io_uring_setup(2). the addition of sq_off.array to the length of the region accounts for the fact that the ring is located at the end of the data structure. as an example, the ring buffer head pointer can be accessed by adding sq_off.head to the address returned from mmap(2):
head = ptr + sq_off.head;
the flags field is used by the kernel to communicate state information to the application. currently, it is used to inform the application when a call to io_uring_enter(2) is necessary. see the documentation for the IORING_SETUP_SQPOLL flag above. the dropped member is incremented for each invalid submission queue entry encountered in the ring buffer π
the head and tail track the ring buffer state. the tail is incremented by the application when submitting new I/O, and the head is incremented by the kernel when the I/O has been successfully submitted. determining the index of the head or tail into the ring is accomplished by applying a mask:
index = tail & ring_mask;
the array of submission queue entries is mapped with:
sqentries = mmap(0, sq_entries * sizeof(struct io_uring_sqe),
PROT_READ|PROT_WRITE, MAP_SHARED|MAP_POPULATE,
ring_fd, IORING_OFF_SQES);
the completion queue is described by cq_entries and cq_off shown here:
struct io_cqring_offsets {
__u32 head;
__u32 tail;
__u32 ring_mask;
__u32 ring_entries;
__u32 overflow;
__u32 cqes;
__u32 flags;
__u32 resv1;
__u64 user_addr;
};
the completion queue is simpler, since the entries are not separated from the queue itself, and can be mapped with:
ptr = mmap(0, cq_off.cqes + cq_entries * sizeof(struct io_uring_cqe),
PROT_READ|PROT_WRITE, MAP_SHARED|MAP_POPULATE, ring_fd,
IORING_OFF_CQ_RING);
closing the file descriptor returned by io_uring_setup(2) will free all resources associated with the io_uring context. but note: this may happen asynchronously within the kernel, so it is not guaranteed that resources are freed immediately π
π RETURN VALUE π
io_uring_setup(2) returns a new file descriptor on success. the application may then provide the file descriptor in a subsequent mmap(2) call to map the submission and completion queues, or to the io_uring_register(2) or io_uring_enter(2) system calls β¨
on error, a negative error code is returned. the caller should not rely on the errno variable π¨
πΈ ERRORS πΈ
EFAULTβparamsis outside your accessible address space.EINVALβ theresvarray contains non-zero data,p.flagscontains an unsupported flag,entriesis out of bounds,IORING_SETUP_SQ_AFFwas specified butIORING_SETUP_SQPOLLwas not, orIORING_SETUP_CQSIZEwas specified butio_uring_params.cq_entrieswas invalid. also:IORING_SETUP_REGISTERED_FD_ONLYwas specified, butIORING_SETUP_NO_MMAPwas not.EMFILEβ the per-process limit on the number of open file descriptors has been reached (see the description ofRLIMIT_NOFILEingetrlimit(2)).ENFILEβ the system-wide limit on the total number of open files has been reached.ENOMEMβ insufficient kernel resources are available.EPERMβIORING_SETUP_SQPOLLwas specified, but the effective user ID of the caller did not have sufficient privileges.EPERMβ/proc/sys/kernel/io_uring_disabledhas the value2, or it has the value1and the calling process does not hold theCAP_SYS_ADMINcapability or is not a member of/proc/sys/kernel/io_uring_group.ENXIOβIORING_SETUP_ATTACH_WQwas set, butparams.wq_fddid not refer to an io_uring instance or refers to an instance that is in the process of shutting down π
π¦ SEE ALSO π¦
io_uring_register(2), io_uring_enter(2)
and that's the whole page, bestie β mwah π source/version is Linux, date 2019-01-29, for io_uring_setup(2). slay β¨π
π¦ where this page comes from
- fedora 44: liburing-devel 2.13-2.fc44 Β· (GPL-2.0-only WITH Linux-syscall-note OR MIT) AND (LGPL-2.0-or-later OR MIT)
- fedora 44: liburing-devel 2.13-2.fc44 Β· (GPL-2.0-only WITH Linux-syscall-note OR MIT) AND (LGPL-2.0-or-later OR MIT)
- π the page's license: LGPL-2.0-or-later
- Copyright (C) 2019 Jens Axboe <axboe@kernel.dk>
- Copyright (C) 2019 Jon Corbet <corbet@lwn.net>
- Copyright (C) 2019 Red Hat, Inc
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms