Skip to content

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 --contains on 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=-rc4 in linux/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 now 78dce99b (2026-09-11): the liburing-2.15 tag (d41bf922, 2026-06-27) plus 35 commits. liburing.spec still says Version: 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 the 7.x series happened after 6.19, not by skipping 6.13+. Markers at 6.13 and 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.12 is independently corroborated by stable external sources. include/uapi/linux/io_uring.h is identical from v7.1-rc6 through v7.3-rc4. The UAPI that did move lives in include/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:

SubtletyExplanation
Enum order ≈ chronologyNew 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 ≠ kernelA 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 authoritativeThe 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_NOP
  • IORING_OP_READV / IORING_OP_WRITEV
  • IORING_OP_FSYNC
  • IORING_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.d walks the minimal setup → submit → wait → CQE cycle; read-write-fixed.d round-trips a payload through a registered (fixed) buffer; poll-add.d arms a one-shot POLL_ADD. All three are runnable during demos.

Worked examples. registered-files.d reads a file by fixed-file table index with IOSQE_FIXED_FILE; sqpoll.d submits a NOP with no io_uring_enter syscall via the IORING_SETUP_SQPOLL kernel thread.

5.2 — Sync file range (July 2019) ​

  • IORING_OP_SYNC_FILE_RANGE — async sync_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 first IORING_FEAT_SINGLE_MMAP actually 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 — async sendmsg(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.d chains a write to an fsync on the same fd with IOSQE_IO_LINK, showing the kernel-guaranteed write-before-fsync ordering and the failure-propagation (-ECANCELED) semantics of linked SQEs.

Worked example. sendmsg-recvmsg.d passes a file descriptor over a unix socket with SCM_RIGHTS via SENDMSG/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 one mmap, cutting setup from three mmap calls 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 by IORING_FEAT_SUBMIT_STABLE (5.5).

Worked example. timeout-link-timeout.d fires a standalone relative TIMEOUT (completing with -ETIME) and then uses a LINK_TIMEOUT (5.5) to cancel a never-ready poll.

A pivotal release for networking and control flow:

  • IORING_OP_TIMEOUT_REMOVE
  • IORING_OP_ACCEPT — async accept4(2).
  • IORING_OP_ASYNC_CANCEL — cancel a previously submitted request by user_data.
  • IORING_OP_LINK_TIMEOUT — a timeout attached to the linked request, cancelling it if it doesn't finish in time.
  • IORING_OP_CONNECT — async connect(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.d drives a loopback ACCEPT+CONNECT+SEND+RECV round-trip through a single ring; async-cancel.d cancels an in-flight poll by its user_data and observes the -ECANCELED completion.

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_FALLOCATE
  • IORING_OP_OPENAT / IORING_OP_OPENAT2 / IORING_OP_CLOSE
  • IORING_OP_FILES_UPDATE (the in-SQE form of file-table update)
  • IORING_OP_STATX
  • IORING_OP_READ / IORING_OP_WRITE (the simple, non-vectored variants)
  • IORING_OP_FADVISE / IORING_OP_MADVISE
  • IORING_OP_SEND / IORING_OP_RECV (the buffer-pointer, non-msghdr forms)
  • IORING_OP_EPOLL_CTL — async epoll_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.d chains OPENAT→STATX→READ→CLOSE asynchronously, feeding each op's result fd into the next; probe.d uses IORING_REGISTER_PROBE to print which opcodes the running kernel supports.

5.7 — Splice, provided buffers, fast poll (May 2020) ​

  • IORING_OP_SPLICE — async splice(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 via IOSQE_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 like RECVMSG on 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 makes io_uring a competitive readiness reactor as well as a completion proactor.

Worked example. splice-tee.d does a zero-copy SPLICE between pipes and a TEE duplication.

5.8 — Tee (August 2020) ​

  • IORING_OP_TEE — async tee(2). (io_uring_enter.2: "Available since 5.8".)
  • IORING_REGISTER_EVENTFD ordering semantics refined ("since 5.8").

5.9 — 32-bit poll events (October 2020) ​

  • IORING_FEAT_POLL_32BITS — poll requests accept the full 32-bit epoll event mask including EPOLLEXCLUSIVE. (io_uring_setup.2: "Available since kernel 5.9"; kernel git: the IORING_FEAT_POLL_32BITS define is not present in v5.8 and first appears in v5.9 via commit 5769a351b89c, "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_ARG groundwork (finalized 5.11).

Worked example. restrictions.d sandboxes a ring to a whitelist of opcodes via IORING_REGISTER_RESTRICTIONS + IORING_SETUP_R_DISABLED.

5.11 — Filesystem mutation ops, SQPOLL without root (February 2021) ​

  • IORING_OP_SHUTDOWN — async shutdown(2).
  • IORING_OP_RENAMEAT — async renameat2(2).
  • IORING_OP_UNLINKAT — async unlinkat(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.d performs async MKDIRAT/SYMLINKAT/LINKAT/RENAMEAT/UNLINKAT directory mutations.

5.12 — Files-update by index, native workers (April 2021) ​

  • IORING_REGISTER_FILES_UPDATE can place an fd at a specific index in the registered file table; descriptors set to IORING_REGISTER_FILES_SKIP are left untouched at that index. (io_uring_register.2: both "Available since 5.12"; the IORING_REGISTER_FILES_SKIP define is first present in the v5.12 UAPI header.)
  • IORING_FEAT_NATIVE_WORKERS — io_uring switches 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/UNLINKAT see refinements.

5.13 — Resource tags, poll update (June 2021) ​

  • IORING_REGISTER_BUFFERS2 / IORING_REGISTER_BUFFERS_UPDATE and IORING_REGISTER_FILES2 / IORING_REGISTER_FILES_UPDATE2 — the tagged-resource registration family. (All "Available since 5.13" in io_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_ADD can 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 — async mkdirat(2).
  • IORING_OP_SYMLINKAT — async symlinkat(2).
  • IORING_OP_LINKAT — async linkat(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.d opens into a fixed-file slot, uses it by index, then FIXED_FD_INSTALLs it back to a real fd.

5.17 — CQE skip, faster cancel (March 2022) ​

  • IORING_FEAT_CQE_SKIP — IOSQE_CQE_SKIP_SUCCESS lets 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.d suppresses a successful op's CQE in a link with IOSQE_CQE_SKIP_SUCCESS.

5.18 — Ring-fd registration, msg-ring, linked-file (May 2022) ​

  • IORING_OP_MSG_RING — send a message (two u64s) from one ring to another, enabling ring-to-ring wakeups. (io_uring_enter.2: "Available since 5.18". Git: the enum value IORING_OP_MSG_RING, is added by commit "io_uring: add support for IORING_OP_MSG_RING command", first contained in tag v5.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 so io_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 in v5.18.)

Worked examples. msg-ring.d posts a u64 from one ring into another — the cross-core wakeup primitive; registered-ring-fd.d registers the ring fd so io_uring_enter skips per-call fdget/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 — async socket(2). (io_uring_enter.2: "Available since 5.19". Git confirms enum value first in v5.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_MULTISHOT on IORING_OP_ACCEPT) — one SQE yields a CQE per incoming connection while IORING_CQE_F_MORE stays 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.d serves multiple loopback connections from a single armed accept SQE (each CQE carries CQEFlags.MORE); provided-buf-ring.d registers an io_uring_buf ring and issues a buffer-selecting RECV.

Worked example. uring-cmd-socket.d does socket getsockopt/setsockopt through the URING_CMD passthrough channel.

6.0 — Zero-copy send, single-issuer, sync cancel (October 2022) ​

  • IORING_OP_SEND_ZC — zero-copy send; 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 in v6.0.)
  • Multishot recv (IORING_RECV_MULTISHOT) becomes available — confirmed "available since kernel 6.0" by the io_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.d performs a zero-copy SEND_ZC over 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.d arms one RECV posting a CQE per segment into provided-ring buffers; sync-cancel.d cancels an in-flight poll synchronously with IORING_REGISTER_SYNC_CANCEL.

6.1 — Zero-copy sendmsg, deferred task-run (December 2022) ​

  • IORING_OP_SENDMSG_ZC — the msghdr form of zero-copy send. (io_uring_enter.2: "Available since 6.1". Git: enum value first in v6.1.)
  • IORING_SETUP_DEFER_TASKRUN — defer completion task-work until the app calls io_uring_enter(2) with IORING_ENTER_GETEVENTS; requires SINGLE_ISSUER. (io_uring_setup.2: "Available since 6.1".) Together with COOP_TASKRUN this is the latency/syscall-batching configuration most high-performance libraries adopt (see Glommio/monoio in the matrix).

Worked example. defer-taskrun.d sets up a ring with SINGLE_ISSUER | DEFER_TASKRUN | COOP_TASKRUN and reaps a NOP and a relative TIMEOUT through the GETEVENTS-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 in v6.3.)

6.4 — Multishot timeout (June 2023) ​

  • IORING_TIMEOUT_MULTISHOT (a timeout_flags bit on IORING_OP_TIMEOUT) — a repeating timer that posts a CQE per interval. (Git: the IORING_TIMEOUT_MULTISHOT define is first contained in tag v6.4.)

Worked example. multishot-timeout.d arms one MULTISHOT timeout, collects three recurring -ETIME ticks (each with CQEFlags.MORE), then stops the repeats with an ASYNC_CANCEL.

6.5 — No-mmap setup (August 2023) ​

  • IORING_SETUP_NO_MMAP — userspace allocates the SQ/CQ memory itself and passes pointers, avoiding the kernel mmap. (io_uring_setup.2: "Available since 6.5".)
  • IORING_SETUP_REGISTERED_FD_ONLY — used with NO_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.2 marks IORING_OP_WAITID as "Available since 6.5". This is incorrect for the kernel: the enum value IORING_OP_WAITID is added by commit f31ecf671ddc (Sept 2023), whose first containing release tag is v6.7, and LWN's waitid coverage and the upstream patch series both target 6.7. This document therefore lists IORING_OP_WAITID under 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 — async waitid(2); a parent gets a CQE on child state change instead of blocking. (Git: enum first in v6.7; LWN.) See the 6.5 discrepancy note above.
  • IORING_OP_FUTEX_WAIT / IORING_OP_FUTEX_WAKE / IORING_OP_FUTEX_WAITV — async futex(2) operations; FUTEX_WAIT mirrors FUTEX_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 in v6.7.)

Worked example. futex.d parks a ring on a 32-bit private futex with IORING_OP_FUTEX_WAIT and is woken by a helper thread issuing a legacy futex(2) wake on the same word.

Worked examples. waitid.d asynchronously reaps a child process with IORING_OP_WAITID; read-multishot.d does repeated reads from a pipe into provided-ring buffers with READ_MULTISHOT; futex-waitv.d waits on a vector of futexes with FUTEX_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 in v6.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 — async ftruncate(2). (io_uring_enter.2: "Available since 6.9"; git: enum first in v6.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.d configures NAPI busy-poll with IORING_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: the IORING_FEAT_RECVSEND_BUNDLE define is first contained in tag v6.10.)

6.11 — Async bind/listen (September 2024) ​

  • IORING_OP_BIND — async bind(2). (Git: enum first in v6.11; io_uring_enter.2: "Available since 6.11".)
  • IORING_OP_LISTEN — async listen(2). (Git: enum first in v6.11; "Available since 6.11".)

Worked example. socket-bind-listen.d drives a full async SOCKET + BIND + LISTEN lifecycle 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_MONOTONIC vs CLOCK_BOOTTIME). (io_uring_register.2: "Available since 6.12"; git: first in v6.12.)
  • IORING_REGISTER_CLONE_BUFFERS (with IORING_REGISTER_DST_REPLACE) — clone a registered buffer table from one ring into another. (io_uring_register.2; git: first in v6.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_TIMEOUT first in v6.12.)
  • IORING_REGISTER_SEND_MSG_RING — issue a MSG_RING synchronously from io_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 (tag liburing-2.15, plus 78dce99b where a page moved) and git tag --contains / git merge-base --is-ancestor in the v7.3-rc4 tree. The 7.2 and 7.3 entries below are taken from that kernel tree: the liburing tag predates them, and 78dce99b still has not copied their UAPI. IORING_TIMEOUT_IMMEDIATE_ARG is the exception — man/io_uring_prep_timeout.3 in 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.d selects the wait clock with IORING_REGISTER_CLOCK and 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 in v6.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 with IORING_SETUP_IOPOLL. (Git: IORING_SETUP_HYBRID_IOPOLL first in v6.13.)
  • IORING_REGISTER_CLONE_BUFFERS gains offset/destination-range support ("6.13 added support for specifying the offsets…").

Worked example. resize-rings.d grows a live ring's SQ/CQ with IORING_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 in v6.15.)
  • IORING_OP_EPOLL_WAIT — async epoll_wait(2), letting legacy epoll loops fold into io_uring. (io_uring_enter.2: "Available since 6.15"; git: enum first in v6.15.)
  • IORING_OP_READV_FIXED / IORING_OP_WRITEV_FIXED — vectored I/O whose iovec entries point into a registered buffer, combining READV/WRITEV with the fixed-buffer fast path. (io_uring_enter.2: "Available since 6.15"; git: enum first in v6.15.)
  • IORING_REGISTER_ZCRX_IFQ — register the zero-copy RX interface queue that RECV_ZC consumes. (io_uring_register.2: "Available since kernel 6.15"; git: enum value first in v6.15.) A later IORING_REGISTER_ZCRX_CTRL control op is documented alongside it.
  • IORING_REGISTER_QUERY — a structured capability-query registration op (a successor/companion to IORING_REGISTER_PROBE). Discrepancy flagged: liburing 2.15's io_uring_register.2 marks it "Available since kernel 6.15", but in this tree the enum value IORING_REGISTER_QUERY is not present in v6.15; it is added by commit c265ae75f900 ("io_uring: introduce io_uring querying", Sept 2025), whose first containing tag is v6.18. Treat the kernel availability as 6.18, not 6.15.

Worked examples. epoll-wait.d folds a legacy epoll set into the ring with IORING_OP_EPOLL_WAIT; recv-zc.d does 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 — async pipe2(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 in v6.16.)

Worked example. pipe.d creates a pipe pair through the ring with IORING_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 to CQE32 for the whole ring. (io_uring_setup.2: "Available since 6.18"; git: IORING_SETUP_CQE_MIXED first in v6.18.)

Worked example. cqe-mixed.d reaps mixed 16- and 32-byte CQEs from one ring with IORING_SETUP_CQE_MIXED.

6.19 — Mixed-size SQE and 128-byte opcodes (≈ February 2026, per tree) ​

  • IORING_SETUP_SQE_MIXED — analogous to CQE_MIXED for submission: a ring mixes 64-byte and 128-byte SQEs.
  • IORING_OP_NOP128 — a NOP that explicitly uses a 128-byte SQE (testing/alignment on SQE_MIXED rings).
  • IORING_OP_URING_CMD128 — a URING_CMD that 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.15 io_uring_enter.2 text labels IORING_OP_NOP128 and IORING_OP_URING_CMD128 "Available since 6.19". In this tree the v6.19 tag does exist and contains the commit, so the man-page marker is internally consistent. There is no 6.x → 7.0 skip: 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.d issues mixed 64/128-byte SQEs + NOP128 with IORING_SETUP_SQE_MIXED (needs ≥6.19).

7.0 — SQ rewind (≈ April 2026, per tree) ​

  • IORING_SETUP_SQ_REWIND — used with IORING_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_REWIND first in v7.0.)

Worked example. sq-rewind.d rewinds the SQ tail to re-submit unconsumed SQEs with IORING_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->addr is the nanosecond count, not a pointer to a timespec. Added by d8345a21902a ("io_uring/timeout: immediate timeout arg", 2026-03-09); first containing tag v7.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_MORE across an in-kernel retry. CQE_F_MASK in net.c is IORING_CQE_F_SOCK_NONEMPTY | IORING_CQE_F_MORE | IORING_CQE_F_BUF_MORE (net.c:844, commit ed46f39c47eb). Dropping BUF_MORE made 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 takes min(buffer length, bytes left). Truncating and then failing the transfer left the buffer permanently smaller.
  • min_wait_usec wakes only once a completion has actually been posted. 29fe1bd01b99 sets the CQ tail target to cq_min_tail + 1 after 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) rejects ulen > SZ_1T with -EINVAL (rsrc.c:142, commit b4e41050b212). The per-buffer length field in struct io_mapped_ubuf widened from unsigned int to size_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 charges RLIMIT_MEMLOCK twice. Accounting stays against the user that created the ring.
  • IORING_RECVSEND_FIXED_BUF works on plain SEND and RECV. Before 7.2 the flag was honored only on SEND_ZC. Commit 57ed21fad402 accepts it in the send/recv flag masks, records sqe->buf_index, and imports the registered buffer at issue time. Prep rejects it unless the opcode is non-vectored IORING_OP_SEND or IORING_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 / RECVMSG still do not take it.
  • ZCRX can tell the application it fell off the zero-copy path. ZCRX_FEATURE_EVENT (zcrx.h:68) plus struct zcrx_event_desc (zcrx.h:88), passed in io_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) and ZCRX_EVENT_COPY (a fragment was copied). The kernel posts one CQE per armed type, with the descriptor's user_data and the event bit in cqe->res; the application re-arms with IORING_REGISTER_ZCRX_CTRL / ZCRX_CTRL_ARM_EVENT (zcrx.h:118, dispatched at zcrx.c:1619). Optional struct zcrx_stats (zcrx.h:83, copy_count / copy_bytes) lives in the refill region at stats_offset when ZCRX_EVENT_DESC_FLAG_STATS is 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 as 0719e10d826a, 255180f7034f, 6935f631465f, and was renamed from "notification" to "event" before the 7.2 tag (e366c15e1610).
  • BPF filters can see a CONNECT target. struct io_uring_bpf_ctx gains a connect arm (bpf_filter.h:37): family, and for AF_INET / AF_INET6 a network-order port plus v4_addr or v6_addr. io_connect_bpf_populate (net.c:1717, commit 899bea8248ce) fills those fields only when addr_len covers them, so a short address cannot leak a previous connect's sockaddr.
  • Provided-buffer addresses are checked, and a peek is capped. access_ok on every ring-provided buffer as it is selected (kbuf.c:215, 46800585ae04). io_ring_buffers_peek caps the imported length at MAX_RW_COUNT (kbuf.c:270, 0b88f4705899), the same per-call ceiling the rest of Linux uses.
  • IORING_REGISTER_RESIZE_RINGS keeps the SQ index array. Pending entries are copied through the old sq_array into the new one (1fe703cc708f). Resizing a ring that still has the indirection array used to repoint ctx->sq_array and then submit the wrong SQE.
  • MSG_RING will not paint a 32-byte CQE onto a 16-byte ring. io_msg_ring_cqe_flags (msg_ring.c:105, 15cd3ccf9b17) returns -EINVAL when IORING_MSG_RING_FLAGS_PASS carries IORING_CQE_F_32 and the target was created with neither IORING_SETUP_CQE32 nor IORING_SETUP_CQE_MIXED.
  • Per-task restrictions survive exec. bc0e8faf90e7 splits task-context teardown so the exec-cancel path frees the io_uring task context and keeps the restriction. A ring created after exec stays inside the restriction installed before it.
  • IOPOLL short reads report the bytes already transferred. c554246ff4c6 runs the same io_fixup_rw_res the 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_TASKRUN local list and the ordinary task-work list moved onto mpscq (io_uring/mpscq.h, d46ab2c98aba and de7341ffe49e). No new flag. Producers are wait-free; the consumer pops in queue order and no longer calls llist_reverse_order. A NULL pop means "the producer has not linked the tail yet," which is not the same as empty — see the comment at the top of mpscq.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_area at zcrx.h:138) adds another memory area to a live ZCRX interface queue. Commit 3c8a5e271594 ("io_uring/zcrx: add dynamic area provisioning"), first tag v7.3-rc1. Registration no longer has to guess the working set: area_ptr points at a struct io_uring_zcrx_area_reg, and io_zcrx_ctrl dispatches the op at zcrx.c:1621.
  • MSG_TRUNC recv no longer consumes more of a provided buffer than was filled. The socket layer returns the full datagram length; 6028b543884f (first tag v7.3-rc3, liburing issue 1619) advances the buffer by the bytes actually copied and still reports the full length in cqe->res.
  • IOPOLL completions or provided-buffer CQE flags into flags the opcode already set (47ccc3f1c615, v7.3-rc3). Assigning them used to clear IORING_CQE_F_32 on a mixed ring.
  • A DEFER_TASKRUN ring 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_CMD that returns a non-negative result inline is completed, not orphaned (360941242f09, v7.3-rc1). A handler that wants to keep the request returns -EIOCBQUEUED or -EAGAIN, the same as any other command.
  • Futex inflight tracking covers private waits only. FUTEX_WAKE is never inflight (73e701909747); a shared FUTEX_WAIT / FUTEX_WAITV is not either (4d327bbd1cd2). Both first appear in v7.3-rc1. The tracking exists so do_exit can cancel a wait that depends on the mm-private futex hash.
  • IORING_OP_WAITID honors task-work cancellation (14572de82e50, v7.3-rc1). When the callback runs from the fallback kworker, tw.cancel completes the request with -ECANCELED and skips the siginfo copy. __do_wait looks up children on current, which on that path is not the submitter.
  • IORING_REGISTER_QUERY rejects a user size above a page before copy_struct_to_user (query.c:80, ba77efee1b95, merged for 7.3).
  • BLOCK_URING_CMD_ZONE_RESET_ALL (include/uapi/linux/blkdev.h, commit ca8f6548e6f7, first tag v7.3-rc1) is a URING_CMD for zoned block devices: reset every sequential-write-required zone. It is not a new IORING_OP_*. liburing wraps it as io_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) and io_uring_register_zcrx_ctrl (af80c9fa, liburing.h:357). man/io_uring_register.2 and the new io_uring_register_query.3 / io_uring_register_zcrx_ctrl.3 still say both are "Available since kernel 6.15". That matches this chronology for IORING_REGISTER_ZCRX_IFQ, and it does not match it for IORING_REGISTER_QUERY (kernel 6.18) or IORING_REGISTER_ZCRX_CTRL (kernel 6.19). The query page lists IO_URING_QUERY_OPCODES, _ZCRX, and _SCQ only. The ctrl page's struct zcrx_ctrl union has zc_flush and zc_export only.
  • IORING_TIMEOUT_IMMEDIATE_ARG is documented on io_uring_prep_timeout(3): when the flag is set, the ts argument is a nanosecond count cast to a pointer-sized integer, not a timespec pointer. 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-rc4In liburing 78dce99b
