io_uring — Chronology by Kernel Version
A version-by-version chronology of the Linux io_uring interface, from its v5.1 introduction through the v7.3-rc4 development tree, recording exactly when each operation (IORING_OP_*), setup flag (IORING_SETUP_*), registration opcode (IORING_REGISTER_*), and feature flag (IORING_FEAT_*) first appeared. From v7.1 onward it also records contract changes that reused an existing opcode — the IORING_OP_* enum itself did not grow.
Scope and ground truth. This document is a chronology, not a tutorial — for what these primitives do, see io_uring features; for the opcode catalog, see opcodes reference. Version markers here are cross-checked against four sources: the kernel UAPI header enum order (which is roughly chronological), the liburing man pages' "Available since" notes, the kernel git history (
git tag --containson the commit that adds each enum value), and external authorities (LWN, man7.org, kernel.dk). Where a marker is uncertain or where the liburing man page disagrees with the kernel git history, the discrepancy is called out inline rather than glossed over.
About the checkout. The figures here are taken from a Linux tree at v7.3-rc4 (
93f51579e7df248780214094418f205253383cc5, 2026-09-20;VERSION=7 PATCHLEVEL=3 SUBLEVEL=0 EXTRAVERSION=-rc4inlinux/Makefile, "Baby Opossum Posse"). An earlier pass of this chronology stopped at v7.1-rc6 (e43ffb69e043, 2026-05-31) and cited a pre-tag liburing tree (40999f52, 2026-05-18, version header already saying 2.15). The liburing companion is now78dce99b(2026-09-11): theliburing-2.15tag (d41bf922, 2026-06-27) plus 35 commits.liburing.specstill saysVersion: 2.15. See the liburing section. The kernel tags run… v6.18 → v6.19 → v7.0 → v7.1 → v7.2 → v7.3-rc4; the jump to the7.xseries happened after6.19, not by skipping6.13+. Markers at6.13and later are forward-dated relative to general public knowledge (those tags carry 2025–2026 commit dates in this tree) and should be treated as "as observed in this checkout" rather than long-settled history. Everything through~6.12is independently corroborated by stable external sources.include/uapi/linux/io_uring.his identical from v7.1-rc6 through v7.3-rc4. The UAPI that did move lives ininclude/uapi/linux/io_uring/{zcrx,query,bpf_filter}.h.
How to read the "since" markers
The kernel exposes a feature the moment its commit lands in a merge window; the first stable release that contains it is what "since vX.Y" means throughout this document. Three caveats:
| Subtlety | Explanation |
|---|---|
| Enum order ≈ chronology | New IORING_OP_* values are appended to enum io_uring_op, so reading io_uring.h top-to-bottom roughly recovers the introduction order. It is not perfectly chronological — reserved slots and re-purposed values exist. |
| liburing ≠ kernel | A kernel feature is usable via raw io_uring_setup(2)/io_uring_enter(2) before liburing ships a io_uring_prep_* helper. The "liburing helper" column below is the ergonomic wrapper, which may trail the kernel by a release. |
| Runtime probing is authoritative | The only correct way to know if a running kernel supports an opcode is IORING_REGISTER_PROBE (since 5.6) / io_uring_get_probe(3), or IORING_REGISTER_QUERY (kernel git: since 6.18 — note liburing 2.15's io_uring_register.2 marks it "since 6.15"; see the 6.15 discrepancy note below). Compile-time version checks are a heuristic. See io_uring features. |
5.1 — The introduction (May 2019)
io_uring debuts, authored by Jens Axboe, replacing the moribund Linux AIO (libaio) interface. The model is two shared, memory-mapped ring buffers — a Submission Queue (SQ) and a Completion Queue (CQ) — plus three syscalls (io_uring_setup, io_uring_enter, io_uring_register). The canonical design write-up is Axboe's "Efficient IO with io_uring"; LWN's "The rapid growth of io_uring" tracks the early opcode additions.
Operations (enum io_uring_op, first six slots):
IORING_OP_NOPIORING_OP_READV/IORING_OP_WRITEVIORING_OP_FSYNCIORING_OP_READ_FIXED/IORING_OP_WRITE_FIXED(against pre-registered buffers)IORING_OP_POLL_ADD/IORING_OP_POLL_REMOVE
Registration (io_uring_register(2)): IORING_REGISTER_BUFFERS / IORING_UNREGISTER_BUFFERS, IORING_REGISTER_FILES / IORING_UNREGISTER_FILES, and IORING_REGISTER_FILES_UPDATE — all noted "Available since 5.1" in liburing/man/io_uring_register.2.
Setup: the base flags IORING_SETUP_IOPOLL (busy-poll completions for O_DIRECT block I/O) and IORING_SETUP_SQPOLL / IORING_SETUP_SQ_AFF (kernel-side submission polling thread) ship in the initial interface.
This is the foundation every surveyed library targets as its baseline (read/write/fsync + poll). See Tokio's and Glommio's backends in the matrix below.
Worked examples.
nop.dwalks the minimal setup → submit → wait → CQE cycle;read-write-fixed.dround-trips a payload through a registered (fixed) buffer;poll-add.darms a one-shotPOLL_ADD. All three are runnableduringdemos.
Worked examples.
registered-files.dreads a file by fixed-file table index withIOSQE_FIXED_FILE;sqpoll.dsubmits aNOPwith noio_uring_entersyscall via theIORING_SETUP_SQPOLLkernel thread.
5.2 — Sync file range (July 2019)
IORING_OP_SYNC_FILE_RANGE— asyncsync_file_range(2). (io_uring_enter.2: "Available since 5.2".)IORING_UNREGISTER_EVENTFD(the registration counterpart for eventfd notification; "Available since 5.2").IORING_FEAT_*flags begin: the very firstIORING_FEAT_SINGLE_MMAPactually lands in 5.4 (below) — 5.2 predates the feature-flag system.
5.3 — Network message ops (September 2019)
IORING_OP_SENDMSG/IORING_OP_RECVMSG— asyncsendmsg(2)/recvmsg(2). (io_uring_enter.2: "Available since 5.3".)- Linked SQEs (
IOSQE_IO_LINK): the ability to chain dependent SQEs so the next starts only after the previous succeeds; the man page records this submission-side feature "Available since 5.3".
Worked example.
linked-sqes.dchains a write to anfsyncon the same fd withIOSQE_IO_LINK, showing the kernel-guaranteed write-before-fsyncordering and the failure-propagation (-ECANCELED) semantics of linked SQEs.
Worked example.
sendmsg-recvmsg.dpasses a file descriptor over a unix socket withSCM_RIGHTSviaSENDMSG/RECVMSG.
5.4 — Timeouts and single mmap (November 2019)
IORING_OP_TIMEOUT— a timeout that completes after N completions or a wall-clock interval. (io_uring_enter.2: "Available since 5.4".)IORING_FEAT_SINGLE_MMAP— SQ and CQ rings can share onemmap, cutting setup from threemmapcalls to two. (io_uring_setup.2: "Available since kernel 5.4".)- A historical note in several
io_uring_prep_*man pages: "Very early kernels (5.4 and earlier) required state to be stable" before submission — an early-era constraint relaxed byIORING_FEAT_SUBMIT_STABLE(5.5).
Worked example.
timeout-link-timeout.dfires a standalone relativeTIMEOUT(completing with-ETIME) and then uses aLINK_TIMEOUT(5.5) to cancel a never-ready poll.
5.5 — Accept/connect, cancel, link-timeout (January 2020)
A pivotal release for networking and control flow:
IORING_OP_TIMEOUT_REMOVEIORING_OP_ACCEPT— asyncaccept4(2).IORING_OP_ASYNC_CANCEL— cancel a previously submitted request byuser_data.IORING_OP_LINK_TIMEOUT— a timeout attached to the linked request, cancelling it if it doesn't finish in time.IORING_OP_CONNECT— asyncconnect(2).
All five are marked "Available since 5.5" in io_uring_enter.2, matching the LWN growth article inventory.
Features: IORING_FEAT_NODROP (the kernel stops silently dropping CQEs on overflow; "since kernel 5.5"), IORING_FEAT_SUBMIT_STABLE (SQE data may be mutated immediately after submission; "since kernel 5.5"). (The IORING_REGISTER_FILES_SKIP skip-semantics for IORING_REGISTER_FILES_UPDATE arrive later — io_uring_register.2 marks them "since 5.12", and the define is first present in the v5.12 kernel; see 5.12 below.)
Worked examples.
tcp-echo.ddrives a loopbackACCEPT+CONNECT+SEND+RECVround-trip through a single ring;async-cancel.dcancels an in-flight poll by itsuser_dataand observes the-ECANCELEDcompletion.
5.6 — The filesystem/syscall expansion (March 2020)
The largest single-release opcode batch — io_uring stops being "just block I/O" and becomes a general async-syscall surface:
IORING_OP_FALLOCATEIORING_OP_OPENAT/IORING_OP_OPENAT2/IORING_OP_CLOSEIORING_OP_FILES_UPDATE(the in-SQE form of file-table update)IORING_OP_STATXIORING_OP_READ/IORING_OP_WRITE(the simple, non-vectored variants)IORING_OP_FADVISE/IORING_OP_MADVISEIORING_OP_SEND/IORING_OP_RECV(the buffer-pointer, non-msghdrforms)IORING_OP_EPOLL_CTL— asyncepoll_ctl(2).
All marked "Available since 5.6" in io_uring_enter.2; the LWN growth article enumerates the same set.
Features: IORING_FEAT_CUR_PERSONALITY ("since kernel 5.6"). Registration: IORING_REGISTER_PROBE (runtime opcode capability probing — the portable feature-detection mechanism, "since 5.6"), IORING_REGISTER_PERSONALITY / IORING_UNREGISTER_PERSONALITY, IORING_REGISTER_EVENTFD_ASYNC (all "since 5.6").
Worked examples.
openat-statx-close.dchainsOPENAT→STATX→READ→CLOSEasynchronously, feeding each op's result fd into the next;probe.dusesIORING_REGISTER_PROBEto print which opcodes the running kernel supports.
5.7 — Splice, provided buffers, fast poll (May 2020)
IORING_OP_SPLICE— asyncsplice(2). (io_uring_enter.2: "Available since 5.7".)IORING_OP_PROVIDE_BUFFERS/IORING_OP_REMOVE_BUFFERS— the application hands the kernel a pool of buffers; recv-style ops pick one viaIOSQE_BUFFER_SELECT. (Both "since 5.7".) This is the precursor to the far more efficient ring-provided buffers of 5.19.IORING_FEAT_FAST_POLL— internally arms a poll and retries, so an op likeRECVMSGon a socket no longer needs an async worker thread to block. (io_uring_setup.2: "Available since 5.7"; corroborated by man7.) A major latency/scaling win that effectively makesio_uringa competitive readiness reactor as well as a completion proactor.
Worked example.
splice-tee.ddoes a zero-copySPLICEbetween pipes and aTEEduplication.
5.8 — Tee (August 2020)
IORING_OP_TEE— asynctee(2). (io_uring_enter.2: "Available since 5.8".)IORING_REGISTER_EVENTFDordering semantics refined ("since 5.8").
5.9 — 32-bit poll events (October 2020)
IORING_FEAT_POLL_32BITS— poll requests accept the full 32-bitepollevent mask includingEPOLLEXCLUSIVE. (io_uring_setup.2: "Available since kernel 5.9"; kernel git: theIORING_FEAT_POLL_32BITSdefine is not present inv5.8and first appears inv5.9via commit5769a351b89c, "io_uring: change the poll type to be 32-bits".)
5.10 — Restrictions and deferred setup (December 2020)
IORING_REGISTER_ENABLE_RINGS+IORING_SETUP_R_DISABLED— create a ring in a disabled state, apply restrictions, then enable it. (io_uring_setup.2: "Available since 5.10".)IORING_REGISTER_RESTRICTIONS— sandbox a ring by whitelisting which opcodes/registrations/SQE flags are permitted. (io_uring_register.2: "Available since 5.10".)IORING_FEAT_EXT_ARGgroundwork (finalized 5.11).
Worked example.
restrictions.dsandboxes a ring to a whitelist of opcodes viaIORING_REGISTER_RESTRICTIONS+IORING_SETUP_R_DISABLED.
5.11 — Filesystem mutation ops, SQPOLL without root (February 2021)
IORING_OP_SHUTDOWN— asyncshutdown(2).IORING_OP_RENAMEAT— asyncrenameat2(2).IORING_OP_UNLINKAT— asyncunlinkat(2).
All "Available since 5.11" (io_uring_enter.2).
Features: IORING_FEAT_EXT_ARG — io_uring_enter(2) accepts an extended argument struct (a timespec timeout for GETEVENTS), "since kernel 5.11". SQPOLL policy change: the man pages note that before 5.11 SQPOLL required registered files and root; in 5.11 registration is no longer required and SQPOLL is usable as non-root with CAP_SYS_NICE (further relaxed in 5.13). Also IORING_OP_ASYNC_CANCEL gains IORING_ASYNC_CANCEL_ALL-style behavior ("since 5.11" in the cancel discussion).
Worked example.
fs-mutations.dperforms asyncMKDIRAT/SYMLINKAT/LINKAT/RENAMEAT/UNLINKATdirectory mutations.
5.12 — Files-update by index, native workers (April 2021)
IORING_REGISTER_FILES_UPDATEcan place an fd at a specific index in the registered file table; descriptors set toIORING_REGISTER_FILES_SKIPare left untouched at that index. (io_uring_register.2: both "Available since 5.12"; theIORING_REGISTER_FILES_SKIPdefine is first present in thev5.12UAPI header.)IORING_FEAT_NATIVE_WORKERS—io_uringswitches from kernel threads that assumed the owning task's identity to native worker threads. (io_uring_setup.2: "Available since kernel 5.12".)IORING_OP_RENAMEAT/UNLINKATsee refinements.
5.13 — Resource tags, poll update (June 2021)
IORING_REGISTER_BUFFERS2/IORING_REGISTER_BUFFERS_UPDATEandIORING_REGISTER_FILES2/IORING_REGISTER_FILES_UPDATE2— the tagged-resource registration family. (All "Available since 5.13" inio_uring_register.2.)IORING_FEAT_RSRC_TAGS— registered buffers/files carry tags and can be updated without unregistering first; registration no longer waits for the ring to idle. (io_uring_setup.2: "since kernel 5.13".)IORING_POLL_UPDATE_EVENTS/IORING_POLL_UPDATE_USER_DATA—IORING_OP_POLL_ADDcan update an existing poll. (io_uring_enter.2: poll update "available since 5.13".)- SQPOLL fully usable without special privileges in newer kernels ("In 5.13 this requirement was also relaxed").
5.14 — io-wq affinity controls (August 2021)
IORING_REGISTER_IOWQ_AFF/IORING_UNREGISTER_IOWQ_AFF— pin the async (io-wq) worker pool to a CPU mask. (io_uring_register.2: "Available since 5.14".)
5.15 — More filesystem ops, worker caps (October 2021)
IORING_OP_MKDIRAT— asyncmkdirat(2).IORING_OP_SYMLINKAT— asyncsymlinkat(2).IORING_OP_LINKAT— asynclinkat(2).
All "Available since 5.15" (io_uring_enter.2). io_uring direct descriptors are also introduced in 5.15 (io_uring_enter.2: "the 5.15 kernel, where direct descriptors were introduced"). Note: the dedicated auto-allocation sentinel IORING_FILE_INDEX_ALLOC is a later addition — the define is first present in the v5.19 UAPI header, not 5.15.
Registration: IORING_REGISTER_IOWQ_MAX_WORKERS — cap the number of bounded/unbounded async workers. (io_uring_register.2: "Available since 5.15".)
Worked example.
direct-descriptors.dopens into a fixed-file slot, uses it by index, thenFIXED_FD_INSTALLs it back to a real fd.
5.17 — CQE skip, faster cancel (March 2022)
IORING_FEAT_CQE_SKIP—IOSQE_CQE_SKIP_SUCCESSlets a successful SQE produce no CQE (errors still post one). (io_uring_setup.2: "Available since 5.17"; git: enum value lands in the 5.17 merge window.) A throughput win for fire-and-forget links.
Worked example.
cqe-skip.dsuppresses a successful op's CQE in a link withIOSQE_CQE_SKIP_SUCCESS.
5.18 — Ring-fd registration, msg-ring, linked-file (May 2022)
IORING_OP_MSG_RING— send a message (twou64s) from one ring to another, enabling ring-to-ring wakeups. (io_uring_enter.2: "Available since 5.18". Git: the enum valueIORING_OP_MSG_RING,is added by commit "io_uring: add support forIORING_OP_MSG_RINGcommand", first contained in tagv5.18.)IORING_SETUP_SUBMIT_ALL— submit all SQEs even if one errors. (io_uring_setup.2: "since 5.18".)IORING_REGISTER_RING_FDS/IORING_UNREGISTER_RING_FDS+IORING_REGISTER_USE_REGISTERED_RING— register the ring fd itself soio_uring_enter(2)need not pass a real fd. (io_uring_register.2: "Available since 5.18".)IORING_FEAT_LINKED_FILE— defer file assignment in a link chain until the request actually runs. (io_uring_setup.2: "since" — git: enum value first inv5.18.)
Worked examples.
msg-ring.dposts au64from one ring into another — the cross-core wakeup primitive;registered-ring-fd.dregisters the ring fd soio_uring_enterskips per-callfdget/fdput.
5.19 — Buffer rings, zero-copy groundwork, big SQE/CQE (July 2022)
A second pivotal release (alongside 5.5 and 5.6) — it lands the modern high-throughput primitives:
IORING_OP_SOCKET— asyncsocket(2). (io_uring_enter.2: "Available since 5.19". Git confirms enum value first inv5.19.)IORING_OP_URING_CMD— the passthrough/command channel (used by NVMe passthrough,ublk, etc.). (io_uring_enter.2: "Available since 5.19".)- The extended-attribute ops
IORING_OP_FSETXATTR/IORING_OP_SETXATTR/IORING_OP_FGETXATTR/IORING_OP_GETXATTR("Available since 5.19"). - Multishot accept (
IORING_ACCEPT_MULTISHOTonIORING_OP_ACCEPT) — one SQE yields a CQE per incoming connection whileIORING_CQE_F_MOREstays set. (man7; "multishot variants are available since 5.19".)
Registration: IORING_REGISTER_PBUF_RING / IORING_UNREGISTER_PBUF_RING — ring-provided buffers, the efficient successor to IORING_OP_PROVIDE_BUFFERS: the kernel consumes buffers from a mapped ring with no per-buffer SQE. (io_uring_register.2: "Available since 5.19".)
Setup: IORING_SETUP_COOP_TASKRUN (don't IPI the target task for completion task-work; "Available since 5.19"), IORING_SETUP_TASKRUN_FLAG (surface IORING_SQ_TASKRUN so userspace knows to reap; "since 5.19"), IORING_SETUP_SQE128 (128-byte SQEs, required by some URING_CMD users like NVMe passthrough; "since 5.19"), IORING_SETUP_CQE32 (32-byte CQEs; "since 5.19").
Worked examples.
multishot-accept.dserves multiple loopback connections from a single armed accept SQE (each CQE carriesCQEFlags.MORE);provided-buf-ring.dregisters anio_uring_bufring and issues a buffer-selectingRECV.
Worked example.
uring-cmd-socket.ddoes socketgetsockopt/setsockoptthrough theURING_CMDpassthrough channel.
6.0 — Zero-copy send, single-issuer, sync cancel (October 2022)
IORING_OP_SEND_ZC— zero-copysend; the data is pinned and a second "notification" CQE (IORING_CQE_F_NOTIF) fires when the kernel is done with the buffer. (io_uring_enter.2: "Available since 6.0". Git: enum value first inv6.0.)- Multishot recv (
IORING_RECV_MULTISHOT) becomes available — confirmed "available since kernel 6.0" by theio_uring_prep_recv_multishot(3)man page.
Setup: IORING_SETUP_SINGLE_ISSUER — promise that one task submits, enabling lock elision. (io_uring_setup.2: "Available since 6.0".)
Registration: IORING_REGISTER_SYNC_CANCEL (synchronous cancel from userspace; "Available since 6.0"), IORING_REGISTER_FILE_ALLOC_RANGE (reserve a sub-range of the direct-descriptor table; "Available since 6.0").
Worked example.
send-zc.dperforms a zero-copySEND_ZCover loopback and asserts the two-CQE pattern: a transfer-result CQE (CQEFlags.MORE) followed by a separate notification CQE (CQEFlags.NOTIF).
Worked examples.
multishot-recv.darms oneRECVposting a CQE per segment into provided-ring buffers;sync-cancel.dcancels an in-flight poll synchronously withIORING_REGISTER_SYNC_CANCEL.
6.1 — Zero-copy sendmsg, deferred task-run (December 2022)
IORING_OP_SENDMSG_ZC— themsghdrform of zero-copy send. (io_uring_enter.2: "Available since 6.1". Git: enum value first inv6.1.)IORING_SETUP_DEFER_TASKRUN— defer completion task-work until the app callsio_uring_enter(2)withIORING_ENTER_GETEVENTS; requiresSINGLE_ISSUER. (io_uring_setup.2: "Available since 6.1".) Together withCOOP_TASKRUNthis is the latency/syscall-batching configuration most high-performance libraries adopt (see Glommio/monoio in the matrix).
Worked example.
defer-taskrun.dsets up a ring withSINGLE_ISSUER | DEFER_TASKRUN | COOP_TASKRUNand reaps aNOPand a relativeTIMEOUTthrough theGETEVENTS-driven deferred task-run path.
6.3 — Registered-ring registration (April 2023)
IORING_FEAT_REG_REG_RING—io_uring_register(2)itself may be called via a registered ring fd. (io_uring_setup.2: "Available since kernel 6.3"; git: enum value first inv6.3.)
6.4 — Multishot timeout (June 2023)
IORING_TIMEOUT_MULTISHOT(atimeout_flagsbit onIORING_OP_TIMEOUT) — a repeating timer that posts a CQE per interval. (Git: theIORING_TIMEOUT_MULTISHOTdefine is first contained in tagv6.4.)
Worked example.
multishot-timeout.darms oneMULTISHOTtimeout, collects three recurring-ETIMEticks (each withCQEFlags.MORE), then stops the repeats with anASYNC_CANCEL.
6.5 — No-mmap setup (August 2023)
IORING_SETUP_NO_MMAP— userspace allocates the SQ/CQ memory itself and passes pointers, avoiding the kernelmmap. (io_uring_setup.2: "Available since 6.5".)IORING_SETUP_REGISTERED_FD_ONLY— used withNO_MMAP; the ring is only addressable via its registered index, never a real fd. (io_uring_setup.2: "Available since 6.5".)
Discrepancy flagged.
liburing/man/io_uring_enter.2marksIORING_OP_WAITIDas "Available since 6.5". This is incorrect for the kernel: the enum valueIORING_OP_WAITIDis added by commitf31ecf671ddc(Sept 2023), whose first containing release tag isv6.7, and LWN's waitid coverage and the upstream patch series both target 6.7. This document therefore listsIORING_OP_WAITIDunder 6.7, not 6.5.
6.6 — No SQ array (October 2023)
IORING_SETUP_NO_SQARRAY— drop the indirection array between the SQ ring head and the SQE array; SQEs are consumed in order. (io_uring_setup.2: "Available since 6.6".)
6.7 — Futex, waitid, read-multishot (January 2024)
IORING_OP_WAITID— asyncwaitid(2); a parent gets a CQE on child state change instead of blocking. (Git: enum first inv6.7; LWN.) See the 6.5 discrepancy note above.IORING_OP_FUTEX_WAIT/IORING_OP_FUTEX_WAKE/IORING_OP_FUTEX_WAITV— asyncfutex(2)operations;FUTEX_WAITmirrorsFUTEX_WAIT_BITSET. (io_uring_enter.2: "Available since 6.7"; LWN futex coverage.)IORING_OP_READ_MULTISHOT— repeatedly read from a pollable fd into ring-provided buffers, one CQE per chunk. (io_uring_enter.2: "Available since 6.7"; git: enum first inv6.7.)
Worked example.
futex.dparks a ring on a 32-bit private futex withIORING_OP_FUTEX_WAITand is woken by a helper thread issuing a legacyfutex(2)wake on the same word.
Worked examples.
waitid.dasynchronously reaps a child process withIORING_OP_WAITID;read-multishot.ddoes repeated reads from a pipe into provided-ring buffers withREAD_MULTISHOT;futex-waitv.dwaits on a vector of futexes withFUTEX_WAITV.
6.8 — Fixed-fd install, pbuf status (March 2024)
IORING_OP_FIXED_FD_INSTALL— convert a registered (direct) descriptor back into a normal process fd. (io_uring_enter.2: "Available since 6.8"; git: enum first inv6.8.)IORING_REGISTER_PBUF_STATUS— query how many buffers remain in a provided-buffer ring. (io_uring_register.2: "Available since 6.8".)
6.9 — NAPI busy-poll, ftruncate (May 2024)
IORING_OP_FTRUNCATE— asyncftruncate(2). (io_uring_enter.2: "Available since 6.9"; git: enum first inv6.9.)IORING_REGISTER_NAPI/IORING_UNREGISTER_NAPI— configure NAPI busy-polling for network completions, trading CPU for latency. (io_uring_register.2: "Available since 6.9".)
Worked example.
napi.dconfigures NAPI busy-poll withIORING_REGISTER_NAPI.
6.10 — Send/recv bundles (July 2024)
IORING_FEAT_RECVSEND_BUNDLE— bundle multiple buffers into a single send/recv using provided-buffer rings (io_uring_prep_send_bundle(3)etc.). (Git: theIORING_FEAT_RECVSEND_BUNDLEdefine is first contained in tagv6.10.)
6.11 — Async bind/listen (September 2024)
IORING_OP_BIND— asyncbind(2). (Git: enum first inv6.11;io_uring_enter.2: "Available since 6.11".)IORING_OP_LISTEN— asynclisten(2). (Git: enum first inv6.11; "Available since 6.11".)
Worked example.
socket-bind-listen.ddrives a full asyncSOCKET+BIND+LISTENlifecycle through the ring.
6.12 — Clock source, buffer cloning, min-timeout (November 2024)
IORING_REGISTER_CLOCK— select the clock source used for completion waiting (e.g.CLOCK_MONOTONICvsCLOCK_BOOTTIME). (io_uring_register.2: "Available since 6.12"; git: first inv6.12.)IORING_REGISTER_CLONE_BUFFERS(withIORING_REGISTER_DST_REPLACE) — clone a registered buffer table from one ring into another. (io_uring_register.2; git: first inv6.12. The man page notes 6.12 added basic cloning and full-range cloning, with offset support arriving in 6.13.)IORING_FEAT_MIN_TIMEOUT—io_uring_submit_and_wait_min_timeout(3): wait for a batch with a minimum timeout that extends without extra context switches. (Git:IORING_FEAT_MIN_TIMEOUTfirst inv6.12.)IORING_REGISTER_SEND_MSG_RING— issue aMSG_RINGsynchronously fromio_uring_register(2). (io_uring_register.2: "Available since kernel 6.13" — note this one is 6.13, see below.)
Forward-dated boundary. Everything from here on (
6.13+) carries 2025–2026 commit dates in this checkout's git tree. Markers through v7.0 are read from liburing man pages (tagliburing-2.15, plus78dce99bwhere a page moved) andgit tag --contains/git merge-base --is-ancestorin the v7.3-rc4 tree. The 7.2 and 7.3 entries below are taken from that kernel tree: the liburing tag predates them, and78dce99bstill has not copied their UAPI.IORING_TIMEOUT_IMMEDIATE_ARGis the exception —man/io_uring_prep_timeout.3in the 2.15 tag already says "Available since the 7.1 kernel." Treat the whole span as "as observed here."
Worked example.
clock-min-timeout.dselects the wait clock withIORING_REGISTER_CLOCKand uses a min-timeout batched wait.
6.13 — Ring resize, mem regions, hybrid iopoll (≈ January 2025, per tree)
IORING_REGISTER_RESIZE_RINGS— grow/shrink the SQ/CQ rings of a live ring without tearing it down. (io_uring_register.2: "Available since kernel 6.13"; git: first inv6.13.)IORING_REGISTER_MEM_REGION— register a memory region the kernel can map for ring/aux data. (io_uring_register.2: "Available since kernel 6.13".)IORING_REGISTER_SEND_MSG_RING("Available since kernel 6.13").IORING_SETUP_HYBRID_IOPOLL— a hybrid of interrupt-driven and busy-poll completions, used withIORING_SETUP_IOPOLL. (Git:IORING_SETUP_HYBRID_IOPOLLfirst inv6.13.)IORING_REGISTER_CLONE_BUFFERSgains offset/destination-range support ("6.13 added support for specifying the offsets…").
Worked example.
resize-rings.dgrows a live ring's SQ/CQ withIORING_REGISTER_RESIZE_RINGS.
6.15 — Zero-copy receive, epoll-wait, vectored fixed, query (≈ May 2025, per tree)
A large networking/throughput release in this tree:
IORING_OP_RECV_ZC— zero-copy receive via a pre-registered zero-copy RX interface queue (IORING_REGISTER_ZCRX_IFQ); data arrives in auxiliary CQEs. (io_uring_enter.2: "Available since 6.15"; git: enum first inv6.15.)IORING_OP_EPOLL_WAIT— asyncepoll_wait(2), letting legacy epoll loops fold intoio_uring. (io_uring_enter.2: "Available since 6.15"; git: enum first inv6.15.)IORING_OP_READV_FIXED/IORING_OP_WRITEV_FIXED— vectored I/O whose iovec entries point into a registered buffer, combiningREADV/WRITEVwith the fixed-buffer fast path. (io_uring_enter.2: "Available since 6.15"; git: enum first inv6.15.)IORING_REGISTER_ZCRX_IFQ— register the zero-copy RX interface queue thatRECV_ZCconsumes. (io_uring_register.2: "Available since kernel 6.15"; git: enum value first inv6.15.) A laterIORING_REGISTER_ZCRX_CTRLcontrol op is documented alongside it.IORING_REGISTER_QUERY— a structured capability-query registration op (a successor/companion toIORING_REGISTER_PROBE). Discrepancy flagged:liburing 2.15'sio_uring_register.2marks it "Available since kernel 6.15", but in this tree the enum valueIORING_REGISTER_QUERYis not present inv6.15; it is added by commitc265ae75f900("io_uring: introduceio_uringquerying", Sept 2025), whose first containing tag isv6.18. Treat the kernel availability as 6.18, not 6.15.
Worked examples.
epoll-wait.dfolds a legacy epoll set into the ring withIORING_OP_EPOLL_WAIT;recv-zc.ddoes zero-copy receive via a registered ZCRX interface queue (SKIPs without a capable NIC).
6.16 — Async pipe (≈ July 2025, per tree)
IORING_OP_PIPE— asyncpipe2(2); can create normal or direct (fixed) descriptor pairs, writing the read/write ends into a two-element array. (io_uring_enter.2: "Available since 6.16"; git: enum first inv6.16.)
Worked example.
pipe.dcreates a pipe pair through the ring withIORING_OP_PIPE, then round-trips a byte through the new read/write ends.
6.18 — Mixed-size CQE (≈ November 2025, per tree)
IORING_SETUP_CQE_MIXED— a ring may carry a mix of 16-byte and 32-byte CQEs, transparently, instead of committing toCQE32for the whole ring. (io_uring_setup.2: "Available since 6.18"; git:IORING_SETUP_CQE_MIXEDfirst inv6.18.)
Worked example.
cqe-mixed.dreaps mixed 16- and 32-byte CQEs from one ring withIORING_SETUP_CQE_MIXED.
6.19 — Mixed-size SQE and 128-byte opcodes (≈ February 2026, per tree)
IORING_SETUP_SQE_MIXED— analogous toCQE_MIXEDfor submission: a ring mixes 64-byte and 128-byte SQEs.IORING_OP_NOP128— aNOPthat explicitly uses a 128-byte SQE (testing/alignment onSQE_MIXEDrings).IORING_OP_URING_CMD128— aURING_CMDthat explicitly uses a 128-byte SQE for command-data headroom.
(Git: all three are added by commit 1cba30bf9fdd, "io_uring: add support for IORING_SETUP_SQE_MIXED", dated 2025-10-22, whose first containing release tag is v6.19 in this tree.)
Marker accuracy note. The
liburing 2.15io_uring_enter.2text labelsIORING_OP_NOP128andIORING_OP_URING_CMD128"Available since 6.19". In this tree thev6.19tag does exist and contains the commit, so the man-page marker is internally consistent. There is no6.x → 7.0skip: the sequence is… 6.18 → 6.19 → 7.0 → 7.1 → 7.2 → 7.3-rc. Anyone reading this against a public kernel (≤ 6.13 as of early 2025) will not find these opcodes.
Worked example.
sqe-mixed.dissues mixed 64/128-byte SQEs +NOP128withIORING_SETUP_SQE_MIXED(needs ≥6.19).
7.0 — SQ rewind (≈ April 2026, per tree)
IORING_SETUP_SQ_REWIND— used withIORING_SETUP_NO_SQARRAY, allows the SQ tail to be rewound (re-submission of not-yet-consumed SQEs). (io_uring_setup.2: "Available since 7.0"; git:IORING_SETUP_SQ_REWINDfirst inv7.0.)
Worked example.
sq-rewind.drewinds the SQ tail to re-submit unconsumed SQEs withIORING_SETUP_SQ_REWIND(needs ≥7.0).
7.1 — Immediate timeout argument, bundle and min-wait fixes (June 14, 2026)
Tag v7.1 is 8cd9520d35a6 (2026-06-14). enum io_uring_op still ends at IORING_OP_URING_CMD128. The release's new flag, and the fixes that landed after the v7.1-rc6 snapshot this chronology used to stop at, are:
IORING_TIMEOUT_IMMEDIATE_ARG—sqe->addris the nanosecond count, not a pointer to atimespec. Added byd8345a21902a("io_uring/timeout: immediate timeout arg", 2026-03-09); first containing tagv7.1-rc1, so it ships in 7.1. The earlier v7.1-rc6 pass recorded the flag in the opcode reference and left it out of this chronology because 7.1 was not yet a release.- Bundle recv keeps
IORING_CQE_F_BUF_MOREacross an in-kernel retry.CQE_F_MASKinnet.cisIORING_CQE_F_SOCK_NONEMPTY | IORING_CQE_F_MORE | IORING_CQE_F_BUF_MORE(net.c:844, commited46f39c47eb). DroppingBUF_MOREmade userspace advance a provided-buffer ring head the kernel was still using, on an incremental (IOU_PBUF_RING_INC) bundle. - A bundle no longer shortens the last peeked buffer to the remaining byte count (
70f4886bcbb9,kbuf.c). The application already takesmin(buffer length, bytes left). Truncating and then failing the transfer left the buffer permanently smaller. min_wait_usecwakes only once a completion has actually been posted.29fe1bd01b99sets the CQ tail target tocq_min_tail + 1after the minimum wait expires, so a spurious wakeup with an empty CQ no longer returns early.
7.2 — Larger buffers, fixed-buffer send/recv, ZCRX events (August 16, 2026)
Tag v7.2 is 8d3ae59288f1 (2026-08-16). Still no new IORING_OP_* / IORING_SETUP_* / IORING_FEAT_* / IORING_REGISTER_* value. The user-visible contract changes:
- Registered buffers grow from 1 GiB to 1 TiB.
io_validate_user_buf_range(rsrc.c:131) rejectsulen > SZ_1Twith-EINVAL(rsrc.c:142, commitb4e41050b212). The per-buffer length field instruct io_mapped_ubufwidened fromunsigned inttosize_t(ca76b56a2a2a) so the larger range fits. Huge pages are accounted once per page in a per-ring xarray (hpage_acct,df0a52537c0f): cloning a buffer table, or registering two ranges that share a huge page, no longer chargesRLIMIT_MEMLOCKtwice. Accounting stays against the user that created the ring. IORING_RECVSEND_FIXED_BUFworks on plainSENDandRECV. Before 7.2 the flag was honored only onSEND_ZC. Commit57ed21fad402accepts it in the send/recv flag masks, recordssqe->buf_index, and imports the registered buffer at issue time. Prep rejects it unless the opcode is non-vectoredIORING_OP_SENDorIORING_OP_RECV(net.c:404: "registered buffer send only supported for plain IORING_OP_SEND"), and it is mutually exclusive with buffer select, bundles, and (on recv) multishot.SENDMSG/RECVMSGstill do not take it.- ZCRX can tell the application it fell off the zero-copy path.
ZCRX_FEATURE_EVENT(zcrx.h:68) plusstruct zcrx_event_desc(zcrx.h:88), passed inio_uring_zcrx_ifq_reg.event_desc(zcrx.h:111, carved out of the old__resv). Two event types (zcrx.h:71):ZCRX_EVENT_ALLOC_FAIL(the page pool could not allocate) andZCRX_EVENT_COPY(a fragment was copied). The kernel posts one CQE per armed type, with the descriptor'suser_dataand the event bit incqe->res; the application re-arms withIORING_REGISTER_ZCRX_CTRL/ZCRX_CTRL_ARM_EVENT(zcrx.h:118, dispatched atzcrx.c:1619). Optionalstruct zcrx_stats(zcrx.h:83,copy_count/copy_bytes) lives in the refill region atstats_offsetwhenZCRX_EVENT_DESC_FLAG_STATSis set, and is updated in place on every copy fallback.IO_URING_QUERY_ZCRX_EVENT(query.h:26) reports the event bitmask,sizeof(struct zcrx_stats), and the required alignment (query.c:48,io_query_zcrx_notif). The series landed as0719e10d826a,255180f7034f,6935f631465f, and was renamed from "notification" to "event" before the 7.2 tag (e366c15e1610). - BPF filters can see a
CONNECTtarget.struct io_uring_bpf_ctxgains aconnectarm (bpf_filter.h:37):family, and forAF_INET/AF_INET6a network-orderportplusv4_addrorv6_addr.io_connect_bpf_populate(net.c:1717, commit899bea8248ce) fills those fields only whenaddr_lencovers them, so a short address cannot leak a previous connect's sockaddr. - Provided-buffer addresses are checked, and a peek is capped.
access_okon every ring-provided buffer as it is selected (kbuf.c:215,46800585ae04).io_ring_buffers_peekcaps the imported length atMAX_RW_COUNT(kbuf.c:270,0b88f4705899), the same per-call ceiling the rest of Linux uses. IORING_REGISTER_RESIZE_RINGSkeeps the SQ index array. Pending entries are copied through the oldsq_arrayinto the new one (1fe703cc708f). Resizing a ring that still has the indirection array used to repointctx->sq_arrayand then submit the wrong SQE.MSG_RINGwill not paint a 32-byte CQE onto a 16-byte ring.io_msg_ring_cqe_flags(msg_ring.c:105,15cd3ccf9b17) returns-EINVALwhenIORING_MSG_RING_FLAGS_PASScarriesIORING_CQE_F_32and the target was created with neitherIORING_SETUP_CQE32norIORING_SETUP_CQE_MIXED.- Per-task restrictions survive
exec.bc0e8faf90e7splits task-context teardown so the exec-cancel path frees the io_uring task context and keeps the restriction. A ring created afterexecstays inside the restriction installed before it. - IOPOLL short reads report the bytes already transferred.
c554246ff4c6runs the sameio_fixup_rw_resthe non-polled path uses, so a later EOF returns the partial count instead of zero. - Task work is a FIFO, not a reversed llist. Both the
DEFER_TASKRUNlocal list and the ordinary task-work list moved ontompscq(io_uring/mpscq.h,d46ab2c98abaandde7341ffe49e). No new flag. Producers are wait-free; the consumer pops in queue order and no longer callsllist_reverse_order. ANULLpop means "the producer has not linked the tail yet," which is not the same as empty — see the comment at the top ofmpscq.h.
7.3-rc4 — Current checkout (development, September 20, 2026)
Tag v7.3-rc4 is 93f51579e7df248780214094418f205253383cc5 (2026-09-20). v7.3 itself is not tagged. enum io_uring_op still ends at IORING_OP_URING_CMD128. Anything below is an -rc and can still move before 7.3 final. First containing tag is noted per item.
ZCRX_CTRL_ADD_AREA(zcrx.h:119,struct zcrx_ctrl_add_areaatzcrx.h:138) adds another memory area to a live ZCRX interface queue. Commit3c8a5e271594("io_uring/zcrx: add dynamic area provisioning"), first tagv7.3-rc1. Registration no longer has to guess the working set:area_ptrpoints at astruct io_uring_zcrx_area_reg, andio_zcrx_ctrldispatches the op atzcrx.c:1621.MSG_TRUNCrecv no longer consumes more of a provided buffer than was filled. The socket layer returns the full datagram length;6028b543884f(first tagv7.3-rc3, liburing issue 1619) advances the buffer by the bytes actually copied and still reports the full length incqe->res.- IOPOLL completions or provided-buffer CQE flags into flags the opcode already set (
47ccc3f1c615,v7.3-rc3). Assigning them used to clearIORING_CQE_F_32on a mixed ring. - A
DEFER_TASKRUNring that queues task work from a waitqueue wakeup defers its eventfd signal (cd305ee3633a,v7.3-rc1,IOU_F_TWQ_IN_WAKE). Signaling inline held an arbitrary waitqueue lock and could re-enter epoll. - A multishot
URING_CMDthat returns a non-negative result inline is completed, not orphaned (360941242f09,v7.3-rc1). A handler that wants to keep the request returns-EIOCBQUEUEDor-EAGAIN, the same as any other command. - Futex inflight tracking covers private waits only.
FUTEX_WAKEis never inflight (73e701909747); a sharedFUTEX_WAIT/FUTEX_WAITVis not either (4d327bbd1cd2). Both first appear inv7.3-rc1. The tracking exists sodo_exitcan cancel a wait that depends on the mm-private futex hash. IORING_OP_WAITIDhonors task-work cancellation (14572de82e50,v7.3-rc1). When the callback runs from the fallback kworker,tw.cancelcompletes the request with-ECANCELEDand skips thesiginfocopy.__do_waitlooks up children oncurrent, which on that path is not the submitter.IORING_REGISTER_QUERYrejects a user size above a page beforecopy_struct_to_user(query.c:80,ba77efee1b95, merged for 7.3).BLOCK_URING_CMD_ZONE_RESET_ALL(include/uapi/linux/blkdev.h, commitca8f6548e6f7, first tagv7.3-rc1) is aURING_CMDfor zoned block devices: reset every sequential-write-required zone. It is not a newIORING_OP_*. liburing wraps it asio_uring_prep_cmd_zone_reset_all(below).
liburing companion — 2.15 tag through 78dce99b (September 11, 2026)
The opcode-helper snapshot this catalog used to name (40999f52, 2026-05-18) is one day before the two register helpers below, and about six weeks before the liburing-2.15 tag (June 27). Both helpers shipped in 2.15:
io_uring_register_query(e3242e33,liburing.h:368) andio_uring_register_zcrx_ctrl(af80c9fa,liburing.h:357).man/io_uring_register.2and the newio_uring_register_query.3/io_uring_register_zcrx_ctrl.3still say both are "Available since kernel 6.15". That matches this chronology forIORING_REGISTER_ZCRX_IFQ, and it does not match it forIORING_REGISTER_QUERY(kernel 6.18) orIORING_REGISTER_ZCRX_CTRL(kernel 6.19). The query page listsIO_URING_QUERY_OPCODES,_ZCRX, and_SCQonly. The ctrl page'sstruct zcrx_ctrlunion haszc_flushandzc_exportonly.IORING_TIMEOUT_IMMEDIATE_ARGis documented onio_uring_prep_timeout(3): when the flag is set, thetsargument is a nanosecond count cast to a pointer-sized integer, not atimespecpointer. There is no separate prep function.
78dce99b is liburing-2.15-35-g78dce99b. The spec file is still version 2.15. The FFI symbol map grew a LIBURING_2.16 section for one inline helper. Against kernel v7.3-rc4 the copied headers are behind:
| Kernel UAPI at v7.3-rc4 | In liburing 78dce99b |
|---|---|
ZCRX_FEATURE_EVENT, ZCRX_EVENT_ALLOC_FAIL, ZCRX_EVENT_COPY, struct zcrx_stats | Absent. src/include/liburing/io_uring.h stops enum zcrx_ctrl_op at ZCRX_CTRL_EXPORT (io_uring.h:1094). |
ZCRX_CTRL_ARM_EVENT, ZCRX_CTRL_ADD_AREA | Absent from that enum, so io_uring_register_zcrx_ctrl cannot name them. |
IO_URING_QUERY_ZCRX_EVENT | Absent. src/include/liburing/io_uring/query.h ends at IO_URING_QUERY_SCQ. |
io_uring_bpf_ctx.connect | Absent from src/include/liburing/io_uring/bpf_filter.h. test/cbpf_filter.c (in the 2.15 tag, f4b781ed) already pokes the same layout with raw offsets: family at 16, port at 20, address at 24, pdu_size 24. |
BLOCK_URING_CMD_ZONE_RESET_ALL | io_uring_prep_cmd_zone_reset_all (liburing.h:1690, commit fae20287). man/io_uring_prep_cmd_zone_reset_all.3 is headed "liburing-2.16" and says "Available since Linux 7.TBD." |
Plain SEND/RECV still have no fixed-buffer prep helper. io_uring_prep_send_zc_fixed (liburing.h:1158) and io_uring_prep_sendmsg_zc_fixed (liburing.h:1179) are the only setters of IORING_RECVSEND_FIXED_BUF.
Two queue fixes in this range are worth knowing if a loop uses the helpers rather than the rings directly. io_uring_peek_batch_cqe learned mixed 16/32-byte CQEs inside the 2.15 tag (4279ae37). After the tag, flushing a registered wait region checks IORING_FEAT_EXT_ARG first (e620d33b), and buffer-registration helpers reject a missing tags pointer when the caller asked for tags (b041e95a).
Feature/version × library matrix
Columns mark which surveyed runtimes are known to use a feature (✓), can but don't by default / optional (○), or do not use it (—). Library backends evolve; entries reflect each library's documented io_uring backend strategy as covered in the sibling docs (Tokio, Glommio, monoio, Boost.Asio, Seastar, libuv, Zig, .NET, Eio). Where a library has no io_uring backend at all (Go's netpoller uses epoll; Boost.Asio's io_uring support is opt-in and partial), that is noted in the row legend rather than per-cell.
Legend for libraries: Tok=Tokio (tokio-uring), Glo=Glommio, Mon=monoio, Asio=Boost.Asio, Sea=Seastar, libuv, Zig=Zig std Io, .NET, Eio=OCaml Eio (eio_linux).
| Feature | Since kernel | liburing helper | Tok | Glo | Mon | Asio | Sea | libuv | Zig | .NET | Eio |
|---|---|---|---|---|---|---|---|---|---|---|---|
SQ/CQ rings + setup/enter/register | 5.1 | io_uring_queue_init | ✓ | ✓ | ✓ | ○ | ✓ | ✓ | ✓ | ✓ | ✓ |
READV/WRITEV, FSYNC | 5.1 | io_uring_prep_readv | ✓ | ✓ | ✓ | ○ | ✓ | ✓ | ✓ | ✓ | ✓ |
READ_FIXED/WRITE_FIXED (registered buffers) | 5.1 | io_uring_prep_read_fixed | ✓ | ✓ | ✓ | — | ✓ | — | ○ | ○ | ○ |
POLL_ADD/POLL_REMOVE | 5.1 | io_uring_prep_poll_add | ✓ | ✓ | ✓ | ○ | ✓ | ✓ | ✓ | ✓ | ✓ |
IORING_SETUP_SQPOLL (kernel SQ thread) | 5.1 | io_uring_queue_init_params | ○ | ○ | ○ | — | ○ | — | ○ | ○ | ○ |
IORING_SETUP_IOPOLL (busy-poll completions) | 5.1 | (setup flag) | — | ✓ | — | — | ✓ | — | — | — | — |
SENDMSG/RECVMSG | 5.3 | io_uring_prep_sendmsg | ✓ | ✓ | ✓ | ○ | ✓ | ✓ | ✓ | ✓ | ✓ |
TIMEOUT (+ LINK_TIMEOUT 5.5) | 5.4 / 5.5 | io_uring_prep_timeout | ✓ | ✓ | ✓ | ○ | ✓ | ✓ | ✓ | ✓ | ✓ |
IORING_FEAT_SINGLE_MMAP | 5.4 | (feat flag) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
ACCEPT/CONNECT, ASYNC_CANCEL | 5.5 | io_uring_prep_accept | ✓ | ✓ | ✓ | ○ | ✓ | ✓ | ✓ | ✓ | ✓ |
IORING_FEAT_FAST_POLL | 5.7 | (feat flag) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
OPENAT/CLOSE/STATX, READ/WRITE | 5.6 | io_uring_prep_openat | ✓ | ✓ | ✓ | ○ | ✓ | ✓ | ✓ | ✓ | ✓ |
SEND/RECV (buffer form) | 5.6 | io_uring_prep_send | ✓ | ✓ | ✓ | ○ | ✓ | ✓ | ✓ | ✓ | ✓ |
IORING_REGISTER_PROBE (capability probe) | 5.6 | io_uring_get_probe | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
SPLICE/TEE | 5.7 / 5.8 | io_uring_prep_splice | ○ | ○ | ○ | — | ○ | — | — | — | — |
PROVIDE_BUFFERS (legacy provided buffers) | 5.7 | io_uring_prep_provide_buffers | ○ | ○ | ○ | — | — | — | — | — | — |
IORING_FEAT_NODROP / CQE_SKIP (5.17) | 5.5 / 5.17 | (feat flag) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
IORING_REGISTER_RING_FDS (registered ring) | 5.18 | io_uring_register_ring_fd | ✓ | ✓ | ✓ | — | ✓ | — | ○ | ✓ | ✓ |
MSG_RING | 5.18 | io_uring_prep_msg_ring | ○ | ○ | ○ | — | ○ | — | — | — | — |
Ring-provided buffers (PBUF_RING) | 5.19 | io_uring_setup_buf_ring | ✓ | ○ | ✓ | — | ✓ | — | ○ | ✓ | ○ |
Multishot ACCEPT | 5.19 | io_uring_prep_multishot_accept | ✓ | ○ | ✓ | — | ✓ | — | ○ | ○ | ○ |
SOCKET, URING_CMD, SQE128/CQE32 | 5.19 | io_uring_prep_socket | ○ | ○ | ✓ | — | ○ | — | ○ | ○ | — |
SEND_ZC (zero-copy send) | 6.0 | io_uring_prep_send_zc | ○ | ○ | ✓ | — | ✓ | — | ○ | ○ | — |
Multishot RECV | 6.0 | io_uring_prep_recv_multishot | ✓ | ○ | ✓ | — | ✓ | — | ○ | ○ | ○ |
IORING_SETUP_SINGLE_ISSUER | 6.0 | (setup flag) | ✓ | ✓ | ✓ | — | ✓ | — | ○ | ✓ | ✓ |
IORING_SETUP_DEFER_TASKRUN (+ COOP 5.19) | 6.1 | (setup flag) | ✓ | ✓ | ✓ | — | ✓ | — | ○ | ✓ | ✓ |
SENDMSG_ZC | 6.1 | io_uring_prep_sendmsg_zc | ○ | ○ | ✓ | — | ✓ | — | — | — | — |
IORING_TIMEOUT_MULTISHOT | 6.4 | io_uring_prep_timeout (flag) | ○ | ○ | ○ | — | ○ | — | — | ○ | ○ |
IORING_SETUP_NO_MMAP | 6.5 | (setup flag) | ○ | ○ | ○ | — | ○ | — | — | ○ | — |
FUTEX_WAIT/WAKE/WAITV | 6.7 | io_uring_prep_futex_wait | ○ | ○ | ○ | — | ○ | — | — | ○ | — |
WAITID (man says 6.5 — actually 6.7) | 6.7 | io_uring_prep_waitid | ○ | — | — | — | — | — | — | ○ | — |
READ_MULTISHOT | 6.7 | io_uring_prep_read_multishot | ○ | ○ | ○ | — | ○ | — | — | ○ | — |
FIXED_FD_INSTALL | 6.8 | io_uring_prep_fixed_fd_install | ○ | ○ | ○ | — | ○ | — | — | ○ | — |
IORING_REGISTER_NAPI (busy-poll) | 6.9 | io_uring_register_napi | ○ | ✓ | ○ | — | ✓ | — | — | — | — |
FTRUNCATE | 6.9 | io_uring_prep_ftruncate | ○ | ○ | ○ | — | ○ | — | — | ○ | — |
IORING_FEAT_RECVSEND_BUNDLE | 6.10 | io_uring_prep_send_bundle | ○ | ○ | ○ | — | ○ | — | — | ○ | — |
BIND/LISTEN | 6.11 | io_uring_prep_bind | ○ | ○ | ○ | — | ○ | — | — | ○ | — |
IORING_REGISTER_RESIZE_RINGS | 6.13† | io_uring_resize_rings | ○ | ○ | ○ | — | ○ | — | — | ○ | — |
RECV_ZC + ZCRX_IFQ (zero-copy recv) | 6.15† | io_uring_register_ifq | — | — | ○ | — | ○ | — | — | — | — |
EPOLL_WAIT | 6.15† | io_uring_prep_epoll_wait | — | — | — | — | — | — | — | — | — |
READV_FIXED/WRITEV_FIXED | 6.15† | io_uring_prep_readv_fixed | ○ | ○ | ○ | — | ○ | — | — | — | — |
PIPE | 6.16† | io_uring_prep_pipe | — | — | — | — | — | — | — | — | — |
IORING_SETUP_CQE_MIXED | 6.18† | (setup flag) | — | — | — | — | — | — | — | — | — |
SQE_MIXED, NOP128, URING_CMD128 | 6.19† | io_uring_prep_nop128 | — | — | — | — | — | — | — | — | — |
IORING_SETUP_SQ_REWIND | 7.0† | (setup flag) | — | — | — | — | — | — | — | — | — |
IORING_TIMEOUT_IMMEDIATE_ARG | 7.1† | io_uring_prep_timeout (flag) | — | — | — | — | — | — | — | — | — |
Registered buffer up to 1 TiB (SZ_1T) | 7.2† | — | — | — | — | — | — | — | — | — | — |
RECVSEND_FIXED_BUF on plain SEND/RECV | 7.2† | — | — | — | — | — | — | — | — | — | — |
ZCRX events (ALLOC_FAIL, COPY, stats) | 7.2† | — | — | — | — | — | — | — | — | — | — |
BPF filter context for CONNECT | 7.2† | — (test/cbpf_filter.c) | — | — | — | — | — | — | — | — | — |
ZCRX_CTRL_ADD_AREA (dynamic RX areas) | 7.3† | — | — | — | — | — | — | — | — | — | — |
URING_CMD zone-reset-all | 7.3† | io_uring_prep_cmd_zone_reset_all | — | — | — | — | — | — | — | — | — |
† Markers at 6.13 and beyond are forward-dated relative to public knowledge. Through 7.1 they are read from the liburing-2.15 man pages and the v7.3-rc4 git tags; 7.2–7.3 are read from that kernel tree. 78dce99b wraps zone-reset-all and documents IMMEDIATE_ARG, and its copied headers do not yet name the 7.2 ZCRX-event, query, or BPF-connect structs (see the liburing section). 7.3 is still -rc4 in the kernel tree. Library-usage cells for these very-recent features are conservatively —/○ because the surveyed libraries had not adopted them as of their last reviewed releases.
Library-row caveats:
- Boost.Asio (
Asiocolumn):io_uringis an optional backend (BOOST_ASIO_HAS_IO_URING), used only as a reactor substitute; it does not exploit zero-copy or multishot. Hence mostly○/—. - libuv: gained an
io_uringbackend (file ops, then some net ops) but stays conservative — read/write/fsync/poll-style usage, not the modern zero-copy/multishot surface. - Go: absent from the matrix — the Go runtime netpoller is epoll/kqueue-based and does not use
io_uring(see the Go netpoller sibling doc). - Eio (
eio_linux): uses a core set (rings, read/write, openat, poll, send/recv, registered fds, single-issuer/defer-taskrun) but not the newest zero-copy ops; see Eio.
Worked examples
Each row links a runnable, standalone during-based D program in the examples/ directory next to this file. Every example demonstrates its feature against the live kernel and degrades to a SKIP (exit 0) on kernels that lack it, so the whole set stays green from the 5.1 baseline up to a bleeding-edge tree. Run them all with ci --example-files, or one at a time with dub run --single <file>.
| Example | Since | Demonstrates |
|---|---|---|
nop.d | 5.1 | Minimal setup → submit → wait → CQE cycle |
read-write-fixed.d | 5.1 | Registered (fixed) buffers via WRITE_FIXED/READ_FIXED |
poll-add.d | 5.1 | One-shot POLL_ADD readiness on a pipe |
registered-files.d | 5.1 | Read a file by fixed-file index with IOSQE_FIXED_FILE |
sqpoll.d | 5.1 | NOP with no io_uring_enter via the SQPOLL kernel thread |
linked-sqes.d | 5.3 | IOSQE_IO_LINK write→fsync ordering |
sendmsg-recvmsg.d | 5.3 | Pass an fd over a unix socket (SCM_RIGHTS) via SENDMSG/RECVMSG |
timeout-link-timeout.d | 5.4 / 5.5 | TIMEOUT (-ETIME) and LINK_TIMEOUT cancelling a poll |
tcp-echo.d | 5.5 / 5.6 | Loopback ACCEPT+CONNECT+SEND+RECV on one ring |
async-cancel.d | 5.5 | ASYNC_CANCEL of an in-flight poll by user_data |
openat-statx-close.d | 5.6 | Async OPENAT→STATX→READ→CLOSE chain |
probe.d | 5.6 | IORING_REGISTER_PROBE per-opcode capability table |
splice-tee.d | 5.7 | Zero-copy SPLICE between pipes and TEE duplication |
restrictions.d | 5.10 | Sandbox a ring via REGISTER_RESTRICTIONS + R_DISABLED |
fs-mutations.d | 5.11 | Async MKDIRAT/SYMLINKAT/LINKAT/RENAMEAT/UNLINKAT |
direct-descriptors.d | 5.15 | Open into a fixed slot, then FIXED_FD_INSTALL back to a real fd |
cqe-skip.d | 5.17 | Suppress a successful op's CQE with IOSQE_CQE_SKIP_SUCCESS |
msg-ring.d | 5.18 | Post a u64 from one ring into another (cross-core wakeup) |
registered-ring-fd.d | 5.18 | Register the ring fd so io_uring_enter skips fdget/fdput |
multishot-accept.d | 5.19 | Multishot ACCEPT (CQEFlags.MORE keeps it armed) |
provided-buf-ring.d | 5.19 | Ring-provided buffers (PBUF_RING) selecting a RECV |
uring-cmd-socket.d | 5.19 | Socket getsockopt/setsockopt via URING_CMD passthrough |
send-zc.d | 6.0 | Zero-copy SEND_ZC transfer-CQE → notification-CQE |
multishot-recv.d | 6.0 | Multishot RECV posting a CQE per segment into provided buffers |
sync-cancel.d | 6.0 | Synchronous cancel of a poll via REGISTER_SYNC_CANCEL |
defer-taskrun.d | 6.1 | SINGLE_ISSUER+DEFER_TASKRUN+COOP_TASKRUN setup |
multishot-timeout.d | 6.4 | IORING_TIMEOUT_MULTISHOT recurring timer |
futex.d | 6.7 | Async FUTEX_WAIT woken by a legacy futex(2) wake |
waitid.d | 6.7 | Async child-process reap with IORING_OP_WAITID |
read-multishot.d | 6.7 | Repeated pipe reads into provided buffers via READ_MULTISHOT |
futex-waitv.d | 6.7 | Wait on a vector of futexes with FUTEX_WAITV |
napi.d | 6.9 | Configure NAPI busy-poll with REGISTER_NAPI |
socket-bind-listen.d | 6.11 | Async SOCKET + BIND + LISTEN lifecycle through the ring |
clock-min-timeout.d | 6.12 | Select the wait clock (REGISTER_CLOCK) + min-timeout wait |
resize-rings.d | 6.13 | Grow a live ring's SQ/CQ with REGISTER_RESIZE_RINGS |
epoll-wait.d | 6.15 | Fold a legacy epoll set into the ring with EPOLL_WAIT |
recv-zc.d | 6.15 | Zero-copy receive via a registered ZCRX interface queue |
pipe.d | 6.16 | IORING_OP_PIPE pipe-pair creation through the ring |
cqe-mixed.d | 6.18 | Reap mixed 16- and 32-byte CQEs with SETUP_CQE_MIXED |
sqe-mixed.d | 6.19 | Mixed 64/128-byte SQEs + NOP128 with SETUP_SQE_MIXED |
sq-rewind.d | 7.0 | Rewind the SQ tail to re-submit SQEs with SETUP_SQ_REWIND |
Cross-references
- Mechanics of each primitive: io_uring features.
- Per-opcode catalog with SQE field layouts: opcodes reference.
- How runtimes drive the loop: Tokio.
- Completion-based I/O behind an effect system: OCaml Eio.
Sources
- Linux kernel source —
include/uapi/linux/io_uring.h(enumio_uring_op, flag defines; identical from v7.1-rc6 through the v7.3-rc4 checkout) - Linux kernel source —
include/uapi/linux/io_uring/zcrx.h(ZCRX_FEATURE_EVENT,ZCRX_CTRL_ADD_AREA; v7.3-rc4) - Linux kernel source —
include/uapi/linux/io_uring/query.h(IO_URING_QUERY_ZCRX_EVENT; v7.3-rc4) - Linux kernel source —
include/uapi/linux/io_uring/bpf_filter.h(io_uring_bpf_ctx.connect; v7.3-rc4) - liburing repository at
78dce99b(2026-09-11;liburing-2.15plus 35 commits). Man pagesio_uring_enter.2,io_uring_setup.2,io_uring_register.2,io_uring_register_query.3,io_uring_register_zcrx_ctrl.3,io_uring_prep_cmd_zone_reset_all.3 - io_uring_enter(2) — man7.org
- io_uring_setup(2) — man7.org
- io_uring_register(2) — man7.org
- io_uring_prep_recv_multishot(3) — man7.org
- "Efficient IO with io_uring" — Jens Axboe (kernel.dk)
- "The rapid growth of io_uring" — LWN.net
- "Add io_uring support for waitid" — LWN.net
- "Add io_uring support for futex wait/wake" — LWN.net
- Linux kernel version history — Wikipedia (release dates)