Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Layer-0 Spec — io_uring Module: Master Inventory Document

Technical specification — Version 1.0 (frozen kernel target: Linux 6.12 LTS)

Role of this document. This is the reference map of the air-sys-syscall::io_uring module. It exhaustively enumerates the ABI surface of io_uring as it exists in kernel 6.12 (the frozen baseline), classifies each entry (retained / obsolete-dropped), links it to a specification Stage and a Rust façade symbol, and concludes with the rustdoc skeleton of the core API.

It makes the goal of “exposing all io_uring features” verifiable: every entry in the uapi header io_uring.h of v6.12 must appear here with a decision. This document is the entry point to validate before producing the detailed per-Stage specs and before the implementation (function bodies) is handed off.

Relation to existing documents. This document replaces the inventory role of io-uring-overview.md (calibrated against a ~5.15 kernel, now incomplete). Structural decisions remain those of ADR-022; the two soundness decisions in section 3 extend ADR-022 and will be the subject of an RFC amendment (see §3.4).


1. Frozen Kernel Target and Methodology

1.1 Scope Decision

  • Baseline = Linux 6.12 LTS. Everything predating 6.12 is dropped at the outset. Paths for older kernels will only be implemented if there is an explicit request (and then via feature-gate + documented fallback).
  • Source of truth = the include/uapi/linux/io_uring.h header from the v6.12 tag. Every symbol present in that header is in scope; any symbol present only in master (later) is out of scope at startup and will only be added later, via runtime detection, without amendment (ADR-022, future status).
  • A single façade: the modern one. When kernel 6.12 already offers a modern variant of a feature, Air exposes only the modern one. The legacy variant is recorded in docs/UNSUPPORTED.md with justification (consistent with “modern variants preferred” from CLAUDE.md). See §4.

1.2 Out of Startup Scope (present in master, absent from 6.12)

Listed here for reference, to avoid accidentally reintroducing them:

  • Opcodes: RECV_ZC, EPOLL_WAIT, READV_FIXED, WRITEV_FIXED, PIPE, NOP128, URING_CMD128.
  • Register opcodes: SEND_MSG_RING, ZCRX_IFQ, ZCRX_CTRL, RESIZE_RINGS, MEM_REGION, QUERY, BPF_FILTER.
  • Setup flags: HYBRID_IOPOLL, CQE_MIXED, SQE_MIXED, SQ_REWIND.
  • CQE flags: IORING_CQE_F_SKIP, IORING_CQE_F_32.
  • Enter flags: EXT_ARG_REG, NO_IOWAIT.
  • Features: RW_ATTR, NO_IOWAIT.
  • uring_cmd socket op: SOCKET_URING_OP_TX_TIMESTAMP, SOCKET_URING_OP_GETSOCKNAME (6.12 only has SIOCINQ/SIOCOUTQ/GET/SETSOCKOPT).

2. The io_uring Model in Brief (Orientation Recap)

io_uring is built on three syscalls and a protocol of mmapped rings:

SyscallRole
io_uring_setup(entries, params)Creates the ring, returns an FD, fills in the mmap offsets.
io_uring_enter(fd, to_submit, min_complete, flags, arg, argsz)Submits SQEs and/or waits for CQEs.
io_uring_register(fd, opcode, arg, nr_args)Registers/configures resources (buffers, files, restrictions, …).

Three mmapped regions: the submission queue (SQ), the completion queue (CQ), and the SQE array (64 bytes, or 128 with SETUP_SQE128). The CQE is 16 bytes (or 32 with SETUP_CQE32). The user_data coupling (written into the SQE, returned in the CQE) is the thread linking a submission to its completion — it is the pivot of the Air façade (see §3.1).

The functional surface breaks down into 8 enumerable axes; sections 5.1 through 5.8 cover them exhaustively.

2.1 Terminology and Abbreviations (reference)

io_uring uses standardized abbreviations (kernel, man pages, liburing). This table defines them once and for all; the Air documentation prefers the spelled-out form and only uses the abbreviations where they correspond to a literal kernel ABI name (which is never renamed, for accuracy).

Abbrev.Full formStatus in the Air façade
SQsubmission queueabbreviation = kernel ring; façade is spelled out (submission_queue_*)
CQcompletion queueabbreviation = kernel ring; façade is spelled out (completion_queue_*)
SQEsubmission queue entryfaçade type RawSubmissionQueueEntry; kernel struct io_uring_sqe (kept)
CQEcompletion queue entryfaçade type RawCompletionQueueEntry; kernel struct io_uring_cqe (kept)
FDfile descriptorstd types OwnedFd/BorrowedFd/RawFd (kept)
ZCzero-copyfaçade spelled out: submit_send_zero_copy, ZeroCopyFlags, into_zero_copy_buffer
op / opcodeoperation codefaçade spelled out: IoUringOpcode, RawOpcode
SQPOLLsubmission-queue pollingkernel flag name IORING_SETUP_SQPOLL (kept); type SqpollIoUring
NAPINew API (network busy-poll)kernel mechanism name (kept)
sq_entries / cq_entriessubmission/completion queue depthABI fields of io_uring_params (kept verbatim)