ZCRX_FEATURE_EVENT, ZCRX_EVENT_ALLOC_FAIL, ZCRX_EVENT_COPY, struct zcrx_statsAbsent. 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_AREAAbsent from that enum, so io_uring_register_zcrx_ctrl cannot name them.
IO_URING_QUERY_ZCRX_EVENTAbsent. src/include/liburing/io_uring/query.h ends at IO_URING_QUERY_SCQ.
io_uring_bpf_ctx.connectAbsent 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_ALLio_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).

FeatureSince kernelliburing helperTokGloMonAsioSealibuvZig.NETEio
SQ/CQ rings + setup/enter/register5.1io_uring_queue_init✓✓✓○✓✓✓✓✓
READV/WRITEV, FSYNC5.1io_uring_prep_readv✓✓✓○✓✓✓✓✓
READ_FIXED/WRITE_FIXED (registered buffers)5.1io_uring_prep_read_fixed✓✓✓—✓—○○○
POLL_ADD/POLL_REMOVE5.1io_uring_prep_poll_add✓✓✓○✓✓✓✓✓
IORING_SETUP_SQPOLL (kernel SQ thread)5.1io_uring_queue_init_params○○○—○—○○○
IORING_SETUP_IOPOLL (busy-poll completions)5.1(setup flag)—✓——✓————
SENDMSG/RECVMSG5.3io_uring_prep_sendmsg✓✓✓○✓✓✓✓✓
TIMEOUT (+ LINK_TIMEOUT 5.5)5.4 / 5.5io_uring_prep_timeout✓✓✓○✓✓✓✓✓
IORING_FEAT_SINGLE_MMAP5.4(feat flag)✓✓✓✓✓✓✓✓✓
ACCEPT/CONNECT, ASYNC_CANCEL5.5io_uring_prep_accept✓✓✓○✓✓✓✓✓
IORING_FEAT_FAST_POLL5.7(feat flag)✓✓✓✓✓✓✓✓✓
OPENAT/CLOSE/STATX, READ/WRITE5.6io_uring_prep_openat✓✓✓○✓✓✓✓✓
SEND/RECV (buffer form)5.6io_uring_prep_send✓✓✓○✓✓✓✓✓
IORING_REGISTER_PROBE (capability probe)5.6io_uring_get_probe✓✓✓✓✓✓✓✓✓
SPLICE/TEE5.7 / 5.8io_uring_prep_splice○○○—○————
PROVIDE_BUFFERS (legacy provided buffers)5.7io_uring_prep_provide_buffers○○○——————
IORING_FEAT_NODROP / CQE_SKIP (5.17)5.5 / 5.17(feat flag)✓✓✓✓✓✓✓✓✓
IORING_REGISTER_RING_FDS (registered ring)5.18io_uring_register_ring_fd✓✓✓—✓—○✓✓
MSG_RING5.18io_uring_prep_msg_ring○○○—○————
Ring-provided buffers (PBUF_RING)5.19io_uring_setup_buf_ring✓○✓—✓—○✓○
Multishot ACCEPT5.19io_uring_prep_multishot_accept✓○✓—✓—○○○
SOCKET, URING_CMD, SQE128/CQE325.19io_uring_prep_socket○○✓—○—○○—
SEND_ZC (zero-copy send)6.0io_uring_prep_send_zc○○✓—✓—○○—
Multishot RECV6.0io_uring_prep_recv_multishot✓○✓—✓—○○○
IORING_SETUP_SINGLE_ISSUER6.0(setup flag)✓✓✓—✓—○✓✓
IORING_SETUP_DEFER_TASKRUN (+ COOP 5.19)6.1(setup flag)✓✓✓—✓—○✓✓
SENDMSG_ZC6.1io_uring_prep_sendmsg_zc○○✓—✓————
IORING_TIMEOUT_MULTISHOT6.4io_uring_prep_timeout (flag)○○○—○——○○
IORING_SETUP_NO_MMAP6.5(setup flag)○○○—○——○—
FUTEX_WAIT/WAKE/WAITV6.7io_uring_prep_futex_wait○○○—○——○—
WAITID (man says 6.5 — actually 6.7)6.7io_uring_prep_waitid○——————○—
READ_MULTISHOT6.7io_uring_prep_read_multishot○○○—○——○—
FIXED_FD_INSTALL6.8io_uring_prep_fixed_fd_install○○○—○——○—
IORING_REGISTER_NAPI (busy-poll)6.9io_uring_register_napi○✓○—✓————
FTRUNCATE6.9io_uring_prep_ftruncate○○○—○——○—
IORING_FEAT_RECVSEND_BUNDLE6.10io_uring_prep_send_bundle○○○—○——○—
BIND/LISTEN6.11io_uring_prep_bind○○○—○——○—
IORING_REGISTER_RESIZE_RINGS6.13†io_uring_resize_rings○○○—○——○—
RECV_ZC + ZCRX_IFQ (zero-copy recv)6.15†io_uring_register_ifq——○—○————
EPOLL_WAIT6.15†io_uring_prep_epoll_wait—————————
READV_FIXED/WRITEV_FIXED6.15†io_uring_prep_readv_fixed○○○—○————
PIPE6.16†io_uring_prep_pipe—————————
IORING_SETUP_CQE_MIXED6.18†(setup flag)—————————
SQE_MIXED, NOP128, URING_CMD1286.19†io_uring_prep_nop128—————————
IORING_SETUP_SQ_REWIND7.0†(setup flag)—————————
IORING_TIMEOUT_IMMEDIATE_ARG7.1†io_uring_prep_timeout (flag)—————————
Registered buffer up to 1 TiB (SZ_1T)7.2†——————————
RECVSEND_FIXED_BUF on plain SEND/RECV7.2†——————————
ZCRX events (ALLOC_FAIL, COPY, stats)7.2†——————————
BPF filter context for CONNECT7.2†— (test/cbpf_filter.c)—————————
ZCRX_CTRL_ADD_AREA (dynamic RX areas)7.3†——————————
URING_CMD zone-reset-all7.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 (Asio column): io_uring is 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_uring backend (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>.

ExampleSinceDemonstrates
nop.d5.1Minimal setup → submit → wait → CQE cycle
read-write-fixed.d5.1Registered (fixed) buffers via WRITE_FIXED/READ_FIXED
poll-add.d5.1One-shot POLL_ADD readiness on a pipe
registered-files.d5.1Read a file by fixed-file index with IOSQE_FIXED_FILE
sqpoll.d5.1NOP with no io_uring_enter via the SQPOLL kernel thread
linked-sqes.d5.3IOSQE_IO_LINK write→fsync ordering
sendmsg-recvmsg.d5.3Pass an fd over a unix socket (SCM_RIGHTS) via SENDMSG/RECVMSG
timeout-link-timeout.d5.4 / 5.5TIMEOUT (-ETIME) and LINK_TIMEOUT cancelling a poll
tcp-echo.d5.5 / 5.6Loopback ACCEPT+CONNECT+SEND+RECV on one ring
async-cancel.d5.5ASYNC_CANCEL of an in-flight poll by user_data
openat-statx-close.d5.6Async OPENAT→STATX→READ→CLOSE chain
probe.d5.6IORING_REGISTER_PROBE per-opcode capability table
splice-tee.d5.7Zero-copy SPLICE between pipes and TEE duplication
restrictions.d5.10Sandbox a ring via REGISTER_RESTRICTIONS + R_DISABLED
fs-mutations.d5.11Async MKDIRAT/SYMLINKAT/LINKAT/RENAMEAT/UNLINKAT
direct-descriptors.d5.15Open into a fixed slot, then FIXED_FD_INSTALL back to a real fd
cqe-skip.d5.17Suppress a successful op's CQE with IOSQE_CQE_SKIP_SUCCESS
msg-ring.d5.18Post a u64 from one ring into another (cross-core wakeup)
registered-ring-fd.d5.18Register the ring fd so io_uring_enter skips fdget/fdput
multishot-accept.d5.19Multishot ACCEPT (CQEFlags.MORE keeps it armed)
provided-buf-ring.d5.19Ring-provided buffers (PBUF_RING) selecting a RECV
uring-cmd-socket.d5.19Socket getsockopt/setsockopt via URING_CMD passthrough
send-zc.d6.0Zero-copy SEND_ZC transfer-CQE → notification-CQE
multishot-recv.d6.0Multishot RECV posting a CQE per segment into provided buffers
sync-cancel.d6.0Synchronous cancel of a poll via REGISTER_SYNC_CANCEL
defer-taskrun.d6.1SINGLE_ISSUER+DEFER_TASKRUN+COOP_TASKRUN setup
multishot-timeout.d6.4IORING_TIMEOUT_MULTISHOT recurring timer
futex.d6.7Async FUTEX_WAIT woken by a legacy futex(2) wake
waitid.d6.7Async child-process reap with IORING_OP_WAITID
read-multishot.d6.7Repeated pipe reads into provided buffers via READ_MULTISHOT
futex-waitv.d6.7Wait on a vector of futexes with FUTEX_WAITV
napi.d6.9Configure NAPI busy-poll with REGISTER_NAPI
socket-bind-listen.d6.11Async SOCKET + BIND + LISTEN lifecycle through the ring
clock-min-timeout.d6.12Select the wait clock (REGISTER_CLOCK) + min-timeout wait
resize-rings.d6.13Grow a live ring's SQ/CQ with REGISTER_RESIZE_RINGS
epoll-wait.d6.15Fold a legacy epoll set into the ring with EPOLL_WAIT
recv-zc.d6.15Zero-copy receive via a registered ZCRX interface queue
pipe.d6.16IORING_OP_PIPE pipe-pair creation through the ring
cqe-mixed.d6.18Reap mixed 16- and 32-byte CQEs with SETUP_CQE_MIXED
sqe-mixed.d6.19Mixed 64/128-byte SQEs + NOP128 with SETUP_SQE_MIXED
sq-rewind.d7.0Rewind the SQ tail to re-submit SQEs with SETUP_SQ_REWIND

Cross-references ​


Sources ​