1use super::{Completion, IoUring, SubmissionToken, raw, syscall};
32use crate::mem::{Mapping, MmapRegion, mmap_file};
33use air_sys_types::Errno;
34use air_sys_types::fd::{AsRawFd, BorrowedFd};
35use air_sys_types::io_uring::CompletionFlags;
36use air_sys_types::mem::{MapFlags, ProtectionFlags};
37use air_sys_types::net::MessageFlags;
38use alloc::vec::Vec;
39use core::num::{NonZeroU16, NonZeroU32};
40use core::sync::atomic::{AtomicU16, Ordering};
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
48pub struct ProvidedBufferRingOptions {
49 pub kernel_mmap: bool,
53 pub incremental: bool,
56}
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
60pub struct ProvidedBufferRingStatus {
61 pub head: u32,
63}
64
65#[derive(Debug)]
71enum RingMemory {
72 App(MmapRegion),
74 Kernel(Mapping),
76}
77
78impl RingMemory {
79 fn base(&self) -> *mut u8 {
83 match self {
84 Self::App(region) => region.as_ptr().cast_mut(),
85 Self::Kernel(mapping) => mapping.as_ptr().cast_mut(),
86 }
87 }
88}
89
90#[derive(Debug)]
97pub struct ProvidedBufferRing {
98 bgid: u16,
100 count: u16,
102 mask: u16,
104 buf_size: u32,
106 ring_mem: RingMemory,
108 data: Vec<u8>,
111 provide_tail: u16,
113 available: u16,
115 out_flags: Vec<bool>,
119}
120
121impl ProvidedBufferRing {
122 pub fn register(
132 ring: &mut IoUring,
133 group_id: u16,
134 count: NonZeroU16,
135 buf_size: NonZeroU32,
136 opts: ProvidedBufferRingOptions,
137 ) -> Result<Self, Errno> {
138 let count = count.get();
139 if !count.is_power_of_two() {
140 return Err(Errno::EINVAL);
141 }
142 let buf_size = buf_size.get();
143 let count_usize = usize::from(count);
144 let total = count_usize
145 .checked_mul(usize::try_from(buf_size).expect("u32 ⊆ usize sur cible LP64"))
146 .ok_or(Errno::EINVAL)?;
147 let ring_bytes = count_usize
148 .checked_mul(raw::IO_URING_BUF_SIZE)
149 .ok_or(Errno::EINVAL)?;
150 let data = vec![0u8; total];
151
152 let mut flags = if opts.incremental {
153 raw::IOU_PBUF_RING_INC
154 } else {
155 0
156 };
157 let app_region = if opts.kernel_mmap {
159 flags |= raw::IOU_PBUF_RING_MMAP;
160 None
161 } else {
162 Some(MmapRegion::new_anonymous(
163 ring_bytes,
164 ProtectionFlags::READ | ProtectionFlags::WRITE,
165 MapFlags::PRIVATE,
166 )?)
167 };
168 let ring_addr = app_region.as_ref().map_or(0, |r| r.as_ptr() as u64);
169
170 let reg = raw::IoUringBufReg {
171 ring_addr,
172 ring_entries: u32::from(count),
173 bgid: group_id,
174 flags,
175 resv: [0; 3],
176 };
177 let ret = unsafe {
181 syscall::register(
182 ring.fd_raw(),
183 raw::IORING_REGISTER_PBUF_RING,
184 core::ptr::from_ref(®) as u64,
185 1,
186 )
187 };
188 if ret < 0 {
189 return Err(raw::errno_from_negative_syscall_ret(ret));
190 }
191
192 let ring_mem = match app_region {
194 Some(region) => RingMemory::App(region),
195 None => {
196 let offset =
197 raw::IORING_OFF_PBUF_RING | (u64::from(group_id) << raw::IORING_OFF_PBUF_SHIFT);
198 let fd = unsafe { BorrowedFd::borrow_raw(ring.fd_raw()) };
201 let mapping = mmap_file(
202 fd,
203 ring_bytes,
204 offset,
205 ProtectionFlags::READ | ProtectionFlags::WRITE,
206 MapFlags::SHARED,
207 )?;
208 RingMemory::Kernel(mapping)
209 }
210 };
211
212 let mut this = Self {
213 bgid: group_id,
214 count,
215 mask: count.wrapping_sub(1),
216 buf_size,
217 ring_mem,
218 data,
219 provide_tail: 0,
220 available: 0,
221 out_flags: vec![false; usize::from(count)],
222 };
223 this.provide_initial();
224 Ok(this)
225 }
226
227 fn provide_initial(&mut self) {
230 for bid in 0..self.count {
231 self.write_descriptor(bid, bid);
233 }
234 self.provide_tail = self.count;
235 self.available = self.count;
236 self.publish_tail();
237 }
238
239 fn write_descriptor(&self, slot: u16, bid: u16) {
242 let base = self.ring_mem.base();
243 let slot_off = usize::from(slot & self.mask)
244 .checked_mul(raw::IO_URING_BUF_SIZE)
245 .expect("slot borné par le masque");
246 let data_off = usize::from(bid)
247 .checked_mul(usize::try_from(self.buf_size).expect("u32 ⊆ usize"))
248 .expect("bid × buf_size ≤ total (borné par construction)");
249 let addr = self.data.as_ptr().wrapping_add(data_off) as u64;
250 unsafe {
255 let buf = base.add(slot_off);
256 buf.cast::<u64>().write(addr);
257 buf.add(8).cast::<u32>().write(self.buf_size);
258 buf.add(12).cast::<u16>().write(bid);
259 }
260 }
261
262 fn publish_tail(&self) {
265 let base = self.ring_mem.base();
266 unsafe {
270 let tail = base
271 .add(raw::IO_URING_BUF_RING_TAIL_OFFSET)
272 .cast::<AtomicU16>();
273 (*tail).store(self.provide_tail, Ordering::Release);
274 }
275 }
276
277 fn checkout(&mut self, bid: u16) {
281 let newly_out = self
282 .out_flags
283 .get_mut(usize::from(bid))
284 .is_some_and(|flag| {
285 if *flag {
286 false
287 } else {
288 *flag = true;
289 true
290 }
291 });
292 if newly_out {
293 self.available = self.available.saturating_sub(1);
294 }
295 }
296
297 fn return_buffer(&mut self, bid: u16) {
300 self.write_descriptor(self.provide_tail, bid);
301 self.provide_tail = self.provide_tail.wrapping_add(1);
302 self.publish_tail();
303 let was_out = self
304 .out_flags
305 .get_mut(usize::from(bid))
306 .is_some_and(|flag| {
307 let prev = *flag;
308 *flag = false;
309 prev
310 });
311 if was_out {
312 self.available = self.available.saturating_add(1).min(self.count);
313 }
314 }
315
316 fn buffer_bytes(&self, bid: u16, len: usize) -> &[u8] {
319 let bufsz = usize::try_from(self.buf_size).expect("u32 ⊆ usize");
320 let start = usize::from(bid)
321 .checked_mul(bufsz)
322 .expect("bid × buf_size borné");
323 let end = start.checked_add(len).expect("start + len borné");
324 self.data
325 .get(start..end)
326 .expect("buffer borné (len ≤ buf_size, bid < count)")
327 }
328
329 fn buffer_bytes_mut(&mut self, bid: u16, len: usize) -> &mut [u8] {
331 let bufsz = usize::try_from(self.buf_size).expect("u32 ⊆ usize");
332 let start = usize::from(bid)
333 .checked_mul(bufsz)
334 .expect("bid × buf_size borné");
335 let end = start.checked_add(len).expect("start + len borné");
336 self.data
337 .get_mut(start..end)
338 .expect("buffer borné (len ≤ buf_size, bid < count)")
339 }
340
341 pub fn unregister(self, ring: &mut IoUring) -> Result<(), Errno> {
366 let reg = raw::IoUringBufReg {
367 ring_addr: 0,
368 ring_entries: 0,
369 bgid: self.bgid,
370 flags: 0,
371 resv: [0; 3],
372 };
373 let ret = unsafe {
376 syscall::register(
377 ring.fd_raw(),
378 raw::IORING_UNREGISTER_PBUF_RING,
379 core::ptr::from_ref(®) as u64,
380 1,
381 )
382 };
383 if ret < 0 {
384 return Err(raw::errno_from_negative_syscall_ret(ret));
385 }
386 Ok(())
387 }
388
389 pub fn status(&self, ring: &IoUring) -> Result<ProvidedBufferRingStatus, Errno> {
396 let mut st = raw::IoUringBufStatus {
397 buf_group: u32::from(self.bgid),
398 head: 0,
399 resv: [0; 8],
400 };
401 let ret = unsafe {
404 syscall::register(
405 ring.fd_raw(),
406 raw::IORING_REGISTER_PBUF_STATUS,
407 core::ptr::from_mut(&mut st) as u64,
408 1,
409 )
410 };
411 if ret < 0 {
412 return Err(raw::errno_from_negative_syscall_ret(ret));
413 }
414 Ok(ProvidedBufferRingStatus { head: st.head })
415 }
416
417 #[must_use]
419 pub fn group_id(&self) -> u16 {
420 self.bgid
421 }
422
423 #[must_use]
425 pub fn available(&self) -> u16 {
426 self.available
427 }
428}
429
430impl IoUring {
435 pub fn submit_receive_provided(
445 &mut self,
446 sock: BorrowedFd<'_>,
447 group: &ProvidedBufferRing,
448 flags: MessageFlags,
449 bundle: bool,
450 ) -> Result<SubmissionToken, Errno> {
451 let fd = sock.as_raw_fd();
452 let bgid = group.group_id();
453 let msg_flags = flags.bits().cast_unsigned();
454 self.pending_options = self.pending_options.buffer_select();
455 self.submit_op(None, |sqe| {
456 sqe.opcode = raw::IORING_OP_RECV;
457 sqe.fd = fd;
458 sqe.len = 0; sqe.op_flags = msg_flags;
460 sqe.buf_index_or_group = bgid;
461 if bundle {
462 sqe.ioprio |= raw::IORING_RECVSEND_BUNDLE;
463 }
464 })
465 }
466
467 pub fn submit_read_provided(
475 &mut self,
476 fd: BorrowedFd<'_>,
477 group: &ProvidedBufferRing,
478 length: u32,
479 offset: Option<u64>,
480 bundle: bool,
481 ) -> Result<SubmissionToken, Errno> {
482 let fd = fd.as_raw_fd();
483 let bgid = group.group_id();
484 let off = offset.unwrap_or(u64::MAX);
485 self.pending_options = self.pending_options.buffer_select();
486 self.submit_op(None, |sqe| {
487 sqe.opcode = raw::IORING_OP_READ;
488 sqe.fd = fd;
489 sqe.len = length;
490 sqe.off_or_addr2 = off;
491 sqe.buf_index_or_group = bgid;
492 if bundle {
493 sqe.ioprio |= raw::IORING_RECVSEND_BUNDLE;
494 }
495 })
496 }
497}
498
499impl Completion {
504 #[must_use]
511 pub fn into_provided_buffer<'r>(
512 self,
513 group: &'r mut ProvidedBufferRing,
514 ) -> Option<ProvidedBuffer<'r>> {
515 let bid = self.buffer_id()?;
516 let flags = self.flags();
517 let received = usize::try_from(self.raw_result()).unwrap_or(0);
519 let len = received.min(usize::try_from(group.buf_size).expect("u32 ⊆ usize"));
520 let replenish = !flags.contains(CompletionFlags::BUF_MORE);
523 group.checkout(bid);
524 Some(ProvidedBuffer {
525 group,
526 bid,
527 len,
528 replenish,
529 })
530 }
531}
532
533#[derive(Debug)]
540pub struct ProvidedBuffer<'r> {
541 group: &'r mut ProvidedBufferRing,
543 bid: u16,
545 len: usize,
547 replenish: bool,
549}
550
551impl ProvidedBuffer<'_> {
552 #[must_use]
554 pub fn id(&self) -> u16 {
555 self.bid
556 }
557
558 #[must_use]
560 pub fn data(&self) -> &[u8] {
561 self.group.buffer_bytes(self.bid, self.len)
562 }
563
564 #[must_use]
566 pub fn data_mut(&mut self) -> &mut [u8] {
567 self.group.buffer_bytes_mut(self.bid, self.len)
568 }
569}
570
571impl Drop for ProvidedBuffer<'_> {
572 fn drop(&mut self) {
573 if self.replenish {
574 self.group.return_buffer(self.bid);
575 }
576 }
577}
578
579#[cfg(test)]
592mod tests {
593 use super::syscall::sim::{self, Syscall};
594 use super::*;
595 use crate::io_uring::CompletionFlags;
596 use crate::io_uring::SubmissionToken;
597 use crate::test_support::{sfd, std_file};
598 use air_sys_types::fd::AsFd;
599 use std::io::Write;
600
601 fn ring8() -> IoUring {
602 IoUring::new(NonZeroU32::new(8).expect("8 ≠ 0")).expect("io_uring_setup 6.12")
603 }
604
605 fn nz16(n: u16) -> NonZeroU16 {
606 NonZeroU16::new(n).expect("n ≠ 0")
607 }
608
609 fn nz32(n: u32) -> NonZeroU32 {
610 NonZeroU32::new(n).expect("n ≠ 0")
611 }
612
613 fn tcp_pair() -> (std::net::TcpStream, std::net::TcpStream) {
614 let listener = std::net::TcpListener::bind("127.0.0.1:0").expect("bind loopback");
615 let addr = listener.local_addr().expect("local_addr");
616 let client = std::net::TcpStream::connect(addr).expect("connect loopback");
617 let (server, _) = listener.accept().expect("accept loopback");
618 (client, server)
619 }
620
621 fn complete_one(ring: &mut IoUring, expected: SubmissionToken) -> Completion {
622 ring.submit_and_wait(1).expect("submit_and_wait");
623 let c = ring.wait_completion().expect("wait_completion");
624 assert_eq!(c.token(), expected, "user_data round-trip");
625 c
626 }
627
628 fn synth_completion(bid: u16, res: i32, buf_more: bool) -> Completion {
630 let mut raw_flags = CompletionFlags::BUFFER.bits() | (u32::from(bid) << 16);
631 if buf_more {
632 raw_flags |= CompletionFlags::BUF_MORE.bits();
633 }
634 Completion {
635 token: SubmissionToken::new(0, 0),
636 res,
637 raw_flags,
638 payload: None,
639 multishot: false,
640 }
641 }
642
643 #[test]
646 #[cfg_attr(miri, ignore = "io_uring_register non supporté par Miri")]
647 fn guard_replenishes_on_drop_and_exposes_data() {
648 let mut ring = ring8();
649 let mut group =
650 ProvidedBufferRing::register(&mut ring, 7, nz16(4), nz32(64), Default::default())
651 .expect("register");
652 assert_eq!(group.group_id(), 7);
653 assert_eq!(group.available(), 4);
654 let completion = synth_completion(2, 5, false);
656 {
657 let mut buf = completion
658 .into_provided_buffer(&mut group)
659 .expect("buffer présent");
660 assert_eq!(buf.id(), 2);
661 assert_eq!(buf.data().len(), 5);
662 buf.data_mut()[0] = 0xAB;
663 assert_eq!(buf.data()[0], 0xAB);
664 } assert_eq!(group.available(), 4, "réappro après libération du guard");
666 let completion = synth_completion(0, 3, false);
668 let buf = completion
669 .into_provided_buffer(&mut group)
670 .expect("buffer présent");
671 core::mem::forget(buf); assert_eq!(group.available(), 3, "checkout non rendu");
673 group.unregister(&mut ring).expect("unregister");
674 }
675
676 #[test]
677 #[cfg_attr(miri, ignore = "io_uring_register non supporté par Miri")]
678 fn incremental_buf_more_does_not_replenish() {
679 let mut ring = ring8();
680 let mut group = ProvidedBufferRing::register(
681 &mut ring,
682 3,
683 nz16(2),
684 nz32(64),
685 ProvidedBufferRingOptions {
686 kernel_mmap: false,
687 incremental: true,
688 },
689 )
690 .expect("register incremental");
691 assert_eq!(group.available(), 2);
692 let completion = synth_completion(1, 8, true);
694 {
695 let buf = completion
696 .into_provided_buffer(&mut group)
697 .expect("buffer présent");
698 assert_eq!(buf.id(), 1);
699 } assert_eq!(
701 group.available(),
702 1,
703 "incrémental : pas de réappro sur BUF_MORE"
704 );
705 let completion = synth_completion(1, 8, false);
707 drop(completion.into_provided_buffer(&mut group).expect("buffer"));
708 assert_eq!(group.available(), 2, "réappro à la complétion finale");
709 group.unregister(&mut ring).expect("unregister");
710 }
711
712 #[test]
713 #[cfg_attr(miri, ignore = "io_uring_register non supporté par Miri")]
714 fn into_provided_buffer_none_without_buffer_flag() {
715 let mut ring = ring8();
716 let mut group =
717 ProvidedBufferRing::register(&mut ring, 0, nz16(2), nz32(16), Default::default())
718 .expect("register");
719 let completion = Completion {
721 token: SubmissionToken::new(0, 0),
722 res: -105, raw_flags: 0,
724 payload: None,
725 multishot: false,
726 };
727 assert!(completion.into_provided_buffer(&mut group).is_none());
728 assert_eq!(group.available(), 2, "available inchangé sans checkout");
729 group.unregister(&mut ring).expect("unregister");
730 }
731
732 #[test]
735 #[cfg_attr(miri, ignore = "syscall io_uring non supporté par Miri")]
736 fn provided_recv_round_trip_and_replenish() {
737 let (mut client, server) = tcp_pair();
738 client.write_all(b"hello").expect("write");
739 client.flush().expect("flush");
740 std::thread::sleep(std::time::Duration::from_millis(20));
741 let mut ring = ring8();
742 let mut group =
743 ProvidedBufferRing::register(&mut ring, 1, nz16(4), nz32(64), Default::default())
744 .expect("register");
745 let tok = ring
746 .submit_receive_provided(sfd(&server), &group, MessageFlags::empty(), false)
747 .expect("submit recv_provided");
748 let completion = complete_one(&mut ring, tok);
749 let id;
750 {
751 let buf = completion
752 .into_provided_buffer(&mut group)
753 .expect("buffer choisi par le kernel");
754 id = buf.id();
755 assert!(id < 4);
756 assert_eq!(buf.data(), b"hello");
757 } assert_eq!(group.available(), 4, "réappro après traitement");
761 group.unregister(&mut ring).expect("unregister");
762 let _ = id;
763 }
764
765 #[test]
766 #[cfg_attr(miri, ignore = "syscall io_uring non supporté par Miri")]
767 fn provided_enobufs_then_replenish() {
768 let (mut client, server) = tcp_pair();
769 client.write_all(b"AAAAAAAABBBBBBBB").expect("write 16"); client.flush().expect("flush");
771 std::thread::sleep(std::time::Duration::from_millis(20));
772 let mut ring = ring8();
773 let mut group =
774 ProvidedBufferRing::register(&mut ring, 2, nz16(1), nz32(8), Default::default())
775 .expect("register count=1");
776 let t1 = ring
778 .submit_receive_provided(sfd(&server), &group, MessageFlags::empty(), false)
779 .expect("recv1");
780 let t2 = ring
781 .submit_receive_provided(sfd(&server), &group, MessageFlags::empty(), false)
782 .expect("recv2");
783 ring.submit_and_wait(2).expect("submit 2");
784 let mut ok = 0;
785 let mut enobufs = 0;
786 for _ in 0..2 {
787 let c = ring.wait_completion().expect("completion");
788 assert!(c.token() == t1 || c.token() == t2);
789 if c.flags().contains(CompletionFlags::BUFFER) {
790 let buf = c.into_provided_buffer(&mut group).expect("buffer");
791 assert_eq!(buf.data().len(), 8);
792 ok += 1;
793 drop(buf); } else {
795 assert_eq!(c.into_result(), Err(Errno::ENOBUFS));
796 enobufs += 1;
797 }
798 }
799 assert_eq!((ok, enobufs), (1, 1), "1 servi, 1 en pénurie");
800 let t3 = ring
802 .submit_receive_provided(sfd(&server), &group, MessageFlags::empty(), false)
803 .expect("recv3");
804 let c = complete_one(&mut ring, t3);
805 let buf = c
806 .into_provided_buffer(&mut group)
807 .expect("buffer après réappro");
808 assert_eq!(buf.data(), b"BBBBBBBB");
809 drop(buf);
810 group.unregister(&mut ring).expect("unregister");
811 }
812
813 #[test]
814 #[cfg_attr(miri, ignore = "syscall io_uring non supporté par Miri")]
815 fn provided_kernel_mmap_mode_round_trip() {
816 let (mut client, server) = tcp_pair();
817 client.write_all(b"viamap").expect("write");
818 client.flush().expect("flush");
819 std::thread::sleep(std::time::Duration::from_millis(20));
820 let mut ring = ring8();
821 let mut group = ProvidedBufferRing::register(
822 &mut ring,
823 5,
824 nz16(4),
825 nz32(64),
826 ProvidedBufferRingOptions {
827 kernel_mmap: true,
828 incremental: false,
829 },
830 )
831 .expect("register kernel_mmap");
832 let tok = ring
833 .submit_receive_provided(sfd(&server), &group, MessageFlags::empty(), false)
834 .expect("recv");
835 let c = complete_one(&mut ring, tok);
836 let buf = c.into_provided_buffer(&mut group).expect("buffer");
837 assert_eq!(buf.data(), b"viamap");
838 drop(buf);
839 group.unregister(&mut ring).expect("unregister");
840 }
841
842 #[test]
843 #[cfg_attr(miri, ignore = "syscall io_uring non supporté par Miri")]
844 fn provided_status_and_read_and_bundle() {
845 let mut ring = ring8();
846 let mut group =
847 ProvidedBufferRing::register(&mut ring, 9, nz16(4), nz32(64), Default::default())
848 .expect("register");
849 let status = group.status(&ring).expect("status");
851 assert_eq!(status.head, 0);
852 let (read_end, write_end) = pipe_pair();
854 {
855 let mut w = std_file(write_end);
856 w.write_all(b"piped!").expect("write pipe");
857 w.flush().expect("flush");
858 }
859 let tok = ring
860 .submit_read_provided(read_end.as_fd(), &group, 64, None, false)
861 .expect("read_provided");
862 let c = complete_one(&mut ring, tok);
863 let buf = c.into_provided_buffer(&mut group).expect("buffer read");
864 assert_eq!(buf.data(), b"piped!");
865 drop(buf);
866 let (read2, write2) = pipe_pair();
870 {
871 let mut w = std_file(write2);
872 w.write_all(b"rd2").expect("write pipe2");
873 w.flush().expect("flush");
874 }
875 let tok = ring
876 .submit_read_provided(read2.as_fd(), &group, 64, Some(0), true)
877 .expect("read_provided bundle");
878 let c = complete_one(&mut ring, tok);
879 if let Some(buf) = c.into_provided_buffer(&mut group) {
880 drop(buf);
881 }
882 let (mut client, server) = tcp_pair();
884 client.write_all(b"bundled").expect("write");
885 client.flush().expect("flush");
886 std::thread::sleep(std::time::Duration::from_millis(20));
887 let tok = ring
888 .submit_receive_provided(sfd(&server), &group, MessageFlags::empty(), true)
889 .expect("recv bundle");
890 let c = complete_one(&mut ring, tok);
891 let buf = c.into_provided_buffer(&mut group).expect("buffer bundle");
893 assert!(buf.data().starts_with(b"bundled") || !buf.data().is_empty());
894 drop(buf);
895 group.unregister(&mut ring).expect("unregister");
896 }
897
898 #[test]
901 #[cfg_attr(miri, ignore = "io_uring_register non supporté par Miri")]
902 fn checkout_return_invariants() {
903 let mut ring = ring8();
904 let count: u16 = 4;
905 let mut group =
906 ProvidedBufferRing::register(&mut ring, 0, nz16(count), nz32(32), Default::default())
907 .expect("register");
908 let mut returned: Vec<u16> = Vec::new();
912 for step in 0u16..256 {
913 assert!(group.available() <= count, "available ≤ count");
914 let bid = step % count;
915 let before = group.available();
916 let c = synth_completion(bid, 4, false);
918 {
919 let buf = c.into_provided_buffer(&mut group).expect("buffer");
920 assert_eq!(buf.id(), bid, "id rendu == id demandé, jamais mélangé");
921 } assert_eq!(group.available(), before, "aucun buffer perdu sur un cycle");
924 returned.push(bid);
925 }
926 assert_eq!(group.available(), count, "tout rendu en fin");
927 assert_eq!(returned.len(), 256);
928 group.unregister(&mut ring).expect("unregister");
929 }
930
931 #[test]
934 #[cfg_attr(miri, ignore = "io_uring_register non supporté par Miri")]
935 fn register_validates_and_errors_propagate() {
936 sim::clear();
937 let mut ring = ring8();
938 assert_eq!(
940 ProvidedBufferRing::register(&mut ring, 0, nz16(3), nz32(64), Default::default()).err(),
941 Some(Errno::EINVAL)
942 );
943 sim::inject(Syscall::Register, -22);
945 assert_eq!(
946 ProvidedBufferRing::register(&mut ring, 0, nz16(4), nz32(64), Default::default()).err(),
947 Some(Errno::EINVAL)
948 );
949 let group =
951 ProvidedBufferRing::register(&mut ring, 0, nz16(4), nz32(64), Default::default())
952 .expect("register");
953 sim::inject(Syscall::Register, -22);
954 assert_eq!(group.status(&ring).err(), Some(Errno::EINVAL));
955 sim::inject(Syscall::Register, -22);
956 assert_eq!(group.unregister(&mut ring).err(), Some(Errno::EINVAL));
957 sim::clear();
958 }
959
960 fn pipe_pair() -> (air_sys_types::fd::OwnedFd, air_sys_types::fd::OwnedFd) {
962 let (r, w) = crate::ipc::pipe2(air_sys_types::ipc::PipeFlags::empty()).expect("pipe2");
963 (r, w)
964 }
965}