Skip to main content

air_sys_types/
io_uring.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5//! Types de données **pures** du module `io_uring` (Temps 1, cible kernel 6.12).
6//!
7//! Ce module ne contient que les types à sémantique publique autonome, sans
8//! état interne consommé par le wrapper : les bitflags de setup
9//! ([`SetupFlags`]) et de complétion ([`CompletionFlags`]), et l'énumération
10//! des opcodes ([`IoUringOpcode`]). Les types couplés au wrapper
11//! (`IoUring`, `IoUringBuilder`, `SubmissionToken`, `SubmitOptions`,
12//! `IoUringParams`, `IoUringCapabilities`, `Completion`, `CompletionIter`,
13//! `CancelTarget`, `Restriction`) vivent dans `air-sys-syscall::io_uring`.
14//!
15//! > **Note de cadrage (Pass A) :** la spec §13 (`io-uring-1-core.md`) liste
16//! > l'ensemble de ces types « ajoutés à air-sys-types ». Plusieurs d'entre eux
17//! > exposent des internes consommés par le wrapper (décodage du `user_data`,
18//! > traduction `SubmitOptions`→SQE, champs ABI d'`io_uring_params`) : les
19//! > placer ici exigerait des accesseurs publics hors contrat. Ils sont donc
20//! > gardés côté wrapper au Temps 1 ; **placement final à valider**.
21//!
22//! Référence normative : `docs/specs/layer-0/io-uring-1-core.md`,
23//! `docs/specs/layer-0/io-uring-0-inventaire.md` §5.
24
25bitflags::bitflags! {
26    /// Flags de `io_uring_setup(2)` (axe A, 17 flags en 6.12).
27    ///
28    /// Appliqués via `IoUringBuilder`. Deux flags de l'axe A ne figurent pas
29    /// ici car exposés par des **méthodes** du builder : `CQSIZE`
30    /// (`with_completion_queue_entries`) et `ATTACH_WQ` (`attach_work_queue`).
31    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
32    pub struct SetupFlags: u32 {
33        /// `IORING_SETUP_IOPOLL` : polling I/O (stockage `O_DIRECT`).
34        const IOPOLL = 1 << 0;
35        /// `IORING_SETUP_SQPOLL` : thread kernel de poll de la SQ (Temps 3e).
36        const SQPOLL = 1 << 1;
37        /// `IORING_SETUP_SQ_AFF` : affinité CPU du thread SQPOLL.
38        const SQ_AFF = 1 << 2;
39        /// `IORING_SETUP_CLAMP` : borne `entries` aux limites kernel.
40        const CLAMP = 1 << 4;
41        /// `IORING_SETUP_R_DISABLED` : ring créé désactivé (primitive sandbox, Temps 3f).
42        const R_DISABLED = 1 << 6;
43        /// `IORING_SETUP_SUBMIT_ALL` : ne pas s'arrêter à la première erreur de soumission.
44        const SUBMIT_ALL = 1 << 7;
45        /// `IORING_SETUP_COOP_TASKRUN` : task-work coopératif (moins d'IPI).
46        const COOP_TASKRUN = 1 << 8;
47        /// `IORING_SETUP_TASKRUN_FLAG` : signale le task-work en attente via un flag d'anneau.
48        const TASKRUN_FLAG = 1 << 9;
49        /// `IORING_SETUP_SQE128` : SQE de 128 octets (requis pour `URING_CMD` large).
50        const SQE128 = 1 << 10;
51        /// `IORING_SETUP_CQE32` : CQE de 32 octets (complétions étendues).
52        const CQE32 = 1 << 11;
53        /// `IORING_SETUP_SINGLE_ISSUER` : un seul thread soumetteur (recommandé Air).
54        const SINGLE_ISSUER = 1 << 12;
55        /// `IORING_SETUP_DEFER_TASKRUN` : task-work différé à `GETEVENTS` (latence basse).
56        const DEFER_TASKRUN = 1 << 13;
57        /// `IORING_SETUP_NO_MMAP` : mémoire des anneaux fournie par l'appelant (avancé, gated).
58        const NO_MMAP = 1 << 14;
59        /// `IORING_SETUP_REGISTERED_FD_ONLY` : ring fd uniquement enregistré.
60        const REGISTERED_FD_ONLY = 1 << 15;
61        /// `IORING_SETUP_NO_SQARRAY` : supprime l'indirection du tableau d'index SQ.
62        const NO_SQARRAY = 1 << 16;
63    }
64
65    /// Flags de complétion `IORING_CQE_F_*` (axe E, 5 flags en 6.12).
66    ///
67    /// Exposés de façon typée par les méthodes de `Completion`
68    /// (`has_more`, `is_notif`, `buffer_id`, `socket_has_pending_data`).
69    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
70    pub struct CompletionFlags: u32 {
71        /// `IORING_CQE_F_BUFFER` : un buffer fourni a été consommé (id dans les bits hauts).
72        const BUFFER = 1 << 0;
73        /// `IORING_CQE_F_MORE` : d'autres complétions suivront (multishot, Temps 3d).
74        const MORE = 1 << 1;
75        /// `IORING_CQE_F_SOCK_NONEMPTY` : données restantes lisibles après un `recv`.
76        const SOCK_NONEMPTY = 1 << 2;
77        /// `IORING_CQE_F_NOTIF` : complétion de notification zero-copy (Temps 2b).
78        const NOTIF = 1 << 3;
79        /// `IORING_CQE_F_BUF_MORE` : consommation incrémentale d'un buffer (Temps 3b).
80        const BUF_MORE = 1 << 4;
81    }
82}
83
84/// Énumération des 55 opcodes `IORING_OP_*` retenus (axe B, 6.12).
85///
86/// Sert au probe (`IoUring::supports_op`) et aux restrictions (S3). Les 3
87/// opcodes obsolètes (`OPENAT`, `PROVIDE_BUFFERS`, `REMOVE_BUFFERS`) sont
88/// évacués (cf. `UNSUPPORTED.md`) et **absents** de cette énumération.
89/// `#[non_exhaustive]` : de futurs kernels peuvent en ajouter.
90#[derive(Debug, Clone, Copy, PartialEq, Eq)]
91#[non_exhaustive]
92pub enum IoUringOpcode {
93    /// `IORING_OP_NOP` (0) — no-op (validation du cœur, Temps 2c).
94    Nop,
95    /// `IORING_OP_READV` (1) — lecture vectorisée.
96    Readv,
97    /// `IORING_OP_WRITEV` (2) — écriture vectorisée.
98    Writev,
99    /// `IORING_OP_FSYNC` (3) — synchronisation fichier.
100    Fsync,
101    /// `IORING_OP_READ_FIXED` (4) — lecture vers buffer enregistré.
102    ReadFixed,
103    /// `IORING_OP_WRITE_FIXED` (5) — écriture depuis buffer enregistré.
104    WriteFixed,
105    /// `IORING_OP_POLL_ADD` (6) — ajout d'un poll.
106    PollAdd,
107    /// `IORING_OP_POLL_REMOVE` (7) — retrait d'un poll.
108    PollRemove,
109    /// `IORING_OP_SYNC_FILE_RANGE` (8) — synchronisation d'une plage de fichier.
110    SyncFileRange,
111    /// `IORING_OP_SENDMSG` (9) — envoi de message socket.
112    Sendmsg,
113    /// `IORING_OP_RECVMSG` (10) — réception de message socket.
114    Recvmsg,
115    /// `IORING_OP_TIMEOUT` (11) — minuterie.
116    Timeout,
117    /// `IORING_OP_TIMEOUT_REMOVE` (12) — retrait/màj de minuterie.
118    TimeoutRemove,
119    /// `IORING_OP_ACCEPT` (13) — acceptation de connexion.
120    Accept,
121    /// `IORING_OP_ASYNC_CANCEL` (14) — annulation asynchrone.
122    AsyncCancel,
123    /// `IORING_OP_LINK_TIMEOUT` (15) — timeout lié à l'opération précédente.
124    LinkTimeout,
125    /// `IORING_OP_CONNECT` (16) — connexion socket.
126    Connect,
127    /// `IORING_OP_FALLOCATE` (17) — pré-allocation d'espace fichier.
128    Fallocate,
129    /// `IORING_OP_CLOSE` (19) — fermeture de FD.
130    Close,
131    /// `IORING_OP_FILES_UPDATE` (20) — mise à jour de la table de FD fixes (op).
132    FilesUpdate,
133    /// `IORING_OP_STATX` (21) — métadonnées de fichier étendues.
134    Statx,
135    /// `IORING_OP_READ` (22) — lecture.
136    Read,
137    /// `IORING_OP_WRITE` (23) — écriture.
138    Write,
139    /// `IORING_OP_FADVISE` (24) — conseils d'accès fichier.
140    Fadvise,
141    /// `IORING_OP_MADVISE` (25) — conseils d'accès mémoire.
142    Madvise,
143    /// `IORING_OP_SEND` (26) — envoi socket.
144    Send,
145    /// `IORING_OP_RECV` (27) — réception socket.
146    Recv,
147    /// `IORING_OP_OPENAT2` (28) — ouverture de fichier (superset d'`openat`).
148    Openat2,
149    /// `IORING_OP_EPOLL_CTL` (29) — contrôle d'un epoll.
150    EpollCtl,
151    /// `IORING_OP_SPLICE` (30) — copie zero-copy entre FDs.
152    Splice,
153    /// `IORING_OP_TEE` (33) — duplication zero-copy entre pipes.
154    Tee,
155    /// `IORING_OP_SHUTDOWN` (34) — arrêt d'un socket.
156    Shutdown,
157    /// `IORING_OP_RENAMEAT` (35) — renommage (sémantique `renameat2`).
158    Renameat,
159    /// `IORING_OP_UNLINKAT` (36) — suppression d'entrée de répertoire.
160    Unlinkat,
161    /// `IORING_OP_MKDIRAT` (37) — création de répertoire.
162    Mkdirat,
163    /// `IORING_OP_SYMLINKAT` (38) — création de lien symbolique.
164    Symlinkat,
165    /// `IORING_OP_LINKAT` (39) — création de lien dur.
166    Linkat,
167    /// `IORING_OP_MSG_RING` (40) — message vers un autre ring.
168    MsgRing,
169    /// `IORING_OP_FSETXATTR` (41) — pose d'attribut étendu (par FD).
170    Fsetxattr,
171    /// `IORING_OP_SETXATTR` (42) — pose d'attribut étendu (par chemin).
172    Setxattr,
173    /// `IORING_OP_FGETXATTR` (43) — lecture d'attribut étendu (par FD).
174    Fgetxattr,
175    /// `IORING_OP_GETXATTR` (44) — lecture d'attribut étendu (par chemin).
176    Getxattr,
177    /// `IORING_OP_SOCKET` (45) — création de socket.
178    Socket,
179    /// `IORING_OP_URING_CMD` (46) — commande passthrough (Temps 2d).
180    UringCmd,
181    /// `IORING_OP_SEND_ZC` (47) — envoi zero-copy (CQE `NOTIF`).
182    SendZc,
183    /// `IORING_OP_SENDMSG_ZC` (48) — envoi de message zero-copy.
184    SendmsgZc,
185    /// `IORING_OP_READ_MULTISHOT` (49) — lecture multishot (Temps 3d).
186    ReadMultishot,
187    /// `IORING_OP_WAITID` (50) — attente d'état de processus.
188    Waitid,
189    /// `IORING_OP_FUTEX_WAIT` (51) — attente sur futex.
190    FutexWait,
191    /// `IORING_OP_FUTEX_WAKE` (52) — réveil de futex.
192    FutexWake,
193    /// `IORING_OP_FUTEX_WAITV` (53) — attente sur un vecteur de futex.
194    FutexWaitv,
195    /// `IORING_OP_FIXED_FD_INSTALL` (54) — installe un FD fixe comme FD normal.
196    FixedFdInstall,
197    /// `IORING_OP_FTRUNCATE` (55) — troncature de fichier.
198    Ftruncate,
199    /// `IORING_OP_BIND` (56) — liaison d'adresse socket.
200    Bind,
201    /// `IORING_OP_LISTEN` (57) — mise en écoute d'un socket.
202    Listen,
203}
204
205// ─────────────────────────────────────────────────────────────────────────
206// Types ajoutés au Temps 2c (async-spécifiques). Cf.
207// docs/specs/layer-0/io-uring-2c-async.md §9. Valeurs : uapi Linux 6.12.
208// ─────────────────────────────────────────────────────────────────────────
209
210// `PollEvents` a été **promu** vers la famille `poll` (`crate::poll`, ADR-044) et
211// est **ré-exporté** ci-dessous : son chemin historique
212// `air_sys_types::io_uring::PollEvents` reste valide (type inchangé sur le fil).
213pub use crate::poll::PollEvents;
214
215bitflags::bitflags! {
216    /// Drapeaux de l'op `IORING_OP_TIMEOUT` (`IORING_TIMEOUT_*`).
217    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
218    pub struct TimeoutFlags: u32 {
219        /// `ABS` : l'échéance est **absolue** (pas une durée relative).
220        const ABS           = 1 << 0;
221        /// `BOOTTIME` : horloge `CLOCK_BOOTTIME`.
222        const BOOTTIME      = 1 << 2;
223        /// `REALTIME` : horloge `CLOCK_REALTIME`.
224        const REALTIME      = 1 << 3;
225        /// `ETIME_SUCCESS` : l'expiration est un **succès** (pas `-ETIME`).
226        const ETIME_SUCCESS = 1 << 5;
227        /// `MULTISHOT` : déclenchements répétés (variante multishot, Temps 3d).
228        const MULTISHOT     = 1 << 6;
229    }
230
231    /// Drapeaux de l'annulation asynchrone (`IORING_OP_ASYNC_CANCEL`).
232    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
233    pub struct CancelFlags: u32 {
234        /// `IORING_ASYNC_CANCEL_ALL` : annule **toutes** les correspondances
235        /// (pas seulement la première).
236        const ALL = 1 << 0;
237    }
238
239    /// Drapeaux de l'op `IORING_OP_MSG_RING` (`IORING_MSG_RING_*`).
240    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
241    pub struct MessageRingFlags: u32 {
242        /// `CQE_SKIP` : ne pas poster de CQE côté **cible**.
243        const CQE_SKIP   = 1 << 0;
244        /// `FLAGS_PASS` : propager les `cqe_flags` vers le CQE cible.
245        const FLAGS_PASS = 1 << 1;
246    }
247
248    /// Drapeaux d'un futex io_uring (`FUTEX_WAIT`/`WAKE`/`WAITV`), sous-ensemble
249    /// `FUTEX2_*` pertinent pour un mot futex `u32`.
250    ///
251    /// La **taille** du mot est toujours `FUTEX2_SIZE_U32` : le mot futex d'une
252    /// `MmapRegion` est un [`core::sync::atomic::AtomicU32`]. La façade
253    /// `air-sys-syscall` l'impose ; il n'a donc **pas** à être passé ici, et seul
254    /// `PRIVATE` est exposé (le seul autre bit de réglage utile pour un mot
255    /// `u32` sans NUMA).
256    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
257    pub struct FutexFlags: u32 {
258        /// `FUTEX2_PRIVATE` : le futex est **privé** au processus (pas de
259        /// partage inter-processus). Optimisation : le kernel évite le verrou
260        /// inter-espace-d'adressage. Valeur `FUTEX_PRIVATE_FLAG` (`0x80`).
261        const PRIVATE = 0x80;
262    }
263
264    /// Drapeaux de l'op `IORING_OP_URING_CMD` (46, Temps 2d) — `IORING_URING_CMD_*`.
265    ///
266    /// Seul `FIXED` existe en kernel 6.12 (`IORING_URING_CMD_MASK == FIXED`).
267    /// `MULTISHOT` est postérieur à 6.12 → hors périmètre.
268    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
269    pub struct UringCmdFlags: u32 {
270        /// `IORING_URING_CMD_FIXED` (bit 0) : la commande utilise un **buffer
271        /// enregistré** (index dans `buf_index`) comme zone de données associée.
272        const FIXED = 1 << 0;
273    }
274}
275
276/// Spécification d'une op `TIMEOUT` : durée et/ou seuil de complétions.
277///
278/// - `duration` : échéance temporelle (relative, ou absolue avec
279///   [`TimeoutFlags::ABS`]). `None` ⇒ timeout purement basé sur le compte.
280/// - `count` : nombre de CQE après lequel le timeout se déclenche (`off` du
281///   SQE). `0` ⇒ timeout purement temporel.
282#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
283pub struct TimeoutSpec {
284    /// Échéance temporelle (`__kernel_timespec`).
285    pub duration: Option<core::time::Duration>,
286    /// Seuil de complétions (`off`).
287    pub count: u32,
288}
289
290impl TimeoutSpec {
291    /// Timeout purement temporel (après `duration`).
292    #[must_use]
293    pub fn after(duration: core::time::Duration) -> Self {
294        Self {
295            duration: Some(duration),
296            count: 0,
297        }
298    }
299
300    /// Timeout purement par comptage (après `count` complétions).
301    #[must_use]
302    pub fn after_completions(count: u32) -> Self {
303        Self {
304            duration: None,
305            count,
306        }
307    }
308}
309
310/// Opération de `epoll_ctl(2)` (`EPOLL_CTL_*`).
311#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
312#[repr(i32)]
313pub enum EpollOp {
314    /// `EPOLL_CTL_ADD` : ajoute le FD au set.
315    Add = 1,
316    /// `EPOLL_CTL_DEL` : retire le FD du set.
317    Delete = 2,
318    /// `EPOLL_CTL_MOD` : modifie les événements surveillés.
319    Modify = 3,
320}
321
322impl EpollOp {
323    /// Valeur kernel (`EPOLL_CTL_*`).
324    #[must_use]
325    pub const fn as_raw(self) -> i32 {
326        self as i32
327    }
328}
329
330/// Miroir `#[repr(C, packed)]` de `struct epoll_event` (12 octets sur
331/// x86_64/aarch64). Lu de façon asynchrone par `IORING_OP_EPOLL_CTL` ⇒ gardé en
332/// vie dans le slot S1 jusqu'à la complétion.
333#[repr(C, packed)]
334#[derive(Debug, Clone, Copy, PartialEq, Eq)]
335pub struct EpollEvent {
336    /// Événements surveillés (`POLL*`/`EPOLL*`).
337    pub events: u32,
338    /// Donnée opaque renvoyée à la notification (`epoll_data_t`).
339    pub data: u64,
340}
341
342impl EpollEvent {
343    /// Construit un `epoll_event` à partir d'événements typés et d'une donnée.
344    #[must_use]
345    pub fn new(events: PollEvents, data: u64) -> Self {
346        Self {
347            events: events.bits(),
348            data,
349        }
350    }
351}
352
353// Layout ABI figé par le kernel (epoll_event packed = 12 octets).
354const _: () = {
355    assert!(core::mem::size_of::<EpollEvent>() == 12);
356};
357
358#[cfg(test)]
359mod time_2c_tests;