Spec couche 0 — Module io_uring, Temps 2d : URING_CMD (passthrough)
Spécification technique — Version 1.0 (cible kernel : Linux 6.12 LTS)
Position. Le Temps 2d spécifie l’opération
IORING_OP_URING_CMD(46), un sous-protocole de passthrough par lequel io_uring transmet une commande opaque au gestionnaireuring_cmddu sous-système propriétaire du fichier visé (socket, NVMe, ublk…). Sous-module dédiéair-sys-syscall::io_uring::cmd. C’est l’opération la plus « brute » des Temps 2x : sa sémantique dépend du sous-système cible, pas d’io_uring. Réutilise le cœur du Temps 1.
1. Nature et contraintes
1.1 Le mécanisme
URING_CMD porte deux choses dans le SQE :
cmd_op(__u32) : l’opération propre au sous-système.- une zone de commande
cmd[]: 16 octets dans un SQE standard (la zoneaddr3/padding), ou 80 octets si le ring est créé avecIORING_SETUP_SQE128.
Le contenu de cmd[] est opaque pour io_uring : c’est le gestionnaire
f_op->uring_cmd du fichier qui l’interprète. Conséquence : la façade ne peut
garantir la sûreté que pour les commandes qu’elle connaît typées (socket) ;
le reste est un mécanisme générique encadré.
1.2 Conventions du Temps 2d
- Détection
SQE128: si une commande nécessite plus de 16 octets et que le ring n’a pasSetupFlags::SQE128, la soumission échoue en amont (Err(EINVAL), Principe 4) — pas de troncature silencieuse. - Flag
FIXED:IORING_URING_CMD_FIXED(bit 0) permet d’utiliser un buffer enregistré (index dansbuf_index) comme zone de données associée à la commande. Seul flag disponible en 6.12. - Pas de multishot :
IORING_URING_CMD_MULTISHOTest postérieur à 6.12 → hors périmètre (doc maître §1.2).
2. Commandes socket (couche typée sûre)
io_uring 6.12 expose, via URING_CMD sur un FD de socket, quatre commandes
(enum io_uring_socket_op). La façade les enveloppe typées et sûres :
#![allow(unused)]
fn main() {
impl IoUring {
/// SIOCINQ : octets disponibles en lecture. cmd_op = SOCKET_URING_OP_SIOCINQ (0).
pub fn submit_socket_inq(&mut self, sock: BorrowedFd<'_>)
-> Result<SubmissionToken, Errno>;
/// SIOCOUTQ : octets en attente d'émission. cmd_op = SOCKET_URING_OP_SIOCOUTQ (1).
pub fn submit_socket_outq(&mut self, sock: BorrowedFd<'_>)
-> Result<SubmissionToken, Errno>;
/// getsockopt. cmd_op = SOCKET_URING_OP_GETSOCKOPT (2).
pub fn submit_getsockopt(&mut self, sock: BorrowedFd<'_>, level: SocketOptionLevel,
optname: i32, value: Vec<u8>)
-> Result<SubmissionToken, Errno>;
/// setsockopt. cmd_op = SOCKET_URING_OP_SETSOCKOPT (3).
pub fn submit_setsockopt(&mut self, sock: BorrowedFd<'_>, level: SocketOptionLevel,
optname: i32, value: Vec<u8>)
-> Result<SubmissionToken, Errno>;
}
}
inq/outq: complétioninto_result() -> i32(nombre d’octets). Équivalents :ioctl(SIOCINQ/SIOCOUTQ), mais dans le ring, sansioctlsynchrone — utile pour piloter le flux d’un socket dans un reactor.getsockopt:valueest le buffer de sortie (ownership transféré, S1) ; le SQE portelevel/optname/optlen/optval. Complétioninto_buffer_result() -> (Vec<u8>, usize)(valeur + longueur effective).setsockopt:value(octets, Principe 3) déplacé dans le slot. Complétioncompleted().SocketOptionLevel: type partagé avecfamily-net(SOL_SOCKET, IPPROTO_TCP…).- Erreurs :
ENOPROTOOPT,EINVAL,ENOTSOCK,EFAULT.
Ces quatre commandes remplacent avantageusement les
ioctl/getsockoptsynchrones dans un chemin asynchrone, sans quitter le reactor.
3. Mécanisme générique (sous-systèmes arbitraires)
Pour les sous-systèmes que la façade ne connaît pas typés (NVMe passthrough, ublk, futurs gestionnaires), deux niveaux.
3.1 Niveau typé via trait UringCommand
#![allow(unused)]
fn main() {
/// Une commande passthrough d'un sous-système, sérialisable dans la zone cmd[].
///
/// # Safety
/// L'implémenteur garantit que `encode` produit une commande valide pour le
/// gestionnaire `uring_cmd` du `fd` ciblé, et que `cmd_op`/les buffers associés
/// respectent le contrat du sous-système.
pub unsafe trait UringCommand {
/// Opération propre au sous-système (cmd_op).
fn cmd_op(&self) -> u32;
/// Taille requise de la zone de commande (≤ 16, ou ≤ 80 si SQE128).
fn cmd_len(&self) -> usize;
/// Sérialise la commande dans `out` (long. `cmd_len`).
fn encode(&self, out: &mut [u8]);
/// Interprétation typée de la complétion.
type Output;
fn interpret(&self, completion: &Completion) -> Result<Self::Output, Errno>;
}
impl IoUring {
pub fn submit_uring_cmd<C: UringCommand>(&mut self, fd: BorrowedFd<'_>, cmd: C)
-> Result<SubmissionToken, Errno>;
}
}
- La façade vérifie
cmd.cmd_len() ≤ 16(ou≤ 80siSQE128), sinonErr(EINVAL). Letraitestunsafecar la validité de la commande dépend du sous-système, hors du contrôle d’io_uring. - Exemple de consommateur : un crate de stockage (couche supérieure) définit
un
NvmePassthroughCmd: UringCommand(struct ~72 octets ⇒ exigeSQE128). Air couche 0 fournit le mécanisme, pas les structures NVMe spécifiques (elles vivent là où elles sont utilisées).
3.2 Niveau brut unsafe (échappatoire)
#![allow(unused)]
fn main() {
impl IoUring {
/// # Safety
/// `cmd_data` doit être une commande valide pour le gestionnaire uring_cmd
/// du `fd`. Tout buffer référencé par la commande doit rester valide
/// jusqu'à la complétion. `cmd_data.len()` ≤ 16 (ou ≤ 80 si SQE128).
pub unsafe fn submit_uring_cmd_raw(&mut self, fd: BorrowedFd<'_>,
cmd_op: u32, cmd_data: &[u8], flags: UringCmdFlags)
-> Result<SubmissionToken, Errno>;
}
}
- Dernier recours pour les sous-systèmes non encore typés. Documentation
# Safetyexhaustive (conv. couche 0).UringCmdFlags:FIXED.
4. Récapitulatif
| # opcode | Opcode | Façade (sûre) | Façade (générique) |
|---|---|---|---|
| 46 | URING_CMD | submit_socket_inq/outq, submit_getsockopt/setsockopt | submit_uring_cmd<C> / submit_uring_cmd_raw (unsafe) |
cmd_op socket couverts (6.12) : SIOCINQ (0), SIOCOUTQ (1), GETSOCKOPT
(2), SETSOCKOPT (3). TX_TIMESTAMP/GETSOCKNAME = post-6.12, hors périmètre.
5. Types ajoutés / partagés
Nouveaux : UringCmdFlags (bitflags, FIXED), trait UringCommand. Partagés :
SocketOptionLevel (family-net). Méthode Completion réutilisée :
into_result/into_buffer_result/completed.
6. Stratégie de tests
- Intégration :
inq/outqsur un socket loopback avec données connues en file ;getsockopt(SO_RCVBUF)/setsockopt(SO_REUSEADDR)round-trip ; comparaison avec lesgetsockopt/ioctlsynchrones (family-net). - SQE128 : commande > 16 octets refusée sans
SQE128(EINVAL) ; acceptée avec. - Sûreté : Miri sur le chemin
submit_uring_cmd<C>(sérialisation danscmd[]bornée) ; test quecmd_len> capacité est refusé. - Fuzzing : décodage des complétions
getsockopt(données kernel externes). - Couverture 100 % lignes + branches ; le
submit_uring_cmd_rawunsafetesté via un faux gestionnaire en harnais, ou consigné enCOVERAGE-EXCEPTIONS.mdsi non provoquable.
7. Décisions de fond émergées au Temps 2d
- Deux niveaux d’exposition : commandes socket typées sûres d’un côté ;
trait UringCommand(unsafe trait) + forme bruteunsafepour les sous-systèmes arbitraires de l’autre. On ne prétend pas typer ce que le sous-système seul connaît. - Détection
SQE128en amont — refus net plutôt que troncature silencieuse. - Structures NVMe hors couche 0 — Air fournit le mécanisme passthrough ; les commandes spécifiques (NVMe, ublk) sont définies par leurs consommateurs.
- Pas de multishot uring_cmd — postérieur à 6.12.
8. Travail à reprendre
Spec suivante : io-uring-3a-registration.md (ressources fixes modernes :
FixedFdTable, RegisteredBuffers, ring fd enregistré, eventfd, personality,
affinités io-wq, napi, clock, clone_buffers, alloc_range). Traduction anglaise
globale après validation des documents français.
Licence du document : MPL 2.0
Statut : Spécification technique du Temps 2d (URING_CMD) du module air-sys-syscall::io_uring, cible kernel 6.12 LTS.