Applied rule. Air façade identifiers are spelled out (submission_queue_*, completion_queue_*, IoUringOpcode, ZeroCopyFlags, in_flight, etc.) — verbose and unambiguous for the developer (Principle 7). Kernel ABI names (io_uring_sqe, io_uring_cqe, IORING_*, sqe->res, cq_entries…) are kept verbatim: renaming them would diverge from the kernel and the man pages, hence less precision, not more.

⚠️ Factual note. The “S” in SQ/SQE stands for Submission, not “Send”. SQ = submission queue, SQE = submission queue entry.


3. Frozen Soundness Decisions (Extending ADR-022)

These decisions determine all façade signatures. They are frozen before writing any function body.

3.1 Decision S1 — In-flight Operation State: Pre-allocated Slab

Problem. The “ownership transfer” model (ADR-022, Decision 3) takes the buffer as an argument and returns it at completion. Between submission and completion, {buffer, metadata, op type} must be parked somewhere, retrievable by user_data. A HashMap<u64, _> would allocate per operation — which violates the CLAUDE.md rule “no heap allocation in the happy path.”

Decision. The IoUring owns a pre-allocated slab sized to the SQ depth (sq_entries). Each in-flight operation occupies a slot. The SubmissionToken encapsulates a slot index + generation (compteur de génération anti-réutilisation), not a raw user_data. The kernel-side user_data = the encoded slot index.

Consequences:

  • Zero allocation per operation in the happy path: the transferred buffer is moved into the slot, not copied or reallocated.
  • Natural back-pressure: slab full ⇒ submit_* returns Err(Errno::EBUSY) (or a dedicated error type) before reaching the kernel.
  • The slot is freed when the corresponding completion is consumed and ownership of the buffer is returned to the caller.

3.2 Decision S2 — Safe Teardown: Drop Quiesces, Explicit shutdown()

Problem. If the IoUring (or an in-flight buffer) is dropped while the kernel still has operations in progress, the kernel may write into freed memory. This is the soundness hazard of io_uring + Rust. ADR-022 did not address it.

Decision (faithful to “over-secure then trim after measurement,” Principle 5).

  • shutdown(self) -> Result<(), Errno>: the clean, explicit path. Issues a global cancellation (IORING_REGISTER_SYNC_CANCEL or ASYNC_CANCEL_ANY), drains remaining completions, then closes the FD. Does not block indefinitely (timeout).
  • Drop: safety net. If operations are still in flight, Drop quiesces (cancels + drains in a blocking fashion) before releasing memory. The cost (potential blocking in Drop) is acknowledged and documented; performance-conscious users call shutdown() explicitly.
  • The slots from Decision S1 retain ownership of buffers until the op completes ⇒ the buffer cannot be freed before the kernel is done with it.

3.3 Decision S3 — Restrictions & Sandbox as a Capability Primitive

IORING_REGISTER_RESTRICTIONS + SETUP_R_DISABLED + REGISTER_ENABLE_RINGS form a confinement primitive: create a disabled ring, restrict the allowed opcodes/flags/register-ops, then enable it. This maps directly onto the signed-entitlement model of ADR-010 and the AirCom capabilities (ADR-001).

Decision. This primitive is treated as a first-class citizen (Stage 3f, see §6), not buried in generic registration. The façade exposes an IoUringBuilder constructor capable of applying a set of restrictions before activation, so that a confined Air service receives a ring whose kernel guarantees it can only issue the operations authorized by its manifest.

3.4 RFC Status

S1, S2, S3 extend ADR-022 without contradicting its 10 decisions. They are recorded in ADR-028 (“Soundness and teardown of the io_uring module”), a supplement to ADR-022 — see ../../adrs/ADR-028-soundness-io-uring-en.md.


4. “No Legacy” Rule: Concrete Decisions

Legacy variants dropped to UNSUPPORTED.md (kernel 6.12 already offers the modern version):

