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;