Legacy (dropped)Modern replacement (exposed)Reason
IORING_REGISTER_BUFFERS (reg 0)IORING_REGISTER_BUFFERS2 (reg 15) + BUFFERS_UPDATE (16)Resource tagging (FEAT_RSRC_TAGS), sparse, partial update.
IORING_REGISTER_FILES (reg 2)IORING_REGISTER_FILES2 (reg 13) + FILES_UPDATE2 (14)Same; struct io_uring_files_update is marked deprecated in the header.
IORING_REGISTER_FILES_UPDATE (reg 6)IORING_REGISTER_FILES_UPDATE2 (reg 14)Same.
IORING_OP_PROVIDE_BUFFERS (op 31)IORING_REGISTER_PBUF_RING (reg 22)Ring-mapped provided buffers, no syscall per batch, incremental consumption (IOU_PBUF_RING_INC).
IORING_OP_REMOVE_BUFFERS (op 32)IORING_UNREGISTER_PBUF_RING (reg 23)Same.
SQE field poll_events (__u16)poll32_events (__u32)The header notes “compatibility”; only the 32-bit version is exposed.

Note: UNREGISTER_BUFFERS (1) and UNREGISTER_FILES (3) remain exposed — they are the deregistration counterparts for resources registered via the *2 variants. EVENTFD_ASYNC (7) and EPOLL_CTL (op 29) remain (not obsolete: respectively a useful variant and the only epoll integration in 6.12).


5. Exhaustive Inventory of the 8 Axes

Legend for the “Decision” column: R = retained/exposed · O = obsolete, dropped to UNSUPPORTED.md · G = runtime-gated (probe/feature, exposed but fallible).

5.1 Axis A — Setup Flags IORING_SETUP_* (17 in 6.12)

Exposed as bitflags SetupFlags, applied via IoUringBuilder.

FlagBitDecisionFaçade symbol / note
IOPOLL0RSetupFlags::IOPOLL — I/O polling (O_DIRECT storage).
SQPOLL1RSetupFlags::SQPOLL — kernel thread polling the SQ (Stage 3e).
SQ_AFF2RSetupFlags::SQ_AFF — CPU affinity for the SQPOLL thread.
CQSIZE3RIoUringBuilder::cq_entries().
CLAMP4RSetupFlags::CLAMP.
ATTACH_WQ5RIoUringBuilder::attach_work_queue(&IoUring) — shares the io-wq pool.
R_DISABLED6Rsandbox primitive (Stage 3f, §3.3).
SUBMIT_ALL7RSetupFlags::SUBMIT_ALL.
COOP_TASKRUN8RSetupFlags::COOP_TASKRUN.
TASKRUN_FLAG9RSetupFlags::TASKRUN_FLAG.
SQE12810Rrequired for URING_CMD with large payload (Stage 2d, Stage 4).
CQE3211Rrequired for certain extended completions.
SINGLE_ISSUER12RAir recommended: thread-per-core reactor.
DEFER_TASKRUN13RAir recommended (combined with SINGLE_ISSUER): low latency.
NO_MMAP14Rring memory provided by the caller.
REGISTERED_FD_ONLY15Rring fd as registered-only (combines REG_REG_RING).
NO_SQARRAY16Rremoves the SQ index-array indirection.

5.2 Axis B — Operations IORING_OP_* (58 in 6.12, indices 0–57)

“Stage” column = destination detailed spec (see §6).

Opcode#DecisionStageFaçade symbol
NOP0R2csubmit_nop
READV1R2asubmit_readv
WRITEV2R2asubmit_writev
FSYNC3R2asubmit_fsync
READ_FIXED4R2a/3asubmit_read_fixed (registered buffer)
WRITE_FIXED5R2a/3asubmit_write_fixed
POLL_ADD6R2csubmit_poll_add (+ multi/level flags)
POLL_REMOVE7R2csubmit_poll_remove
SYNC_FILE_RANGE8R2asubmit_sync_file_range
SENDMSG9R2bsubmit_send_message
RECVMSG10R2bsubmit_receive_message
TIMEOUT11R2csubmit_timeout
TIMEOUT_REMOVE12R2csubmit_timeout_remove / update
ACCEPT13R2bsubmit_accept (+ multishot Stage 3d)
ASYNC_CANCEL14R2csubmit_cancel
LINK_TIMEOUT15R2c/3csubmit_link_timeout
CONNECT16R2bsubmit_connect
FALLOCATE17R2asubmit_fallocate
OPENAT18Odropped → OPENAT2 (superset, OpenHow).
CLOSE19R2asubmit_close
FILES_UPDATE20R2c/3asubmit_files_update (op, distinct from register)
STATX21R2asubmit_statx
READ22R2asubmit_read
WRITE23R2asubmit_write
FADVISE24R2asubmit_fadvise
MADVISE25R2asubmit_madvise
SEND26R2bsubmit_send
RECV27R2bsubmit_receive (+ multishot Stage 3d)
OPENAT228R2asubmit_openat2
EPOLL_CTL29R2csubmit_epoll_ctl
SPLICE30R2asubmit_splice
PROVIDE_BUFFERS31Odropped → PBUF_RING (reg 22).
REMOVE_BUFFERS32Odropped → UNREGISTER_PBUF_RING.
TEE33R2asubmit_tee
SHUTDOWN34R2bsubmit_shutdown
RENAMEAT35R2asubmit_renameat (renameat2 semantics)
UNLINKAT36R2asubmit_unlinkat
MKDIRAT37R2asubmit_mkdirat
SYMLINKAT38R2asubmit_symlinkat
LINKAT39R2asubmit_linkat
MSG_RING40R2csubmit_msg_ring (data / send_fd)
FSETXATTR41R2asubmit_fsetxattr
SETXATTR42R2asubmit_setxattr
FGETXATTR43R2asubmit_fgetxattr
GETXATTR44R2asubmit_getxattr
SOCKET45R2bsubmit_socket
URING_CMD46R2dsubmit_uring_cmd (+ socket cmds)
SEND_ZC47R2bsubmit_send_zero_copy (zero-copy, CQE NOTIF)
SENDMSG_ZC48R2bsubmit_send_message_zero_copy
READ_MULTISHOT49R3dsubmit_read_multishot
WAITID50R2csubmit_waitid
FUTEX_WAIT51R2csubmit_futex_wait
FUTEX_WAKE52R2csubmit_futex_wake
FUTEX_WAITV53R2csubmit_futex_waitv
FIXED_FD_INSTALL54R2c/3asubmit_fixed_fd_install
FTRUNCATE55R2asubmit_ftruncate
BIND56R2bsubmit_bind
LISTEN57R2bsubmit_listen

Opcode summary: 55 retained, 3 dropped (OPENAT, PROVIDE_BUFFERS, REMOVE_BUFFERS).

5.3 Axis C — Register Opcodes IORING_REGISTER_* (31 in 6.12, 0–30)

Register op#DecisionStageFaçade symbol
REGISTER_BUFFERS0OBUFFERS2.
UNREGISTER_BUFFERS1R3aRegisteredBuffers::unregister
REGISTER_FILES2OFILES2.
UNREGISTER_FILES3R3aFixedFdTable::unregister
REGISTER_EVENTFD4R3aIoUring::register_eventfd
UNREGISTER_EVENTFD5R3aIoUring::unregister_eventfd
REGISTER_FILES_UPDATE6OFILES_UPDATE2.
REGISTER_EVENTFD_ASYNC7R3aregister_eventfd_async
REGISTER_PROBE8R1IoUring::probe / supports_op
REGISTER_PERSONALITY9R3aIoUring::register_personality
UNREGISTER_PERSONALITY10R3aunregister_personality
REGISTER_RESTRICTIONS11R3fIoUringBuilder::restrict (§3.3)
REGISTER_ENABLE_RINGS12R3fIoUringBuilder::enable
REGISTER_FILES213R3aFixedFdTable::register
REGISTER_FILES_UPDATE214R3aFixedFdTable::update
REGISTER_BUFFERS215R3aRegisteredBuffers::register
REGISTER_BUFFERS_UPDATE16R3aRegisteredBuffers::update
REGISTER_IOWQ_AFF17R3aIoUring::set_work_queue_affinity
UNREGISTER_IOWQ_AFF18R3aclear_work_queue_affinity
REGISTER_IOWQ_MAX_WORKERS19R3aset_work_queue_max_workers
REGISTER_RING_FDS20R3aIoUring::register_ring_fd
UNREGISTER_RING_FDS21R3aunregister_ring_fd
REGISTER_PBUF_RING22R3bProvidedBufferRing::register
UNREGISTER_PBUF_RING23R3bProvidedBufferRing::unregister
REGISTER_SYNC_CANCEL24R1/2cIoUring::sync_cancel (used by S2)
REGISTER_FILE_ALLOC_RANGE25R3aFixedFdTable::set_alloc_range
REGISTER_PBUF_STATUS26R3bProvidedBufferRing::status
REGISTER_NAPI27R3aIoUring::register_napi
UNREGISTER_NAPI28R3aunregister_napi
REGISTER_CLOCK29R3aIoUring::register_clock
REGISTER_CLONE_BUFFERS30R3aRegisteredBuffers::clone_from
USE_REGISTERED_RING (flag 1<<31)R1applied transparently when the ring fd is registered.

Register summary: 28 retained, 3 dropped (REGISTER_BUFFERS, REGISTER_FILES, REGISTER_FILES_UPDATE).

5.4 Axis D — Per-SQE Flags IOSQE_* (7)

FlagDecisionFaçade symbol
FIXED_FILERimplicit via the “fixed” variants (registered FD).
IO_DRAINRSubmitOptions::drain().
IO_LINKRLinkedChainBuilder (Stage 3c).
IO_HARDLINKRLinkedChainBuilder::add_hard_link.
ASYNCRSubmitOptions::force_async().
BUFFER_SELECTRimplicit via ProvidedBufferRing (Stage 3b).
CQE_SKIP_SUCCESSRSubmitOptions::skip_cqe_on_success() (FEAT_CQE_SKIP).

5.5 Axis E — CQE Flags IORING_CQE_F_* (5 in 6.12)

FlagDecisionExposure
BUFFERRCompletion::buffer_id() -> Option<u16>.
MORERCompletion::has_more() (multishot, Stage 3d).
SOCK_NONEMPTYRCompletion::socket_has_pending_data().
NOTIFRCompletion::is_notif() (zero-copy send, Stage 2b).
BUF_MORERincremental consumption (IOU_PBUF_RING_INC, Stage 3b).

5.6 Axis F — io_uring_enter Flags (6 in 6.12)

Managed internally by the façade (not exposed raw):

FlagDecisionInternal usage
GETEVENTSRsubmit_and_wait, wait_completion.
SQ_WAKEUPRwakeup of the SQPOLL thread (Stage 3e).
SQ_WAITRwait for the SQ to drain (SQPOLL).
EXT_ARGRwait timeout (getevents_arg), wait_completion_timeout.
REGISTERED_RINGRapplied if ring fd is registered (transparent).
ABS_TIMERRabsolute timeout (FEAT_MIN_TIMEOUT).

5.7 Axis G — Features IORING_FEAT_* (16 in 6.12)

Read from io_uring_params.features after setup, exposed via IoUringCapabilities:

SINGLE_MMAP, NODROP, SUBMIT_STABLE, RW_CUR_POS, CUR_PERSONALITY, FAST_POLL, POLL_32BITS, SQPOLL_NONFIXED, EXT_ARG, NATIVE_WORKERS, RSRC_TAGS, CQE_SKIP, LINKED_FILE, REG_REG_RING, RECVSEND_BUNDLE, MIN_TIMEOUT. → all R, mapped to IoUringCapabilities::* predicates.

5.8 Axis H — Raw Ring Protocol (Stage 4)

Kernel structures exposed as #[repr(C)] in ::raw, plus ring flags:

  • io_uring_sqe (64 b / 128 b), io_uring_cqe (16 b / 32 b).
  • io_sqring_offsets, io_cqring_offsets, io_uring_params.
  • SQ ring flags: SQ_NEED_WAKEUP, SQ_CQ_OVERFLOW, SQ_TASKRUN.
  • CQ ring flag: CQ_EVENTFD_DISABLED.
  • mmap offsets: OFF_SQ_RING, OFF_CQ_RING, OFF_SQES, OFF_PBUF_RING.
  • Register argument structures: io_uring_rsrc_register, io_uring_rsrc_update(2), io_uring_buf(_ring|_reg|_status), io_uring_napi, io_uring_clock_register, io_uring_clone_buffers, io_uring_sync_cancel_reg, io_uring_file_index_range, io_uring_probe(_op), io_uring_restriction, io_uring_getevents_arg, io_uring_recvmsg_out.
  • Enums: io_uring_op, io_uring_register_op, io_uring_register_restriction_op, io_uring_msg_ring_flags, io_uring_socket_op, io_uring_register_pbuf_ring_flags, io_wq_type.

6. Revised Stage Breakdown

The breakdown from ADR-022 is retained and extended (additions: 2d uring_cmd, separate 3b provided-buffers-ring, 3f restrictions/sandbox):

StageSub-moduleScopeSymbols
1(root)Ring lifecycle, setup flags, slab (S1), Drop/shutdown (S2), submission/completion, Completion, probe/capabilities.~20
2a(root)Filesystem (read/write/sync/open/stat/dir/perm/xattr/splice/ftruncate…).26
2b(root)Network (accept/connect/send/recv/msg/shutdown/socket/bind/listen + zero-copy send/sendmsg).12
2c(root)Async-specific (nop/timeout/cancel/poll/futex/waitid/msg_ring/epoll_ctl/files_update/fixed_fd_install).15
2d::cmdURING_CMD: passthrough + socket commands (SIOCINQ/OUTQ, get/setsockopt).3–5
3a::registrationModern fixed resources: files2/buffers2/update, ring_fds, eventfd, personality, iowq aff/workers, napi, clock, clone_buffers, alloc_range.~25
3b::providedRing-mapped provided buffers (PBUF_RING/STATUS, incremental).6–8
3c::linkedLinked chains (soft/hard link, link_timeout).5–7
3d::multishotMultishot (accept/recv/poll/read_multishot).5–7
3e::sharedMulti-thread: LockedIoUring, RingPool, SqpollIoUring.10–12
3f::sandboxRestrictions + R_DISABLED + ENABLE_RINGS (capability, §3.3).6–8
4::rawRaw protocol (SQE/CQE, rings, register structs).8–12

7. Core Rustdoc Skeleton (Stage 1) — To Validate Before Implementation

This skeleton is the contract that Claude Code will implement (bodies = todo!()). The exhaustive per-opcode submit_* functions (Stages 2a–2d) and the sub-modules (3a–3f, 4) are produced in their dedicated specs on the same model. Layer-0 conventions (ADR-021) applied: Result<_, Errno>, EINTR propagated to caller, Option<T> instead of sentinels, FDs as OwnedFd/BorrowedFd, no heap allocation in the happy path (S1).

#![allow(unused)]
fn main() {
//! Module `air-sys-syscall::io_uring` — typed io_uring façade (target 6.12).
//!
//! Abstraction level 2 (ADR-022, Decision 1): typed submission/completion.
//! Level 1 (raw rings) lives in [`raw`]. Buffers follow the ownership transfer
//! model (ADR-022, Decision 3), parked in a pre-allocated slab (S1); teardown
//! is safe via quiescent `Drop` + [`IoUring::shutdown`] (S2).

use crate::Errno;
use core::num::NonZeroU32;
use std::os::fd::{BorrowedFd, OwnedFd};

// ---------------------------------------------------------------------------
// Construction & lifecycle
// ---------------------------------------------------------------------------

/// io_uring ring. `Send` but not `Sync` (ADR-022, Decision 6): one reactor
/// per thread. For multi-thread use, see [`shared`].
pub struct IoUring {
    /* fd: OwnedFd, SQ/CQ/SQE mmaps, in-flight op slab (S1), capabilities */
}

/// Builds an [`IoUring`], applying setup flags and restrictions (S3) before
/// activation.
pub struct IoUringBuilder { /* ... */ }

impl IoUringBuilder {
    /// Starts a builder for `entries` SQEs (rounded up to the next power of two).
    pub fn new(entries: NonZeroU32) -> Self { todo!() }

    /// Explicit CQ size (`SETUP_CQSIZE`).
    pub fn with_completion_queue_entries(self, entries: NonZeroU32) -> Self { todo!() }

    /// Enables setup flags (see [`SetupFlags`]).
    pub fn with_flags(self, flags: SetupFlags) -> Self { todo!() }

    /// Shares the io-wq pool of an existing ring (`SETUP_ATTACH_WQ`).
    pub fn attach_work_queue(self, other: &IoUring) -> Self { todo!() }

    /// Creates the ring in disabled state (`R_DISABLED`) and applies
    /// restrictions (S3). Must be followed by [`IoUringBuilder::enable`].
    pub fn restrict(self, restrictions: &[Restriction]) -> Self { todo!() }

    /// Finalizes: `io_uring_setup(2)`. If `restrict` was used, the ring is
    /// created disabled and must be activated via [`IoUring::enable`].
    pub fn build(self) -> Result<IoUring, Errno> { todo!() }
}

impl IoUring {
    /// Shortcut: `IoUringBuilder::new(entries).build()`.
    pub fn new(entries: NonZeroU32) -> Result<Self, Errno> { todo!() }

    /// Activates a ring created with `R_DISABLED` (`REGISTER_ENABLE_RINGS`).
    pub fn enable(&mut self) -> Result<(), Errno> { todo!() }

    /// Clean teardown (S2): cancels in-flight ops, drains, closes the FD.
    /// Prefer over an implicit `drop` on the hot path.
    pub fn shutdown(self) -> Result<(), Errno> { todo!() }
}

impl Drop for IoUring {
    /// Safety net (S2): if operations are still in flight, quiesces (cancels +
    /// drains, **blocking**) before releasing memory. Cost is acknowledged.
    fn drop(&mut self) { todo!() }
}

// ---------------------------------------------------------------------------
// Submission & completion
// ---------------------------------------------------------------------------

impl IoUring {
    /// Submits pending SQEs (`io_uring_enter`, without waiting). Returns the
    /// number submitted. EINTR propagated to caller (ADR-021, conv. 2).
    pub fn submit(&mut self) -> Result<u32, Errno> { todo!() }

    /// Submits then waits for at least `want` completions.
    pub fn submit_and_wait(&mut self, want: u32) -> Result<u32, Errno> { todo!() }

    /// Waits (blocking) for the next completion and consumes it.
    pub fn wait_completion(&mut self) -> Result<Completion, Errno> { todo!() }

    /// Waits for a completion with a maximum deadline (`EXT_ARG`/`ABS_TIMER`).
    pub fn wait_completion_timeout(
        &mut self,
        timeout: core::time::Duration,
    ) -> Result<Option<Completion>, Errno> { todo!() }

    /// Returns a completion if one is available, without blocking.
    pub fn try_completion(&mut self) -> Option<Completion> { todo!() }

    /// Iterates over available completions in the CQ.
    pub fn completions(&mut self) -> CompletionIter<'_> { todo!() }

    /// Per-operation options (link, drain, async, skip-cqe) applied to the
    /// next submission.
    pub fn with(&mut self, opts: SubmitOptions) -> &mut Self { todo!() }
}

/// Opaque token linking a submission to its completion. Encapsulates a slab
/// slot index + generation (compteur de génération anti-réutilisation) — *not* a raw `user_data` (S1).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SubmissionToken { /* slot: u32, gen: u32 */ }

/// Token for a multishot operation (multiple completions). See [`multishot`].
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MultishotToken { /* ... */ }

/// Submission options (`IOSQE_*` flags exposed safely).
#[derive(Debug, Clone, Copy, Default)]
pub struct SubmitOptions { /* ... */ }

impl SubmitOptions {
    pub fn drain(self) -> Self { todo!() }
    pub fn force_async(self) -> Self { todo!() }
    pub fn skip_cqe_on_success(self) -> Self { todo!() }
}

// ---------------------------------------------------------------------------
// Completion
// ---------------------------------------------------------------------------

/// A typed completion (CQE). Interpretation methods depend on the submitted op
/// (ADR-022, Decision 9); on failure they return `Err(Errno)`.
pub struct Completion { /* decoded user_data, res, flags */ }

impl Completion {
    /// Token of the operation that produced this completion.
    pub fn token(&self) -> SubmissionToken { todo!() }
    /// Raw kernel result (semantics are op-dependent).
    pub fn raw_result(&self) -> i32 { todo!() }
    /// Completion flags (see [`CompletionFlags`]).
    pub fn flags(&self) -> CompletionFlags { todo!() }

    /// `IORING_CQE_F_MORE`: more completions will follow (multishot).
    pub fn has_more(&self) -> bool { todo!() }
    /// `IORING_CQE_F_NOTIF`: zero-copy notification completion.
    pub fn is_notif(&self) -> bool { todo!() }
    /// ID of the consumed provided buffer (`IORING_CQE_F_BUFFER`).
    pub fn buffer_id(&self) -> Option<u16> { todo!() }
    /// `IORING_CQE_F_SOCK_NONEMPTY`: data remaining after a recv.
    pub fn socket_has_pending_data(&self) -> bool { todo!() }

    // Typed interpretations (sample; completed by Stages 2a–2d):
    /// Bytes read + return of the transferred buffer (S1).
    pub fn into_read_result(self) -> Result<(Vec<u8>, usize), Errno> { todo!() }
    /// Bytes written + return of the buffer.
    pub fn into_write_result(self) -> Result<(Vec<u8>, usize), Errno> { todo!() }
    /// Accepted FD (`submit_accept`), CLOEXEC by default.
    pub fn accepted_fd(self) -> Result<OwnedFd, Errno> { todo!() }
    /// Opened FD (`submit_openat2`).
    pub fn opened_fd(self) -> Result<OwnedFd, Errno> { todo!() }
    /// Success with no return value (close, fsync, …).
    pub fn completed(&self) -> Result<(), Errno> { todo!() }
}

/// Iterator over available completions.
pub struct CompletionIter<'ring> { /* ... */ }

// ---------------------------------------------------------------------------
// Capabilities & introspection
// ---------------------------------------------------------------------------

impl IoUring {
    /// True if the current kernel supports `op` (`REGISTER_PROBE`, Decision 8).
    pub fn supports_op(&self, op: IoUringOpcode) -> bool { todo!() }
    /// Features negotiated at setup (axis G).
    pub fn capabilities(&self) -> IoUringCapabilities { todo!() }
    /// Global or targeted synchronous cancellation (`REGISTER_SYNC_CANCEL`),
    /// building block of [`IoUring::shutdown`] (S2).
    pub fn sync_cancel(&mut self, target: CancelTarget) -> Result<u32, Errno> { todo!() }
}

/// io_uring features of the current kernel (axis G). Stable predicates.
#[derive(Debug, Clone, Copy)]
pub struct IoUringCapabilities { /* feature bits */ }

impl IoUringCapabilities {
    pub fn single_mmap(&self) -> bool { todo!() }
    pub fn nodrop(&self) -> bool { todo!() }
    pub fn cqe_skip(&self) -> bool { todo!() }
    pub fn min_timeout(&self) -> bool { todo!() }
    pub fn recvsend_bundle(&self) -> bool { todo!() }
    pub fn reg_reg_ring(&self) -> bool { todo!() }
    /* … one predicate per feature from axis G … */
}

/// Enumeration of operations (axis B) for probe and restrictions.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum IoUringOpcode { Nop, Readv, Writev, /* … 55 retained variants … */ }

/// Cancellation target for [`IoUring::sync_cancel`].
#[derive(Debug, Clone, Copy)]
pub enum CancelTarget {
    Token(SubmissionToken),
    Fd(BorrowedFd<'static>),
    Op(IoUringOpcode),
    Any,
}

// ---------------------------------------------------------------------------
// Bitflags & sandbox (Stage 3f, detailed in ::sandbox)
// ---------------------------------------------------------------------------

bitflags::bitflags! {
    /// `io_uring_setup` flags (axis A, 17 flags in 6.12).
    pub struct SetupFlags: u32 {
        const IOPOLL = 1 << 0;
        const SQPOLL = 1 << 1;
        const SQ_AFF = 1 << 2;
        const CLAMP = 1 << 4;
        const R_DISABLED = 1 << 6;
        const SUBMIT_ALL = 1 << 7;
        const COOP_TASKRUN = 1 << 8;
        const TASKRUN_FLAG = 1 << 9;
        const SQE128 = 1 << 10;
        const CQE32 = 1 << 11;
        const SINGLE_ISSUER = 1 << 12;
        const DEFER_TASKRUN = 1 << 13;
        const NO_MMAP = 1 << 14;
        const REGISTERED_FD_ONLY = 1 << 15;
        const NO_SQARRAY = 1 << 16;
        /* CQSIZE / ATTACH_WQ exposed via builder methods */
    }

    /// Completion flags (axis E, 5 flags in 6.12).
    pub struct CompletionFlags: u32 {
        const BUFFER = 1 << 0;
        const MORE = 1 << 1;
        const SOCK_NONEMPTY = 1 << 2;
        const NOTIF = 1 << 3;
        const BUF_MORE = 1 << 4;
    }
}

/// A restriction applied before activation (S3; `io_uring_restriction`).
#[derive(Debug, Clone, Copy)]
pub enum Restriction {
    /// Allow only this submission opcode.
    AllowOp(IoUringOpcode),
    /// Allow only this register op.
    AllowRegister(u8),
    /// SQE flags in the allowlist.
    SqeFlagsAllowed(SubmitOptions),
    /// SQE flags required on every submission.
    SqeFlagsRequired(SubmitOptions),
}
}

8. Prospective Cross-cutting Traits (To Be Decided in Stage 1)

To validate: is a common trait for submittable operations needed to factor out linked/multishot handling, or do inherent methods suffice (verbosity in service of clarity, Principle 7)?

  • trait Submittable — an operation that can be submitted (used by LinkedChainBuilder). Candidate; risk of premature abstraction.
  • trait FixedResource — a registerable resource (FD / buffer). Candidate.

Recommendation: do not introduce these traits in Stage 1. Add them only if Stages 3a/3c reveal the need through measured repetition.


9. Next Steps (Detailed Per-Stage Specs)

Produce, at the same level of detail as the other families (full signature, underlying syscall, preconditions/# Safety, behavior, errors, performance, tests, examples), the files: io-uring-1-core-en.md, io-uring-2a-filesystem-en.md, io-uring-2b-network-en.md, io-uring-2c-async-en.md, io-uring-2d-cmd-en.md, io-uring-3a-registration-en.md, io-uring-3b-provided-en.md, io-uring-3c-linked-en.md, io-uring-3d-multishot-en.md, io-uring-3e-shared-en.md, io-uring-3f-sandbox-en.md, io-uring-4-raw-en.md.

Then: the English version of this master document.


Document license: MPL 2.0 Status: Master inventory document for the air-sys-syscall::io_uring module, kernel target 6.12 LTS. To be validated before producing the detailed per-Stage specs.