1#![allow(non_upper_case_globals)]
6#![allow(clippy::unwrap_in_result)]
8
9use bitflags::bitflags;
10use starnix_core::mm::memory::MemoryObject;
11use starnix_core::mm::{
12 DesiredAddress, IOVecPtr, MappingName, MappingOptions, MemoryAccessor, MemoryAccessorExt,
13 PAGE_SIZE, ProtectionFlags, read_to_object_as_bytes,
14};
15use starnix_core::task::CurrentTask;
16use starnix_core::vfs::socket::syscalls::{
17 MsgHdrPtr, MsgHdrRef, WithAlternateBuffer, recvmsg_impl, sys_recvfrom, sys_sendmsg, sys_sendto,
18};
19use starnix_core::vfs::syscalls::{
20 sys_pread64, sys_preadv2, sys_pwrite64, sys_pwritev2, sys_read, sys_write,
21};
22use starnix_core::vfs::{
23 Anon, FdNumber, FileHandle, FileObject, FileOps, fileops_impl_dataless,
24 fileops_impl_nonseekable, fileops_impl_noop_sync,
25};
26use starnix_logging::{set_zx_name, track_stub};
27use starnix_sync::{IoUringStateLock, LockDepMutex};
28use starnix_syscalls::{SUCCESS, SyscallArg, SyscallResult};
29use starnix_types::user_buffer::{UserBuffer, UserBuffers};
30use starnix_uapi::errors::Errno;
31use starnix_uapi::open_flags::OpenFlags;
32use starnix_uapi::user_address::{ArchSpecific, UserAddress, UserRef};
33use starnix_uapi::user_value::UserValue;
34use starnix_uapi::{
35 IORING_FEAT_SINGLE_MMAP, IORING_OFF_CQ_RING, IORING_OFF_SQ_RING, IORING_OFF_SQES, errno, error,
36 io_cqring_offsets, io_sqring_offsets, io_uring_cqe, io_uring_op, io_uring_op_IORING_OP_LAST,
37 io_uring_op_IORING_OP_NOP, io_uring_op_IORING_OP_READ, io_uring_op_IORING_OP_READ_FIXED,
38 io_uring_op_IORING_OP_READV, io_uring_op_IORING_OP_RECV, io_uring_op_IORING_OP_RECVMSG,
39 io_uring_op_IORING_OP_SEND, io_uring_op_IORING_OP_SENDMSG, io_uring_op_IORING_OP_WRITE,
40 io_uring_op_IORING_OP_WRITE_FIXED, io_uring_op_IORING_OP_WRITEV, io_uring_params, io_uring_sqe,
41 io_uring_sqe_flags_bit_IOSQE_ASYNC_BIT, io_uring_sqe_flags_bit_IOSQE_BUFFER_SELECT_BIT,
42 io_uring_sqe_flags_bit_IOSQE_CQE_SKIP_SUCCESS_BIT, io_uring_sqe_flags_bit_IOSQE_FIXED_FILE_BIT,
43 io_uring_sqe_flags_bit_IOSQE_IO_DRAIN_BIT, io_uring_sqe_flags_bit_IOSQE_IO_HARDLINK_BIT,
44 io_uring_sqe_flags_bit_IOSQE_IO_LINK_BIT, off_t, socklen_t, uapi,
45};
46use std::sync::Arc;
47use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
48
49pub const IORING_MAX_ENTRIES: u32 = 1 << 15; const IORING_MAX_CQ_ENTRIES: u32 = 2 * IORING_MAX_ENTRIES;
52
53bitflags! {
54 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
55 pub struct IoRingSetupFlags: u32 {
56 const IoPoll = starnix_uapi::IORING_SETUP_IOPOLL;
57 const SqPoll = starnix_uapi::IORING_SETUP_SQPOLL;
58 const SqAff = starnix_uapi::IORING_SETUP_SQ_AFF;
59 const CqSize = starnix_uapi::IORING_SETUP_CQSIZE;
60 const Clamp = starnix_uapi::IORING_SETUP_CLAMP;
61 const AttachWq = starnix_uapi::IORING_SETUP_ATTACH_WQ;
62 const RDisabled = starnix_uapi::IORING_SETUP_R_DISABLED;
63 const SubmitAll = starnix_uapi::IORING_SETUP_SUBMIT_ALL;
64 const CoopTaskRun = starnix_uapi::IORING_SETUP_COOP_TASKRUN;
65 const TaskRunFlag = starnix_uapi::IORING_SETUP_TASKRUN_FLAG;
66 const SqE128 = starnix_uapi::IORING_SETUP_SQE128;
67 const CqE32 = starnix_uapi::IORING_SETUP_CQE32;
68 const SingleIssuer = starnix_uapi::IORING_SETUP_SINGLE_ISSUER;
69 const DeferTaskRun = starnix_uapi::IORING_SETUP_DEFER_TASKRUN;
70 const NoMmap = starnix_uapi::IORING_SETUP_NO_MMAP;
71 const RegisteredFdOnly = starnix_uapi::IORING_SETUP_REGISTERED_FD_ONLY;
72 const NoSqArray = starnix_uapi::IORING_SETUP_NO_SQARRAY;
73
74 const SupportedFlags = starnix_uapi::IORING_SETUP_CQSIZE |
77 starnix_uapi::IORING_SETUP_COOP_TASKRUN |
78 starnix_uapi::IORING_SETUP_TASKRUN_FLAG |
79 starnix_uapi::IORING_SETUP_SINGLE_ISSUER |
80 starnix_uapi::IORING_SETUP_DEFER_TASKRUN;
81
82 const IgnoredFlags = starnix_uapi::IORING_SETUP_COOP_TASKRUN |
86 starnix_uapi::IORING_SETUP_TASKRUN_FLAG |
87 starnix_uapi::IORING_SETUP_SINGLE_ISSUER |
88 starnix_uapi::IORING_SETUP_DEFER_TASKRUN;
89 }
90
91 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
92 struct SqEntryFlags: u8 {
93 const FIXED_FILE = 1 << io_uring_sqe_flags_bit_IOSQE_FIXED_FILE_BIT;
94 const IO_DRAIN = 1 << io_uring_sqe_flags_bit_IOSQE_IO_DRAIN_BIT;
95 const IO_LINK = 1 << io_uring_sqe_flags_bit_IOSQE_IO_LINK_BIT;
96 const IO_HARDLINK = 1 << io_uring_sqe_flags_bit_IOSQE_IO_HARDLINK_BIT;
97 const ASYNC = 1 << io_uring_sqe_flags_bit_IOSQE_ASYNC_BIT;
98 const BUFFER_SELECT = 1 << io_uring_sqe_flags_bit_IOSQE_BUFFER_SELECT_BIT;
99 const CQE_SKIP_SUCCESS = 1 << io_uring_sqe_flags_bit_IOSQE_CQE_SKIP_SUCCESS_BIT;
100 }
101}
102
103impl IoRingSetupFlags {
104 fn build_and_validate_from(value: u32) -> Result<Self, Errno> {
105 let Some(flags) = IoRingSetupFlags::from_bits(value) else {
106 track_stub!(
107 TODO("https://fxbug.dev/297431387"),
108 "io_uring_setup undefined flag(s)",
109 value
110 );
111 return error!(EINVAL);
112 };
113
114 let unsupported_flags = flags.difference(IoRingSetupFlags::SupportedFlags);
115 if !unsupported_flags.is_empty() {
116 track_stub!(
117 TODO("https://fxbug.dev/297431387"),
118 "io_uring_setup unsupported flags",
119 unsupported_flags.bits()
120 );
121 return error!(EINVAL);
122 }
123 let ignored_flags = flags.intersection(IoRingSetupFlags::IgnoredFlags);
124 if !ignored_flags.is_empty() {
125 track_stub!(
126 TODO("https://fxbug.dev/297431387"),
127 "io_uring_setup ignored flags",
128 ignored_flags.bits()
129 );
130 }
131
132 if flags.contains(IoRingSetupFlags::DeferTaskRun)
134 && !flags.contains(IoRingSetupFlags::SingleIssuer)
135 {
136 return error!(EINVAL);
137 }
138
139 return Ok(flags);
140 }
141}
142
143type RingIndex = u32;
144
145type UserRingBufferHeader = uapi::io_uring_buf_ring__bindgen_ty_1__bindgen_ty_1;
146type UserRingBufferEntry = uapi::io_uring_buf;
147
148static_assertions::const_assert_eq!(
149 std::mem::size_of::<u16>(),
150 uapi::size_of_field!(UserRingBufferHeader, tail)
151);
152static_assertions::const_assert_eq!(
153 std::mem::size_of::<UserRingBufferHeader>(),
154 std::mem::size_of::<UserRingBufferEntry>()
155);
156
157#[repr(C)]
167#[derive(Debug, Default, Copy, Clone, IntoBytes, FromBytes, KnownLayout, Immutable)]
168struct ControlHeader {
169 sq_head: u32,
175
176 sq_tail: u32,
181
182 cq_head: u32,
188
189 cq_tail: u32,
194
195 sq_ring_mask: u32,
197
198 cq_ring_mask: u32,
200
201 sq_ring_entries: u32,
203
204 cq_ring_entries: u32,
206
207 sq_dropped: u32,
209
210 sq_flags: u32,
211 cq_flags: u32,
212
213 cq_overflow: u32,
216
217 _padding: [u8; 16],
218}
219
220const RING_ALIGNMENT: usize = 64;
221
222static_assertions::const_assert_eq!(std::mem::size_of::<ControlHeader>(), RING_ALIGNMENT);
224
225#[repr(C)]
231#[derive(Debug, Default, Copy, Clone, IntoBytes, FromBytes, KnownLayout, Immutable)]
232struct SqEntry {
233 opcode: u8,
234 flags: u8,
235 ioprio: u16,
236 raw_fd: i32,
237 field0: u64,
238 field1: u64,
239 len: u32,
240 op_flags: u32,
241 user_data: u64,
242 buf_index_or_group: u16,
243 personality: u16,
244 field2: u32,
245 field3: [u64; 2usize],
246}
247
248uapi::check_arch_independent_same_layout! {
249 SqEntry = io_uring_sqe {
250 opcode => opcode,
251 flags => flags,
252 ioprio => ioprio,
253 raw_fd => fd,
254 field0 => __bindgen_anon_1,
255 field1 => __bindgen_anon_2,
256 len => len,
257 op_flags => __bindgen_anon_3,
258 user_data => user_data,
259 buf_index_or_group => __bindgen_anon_4,
260 personality => personality,
261 field2 => __bindgen_anon_5,
262 field3 => __bindgen_anon_6,
263 }
264}
265
266uapi::check_arch_independent_layout! {
267 io_uring_recvmsg_out{
268 namelen,
269 controllen,
270 payloadlen,
271 flags,
272 }
273}
274
275impl SqEntry {
276 fn complete(&self, result: Result<SyscallResult, Errno>, flags: u32) -> CqEntry {
277 let res = match result {
278 Ok(return_value) => return_value.value() as i32,
279 Err(errno) => errno.return_value() as i32,
280 };
281 CqEntry { user_data: self.user_data, res, flags }
282 }
283
284 fn fd(&self) -> FdNumber {
285 FdNumber::from_raw(self.raw_fd)
286 }
287
288 fn iovec_addr<Arch: ArchSpecific>(&self, arch: &Arch) -> IOVecPtr {
289 IOVecPtr::new(arch, self.field1)
290 }
291
292 fn iovec_count(&self) -> UserValue<i32> {
293 (self.len as i32).into()
294 }
295
296 fn address(&self) -> UserAddress {
297 self.field1.into()
298 }
299
300 fn length(&self) -> usize {
301 self.len as usize
302 }
303
304 fn offset(&self) -> off_t {
305 self.field0 as off_t
306 }
307
308 fn buf_index(&self) -> usize {
309 self.buf_index_or_group as usize
310 }
311
312 fn group(&self) -> u16 {
313 self.buf_index_or_group
314 }
315}
316
317#[repr(C)]
323#[derive(Debug, Default, Copy, Clone, IntoBytes, FromBytes, KnownLayout, Immutable)]
324struct CqEntry {
325 pub user_data: u64,
326 pub res: i32,
327 pub flags: u32,
328}
329
330static_assertions::assert_eq_size!(CqEntry, io_uring_cqe);
331static_assertions::const_assert_eq!(
332 std::mem::offset_of!(CqEntry, user_data),
333 std::mem::offset_of!(io_uring_cqe, user_data)
334);
335static_assertions::const_assert_eq!(
336 std::mem::offset_of!(CqEntry, res),
337 std::mem::offset_of!(io_uring_cqe, res)
338);
339static_assertions::const_assert_eq!(
340 std::mem::offset_of!(CqEntry, flags),
341 std::mem::offset_of!(io_uring_cqe, flags)
342);
343
344const CQES_OFFSET: usize = std::mem::size_of::<ControlHeader>();
345
346#[inline]
347fn align_ring_field(offset: usize) -> usize {
348 offset.next_multiple_of(RING_ALIGNMENT)
349}
350struct IoUringMetadata {
351 sq_entries: u32,
353
354 cq_entries: u32,
356}
357
358impl IoUringMetadata {
359 fn cq_entry_offset(&self, index: u32) -> u64 {
363 let index = index % self.cq_entries;
364 (CQES_OFFSET + index as usize * std::mem::size_of::<io_uring_cqe>()) as u64
365 }
366
367 fn cqes_offset(&self) -> usize {
369 CQES_OFFSET
370 }
371
372 fn array_offset(&self) -> usize {
374 CQES_OFFSET
375 + align_ring_field(self.cq_entries as usize * std::mem::size_of::<io_uring_cqe>())
376 }
377
378 fn array_entry_offset(&self, index: u32) -> u64 {
381 let index = index % self.sq_entries;
382 (self.array_offset() + index as usize * std::mem::size_of::<RingIndex>()) as u64
383 }
384
385 fn ring_buffer_size(&self) -> usize {
387 self.array_offset() + self.sq_entries as usize * std::mem::size_of::<RingIndex>()
388 }
389
390 fn sq_entry_offset(&self, index: u32) -> u64 {
395 let index = index % self.sq_entries;
396 (index as usize * std::mem::size_of::<io_uring_sqe>()) as u64
397 }
398
399 fn sq_entries_size(&self) -> usize {
401 self.sq_entries as usize * std::mem::size_of::<io_uring_sqe>()
402 }
403}
404
405#[repr(u32)]
406enum Op {
407 NOP = io_uring_op_IORING_OP_NOP,
408 Read = io_uring_op_IORING_OP_READ,
409 ReadV = io_uring_op_IORING_OP_READV,
410 ReadFixed = io_uring_op_IORING_OP_READ_FIXED,
411 Recv = io_uring_op_IORING_OP_RECV,
412 RecvMsg = io_uring_op_IORING_OP_RECVMSG,
413 Send = io_uring_op_IORING_OP_SEND,
414 SendMsg = io_uring_op_IORING_OP_SENDMSG,
415 Write = io_uring_op_IORING_OP_WRITE,
416 WriteV = io_uring_op_IORING_OP_WRITEV,
417 WriteFixed = io_uring_op_IORING_OP_WRITE_FIXED,
418}
419
420impl Op {
421 fn from_code(opcode: io_uring_op) -> Result<Op, Errno> {
422 match opcode {
423 io_uring_op_IORING_OP_NOP => Ok(Self::NOP),
424 io_uring_op_IORING_OP_READ => Ok(Self::Read),
425 io_uring_op_IORING_OP_READV => Ok(Self::ReadV),
426 io_uring_op_IORING_OP_READ_FIXED => Ok(Self::ReadFixed),
427 io_uring_op_IORING_OP_RECV => Ok(Self::Recv),
428 io_uring_op_IORING_OP_RECVMSG => Ok(Self::RecvMsg),
429 io_uring_op_IORING_OP_SEND => Ok(Self::Send),
430 io_uring_op_IORING_OP_SENDMSG => Ok(Self::SendMsg),
431 io_uring_op_IORING_OP_WRITE => Ok(Self::Write),
432 io_uring_op_IORING_OP_WRITEV => Ok(Self::WriteV),
433 io_uring_op_IORING_OP_WRITE_FIXED => Ok(Self::WriteFixed),
434 0..io_uring_op_IORING_OP_LAST => {
435 track_stub!(
436 TODO("https://fxbug.dev/297431387"),
437 "io_uring_enter unimplemented op",
438 opcode
439 );
440 error!(EOPNOTSUPP)
441 }
442 _ => error!(EINVAL),
443 }
444 }
445}
446
447fn read_object<T: FromBytes>(memory_object: &MemoryObject, offset: u64) -> Result<T, Errno> {
455 unsafe {
457 read_to_object_as_bytes(|buf| {
458 memory_object.read_uninit(buf, offset).map_err(|_| errno!(EFAULT))?;
459 Ok(())
460 })
461 }
462}
463
464fn write_object<T: IntoBytes + Immutable>(
467 memory_object: &MemoryObject,
468 offset: u64,
469 value: &T,
470) -> Result<(), Errno> {
471 memory_object.write(value.as_bytes(), offset).map_err(|_| errno!(EFAULT))
472}
473
474struct IoUringQueue {
476 metadata: IoUringMetadata,
478
479 ring_buffer: Arc<MemoryObject>,
490
491 sq_entries: Arc<MemoryObject>,
500}
501
502impl IoUringQueue {
503 fn new(metadata: IoUringMetadata) -> Result<Self, Errno> {
504 let ring_buffer =
505 zx::Vmo::create(metadata.ring_buffer_size() as u64).map_err(|_| errno!(ENOMEM))?;
506 set_zx_name(&ring_buffer, b"io_uring:ring");
507 let sq_entries =
508 zx::Vmo::create(metadata.sq_entries_size() as u64).map_err(|_| errno!(ENOMEM))?;
509 set_zx_name(&sq_entries, b"io_uring:sqes");
510
511 Ok(Self {
512 metadata,
513 ring_buffer: Arc::new(ring_buffer.into()),
514 sq_entries: Arc::new(sq_entries.into()),
515 })
516 }
517
518 fn write_header(&self, header: ControlHeader) -> Result<(), Errno> {
519 write_object(&self.ring_buffer, 0, &header).map_err(|_| errno!(ENOMEM))
520 }
521
522 fn read_sq_head(&self) -> Result<u32, Errno> {
523 read_object(&self.ring_buffer, std::mem::offset_of!(ControlHeader, sq_head) as u64)
524 }
525
526 fn write_sq_head(&self, value: u32) -> Result<(), Errno> {
527 write_object(&self.ring_buffer, std::mem::offset_of!(ControlHeader, sq_head) as u64, &value)
528 }
529
530 fn read_sq_tail(&self) -> Result<u32, Errno> {
531 read_object(&self.ring_buffer, std::mem::offset_of!(ControlHeader, sq_tail) as u64)
534 }
535
536 fn read_cq_head(&self) -> Result<u32, Errno> {
537 read_object(&self.ring_buffer, std::mem::offset_of!(ControlHeader, cq_head) as u64)
540 }
541
542 fn read_cq_tail(&self) -> Result<u32, Errno> {
543 read_object(&self.ring_buffer, std::mem::offset_of!(ControlHeader, cq_tail) as u64)
544 }
545
546 fn write_cq_tail(&self, value: u32) -> Result<(), Errno> {
547 write_object(&self.ring_buffer, std::mem::offset_of!(ControlHeader, cq_tail) as u64, &value)
550 }
551
552 fn read_array_entry(&self, index: u32) -> Result<u32, Errno> {
553 read_object(&self.ring_buffer, self.metadata.array_entry_offset(index))
554 }
555
556 fn read_sq_entry(&self, index: u32) -> Result<SqEntry, Errno> {
557 let sqe_index = self.read_array_entry(index)?;
558 read_object(&self.sq_entries, self.metadata.sq_entry_offset(sqe_index))
559 }
560
561 fn write_cq_entry(&self, index: u32, entry: &CqEntry) -> Result<(), Errno> {
562 write_object(&self.ring_buffer, self.metadata.cq_entry_offset(index), entry)
563 }
564
565 fn increment_overflow(&self) -> Result<(), Errno> {
566 let offset = std::mem::offset_of!(ControlHeader, cq_overflow) as u64;
569 let mut overflow: u32 = read_object(&self.ring_buffer, offset)?;
570 overflow = overflow.saturating_add(1);
571 write_object(&self.ring_buffer, offset, &overflow)
572 }
573
574 fn pop_sq_entry(&self) -> Result<Option<SqEntry>, Errno> {
579 let tail = self.read_sq_tail()?;
580 let head = self.read_sq_head()?;
581 if head != tail {
582 let sq_entry = self.read_sq_entry(head)?;
583 self.write_sq_head(head.wrapping_add(1))?;
584 Ok(Some(sq_entry))
585 } else {
586 Ok(None)
587 }
588 }
589
590 fn push_cq_entry(&self, entry: &CqEntry) -> Result<(), Errno> {
596 let head = self.read_cq_head()?;
597 let tail = self.read_cq_tail()?;
598 if head != tail
601 && self.metadata.cq_entry_offset(tail) == self.metadata.cq_entry_offset(head)
602 {
603 self.increment_overflow()?;
604 } else {
605 self.write_cq_entry(tail, entry)?;
606 self.write_cq_tail(tail.wrapping_add(1))?;
607 }
608 Ok(())
609 }
610}
611
612pub struct IoUringFileObject {
613 queue: IoUringQueue,
614 state: LockDepMutex<IoUringFileMutableState, IoUringStateLock>,
615 _flags: IoRingSetupFlags,
616}
617
618#[derive(Default, Debug)]
619struct IoUringFileMutableState {
620 registered_buffers: UserBuffers,
621 registered_iobuffers: Vec<IoUringProviderRingBuffer>,
622}
623
624impl IoUringFileObject {
625 pub fn new_file(
626 current_task: &CurrentTask,
627 entries: u32,
628 params: &mut io_uring_params,
629 ) -> Result<FileHandle, Errno> {
630 let flags = IoRingSetupFlags::build_and_validate_from(params.flags)?;
631
632 let sq_entries = entries.next_power_of_two();
633 let cq_entries = if flags.contains(IoRingSetupFlags::CqSize) {
634 UserValue::from_raw(params.cq_entries)
635 .validate(sq_entries..IORING_MAX_CQ_ENTRIES)
636 .ok_or_else(|| errno!(EINVAL))?
637 .next_power_of_two()
638 } else {
639 sq_entries * 2
642 };
643
644 let queue =
645 IoUringQueue::new(IoUringMetadata { sq_entries: sq_entries, cq_entries: cq_entries })?;
646
647 queue.write_header(ControlHeader {
648 sq_ring_mask: sq_entries - 1,
649 cq_ring_mask: cq_entries - 1,
650 sq_ring_entries: sq_entries,
651 cq_ring_entries: cq_entries,
652 ..Default::default()
653 })?;
654
655 params.sq_entries = sq_entries;
656 params.cq_entries = cq_entries;
657 params.features = IORING_FEAT_SINGLE_MMAP;
658 params.sq_off = io_sqring_offsets {
659 head: std::mem::offset_of!(ControlHeader, sq_head) as u32,
660 tail: std::mem::offset_of!(ControlHeader, sq_tail) as u32,
661 ring_mask: std::mem::offset_of!(ControlHeader, sq_ring_mask) as u32,
662 ring_entries: std::mem::offset_of!(ControlHeader, sq_ring_entries) as u32,
663 flags: std::mem::offset_of!(ControlHeader, sq_flags) as u32,
664 dropped: std::mem::offset_of!(ControlHeader, sq_dropped) as u32,
665 array: queue.metadata.array_offset() as u32,
666 ..Default::default()
667 };
668 params.cq_off = io_cqring_offsets {
669 head: std::mem::offset_of!(ControlHeader, cq_head) as u32,
670 tail: std::mem::offset_of!(ControlHeader, cq_tail) as u32,
671 ring_mask: std::mem::offset_of!(ControlHeader, cq_ring_mask) as u32,
672 ring_entries: std::mem::offset_of!(ControlHeader, cq_ring_entries) as u32,
673 overflow: std::mem::offset_of!(ControlHeader, cq_overflow) as u32,
674 cqes: queue.metadata.cqes_offset() as u32,
675 flags: std::mem::offset_of!(ControlHeader, cq_flags) as u32,
676 ..Default::default()
677 };
678
679 let object =
680 Box::new(IoUringFileObject { queue, state: Default::default(), _flags: flags });
681 Anon::new_file(current_task, object, OpenFlags::RDWR, "[io_uring]")
682 }
683
684 pub fn register_buffers(&self, buffers: UserBuffers) {
685 self.state.lock().registered_buffers = buffers;
690 }
691
692 pub fn unregister_buffers(&self) {
693 self.state.lock().registered_buffers.clear();
694 }
695
696 pub fn register_ring_buffers(
697 &self,
698 buffer_definition: uapi::io_uring_buf_reg,
699 ) -> Result<(), Errno> {
700 track_stub!(
701 TODO("https://fxbug.dev/297431387"),
702 "IoUringFileObject::register_ring_buffers"
703 );
704 if !buffer_definition.ring_addr.is_multiple_of(*PAGE_SIZE) {
705 return error!(EINVAL);
706 }
707 if !buffer_definition.ring_entries.is_power_of_two() {
708 return error!(EINVAL);
709 }
710 if buffer_definition.ring_entries > IORING_MAX_ENTRIES {
711 return error!(EINVAL);
712 }
713 self.state
714 .lock()
715 .registered_iobuffers
716 .push(IoUringProviderRingBuffer::new(buffer_definition)?);
717 Ok(())
718 }
719
720 pub fn unregister_ring_buffers(
721 &self,
722 buffer_definition: uapi::io_uring_buf_reg,
723 ) -> Result<(), Errno> {
724 if self
725 .state
726 .lock()
727 .registered_iobuffers
728 .extract_if(.., |buffer| buffer.config.bgid == buffer_definition.bgid)
729 .next()
730 .is_none()
731 {
732 return error!(EINVAL);
733 }
734 Ok(())
735 }
736
737 pub fn ring_buffer_status(
738 &self,
739 buffer_status: &mut uapi::io_uring_buf_status,
740 ) -> Result<(), Errno> {
741 let state = self.state.lock();
742 let Some(buffer) = state
743 .registered_iobuffers
744 .iter()
745 .find(|buffer| buffer.config.bgid as u32 == buffer_status.buf_group)
746 else {
747 return error!(EINVAL);
748 };
749 buffer_status.head = buffer.head as u32;
750 Ok(())
751 }
752
753 pub fn enter(
754 &self,
755 current_task: &CurrentTask,
756 to_submit: u32,
757 _min_complete: u32,
758 _flags: u32,
759 ) -> Result<u32, Errno> {
760 let mut submitted = 0;
761 while let Some(sq_entry) = self.queue.pop_sq_entry()? {
762 submitted += 1;
763 let mut complete_flags: u32 = 0;
765 let result = self.execute(current_task, &sq_entry, &mut complete_flags);
766 let cq_entry = sq_entry.complete(result, complete_flags);
767 self.queue.push_cq_entry(&cq_entry)?;
768 if submitted >= to_submit {
769 break;
770 }
771 }
772 Ok(submitted)
773 }
774
775 fn has_registered_buffers(&self) -> bool {
776 !self.state.lock().registered_buffers.is_empty()
777 }
778
779 fn check_buffer(&self, entry: &SqEntry) -> Result<(), Errno> {
780 let index = entry.buf_index();
781 let state = self.state.lock();
782 let buffers = &state.registered_buffers;
783 if buffers.is_empty() {
784 return error!(EFAULT);
785 }
786 let buffer = buffers.get(index).ok_or_else(|| errno!(EINVAL))?;
787 if !buffer.contains(entry.address(), entry.length()) { error!(EFAULT) } else { Ok(()) }
788 }
789
790 fn execute(
791 &self,
792 current_task: &CurrentTask,
793 entry: &SqEntry,
794 complete_flags: &mut u32,
795 ) -> Result<SyscallResult, Errno> {
796 assert_eq!(*complete_flags, 0);
797
798 let flags = SqEntryFlags::from_bits(entry.flags).ok_or_else(|| errno!(EINVAL))?;
799 match Op::from_code(entry.opcode as io_uring_op)? {
800 Op::NOP => Ok(SUCCESS),
801 Op::ReadV => {
802 if !flags.is_empty() {
803 return error!(EINVAL);
804 }
805 if entry.ioprio != 0 || entry.buf_index() != 0 {
806 return error!(EINVAL);
807 }
808 sys_preadv2(
809 current_task,
810 entry.fd(),
811 entry.iovec_addr(current_task),
812 entry.iovec_count(),
813 entry.offset(),
814 SyscallArg::default(),
815 entry.op_flags,
816 )
817 .map(Into::into)
818 }
819 Op::WriteV => {
820 if !flags.is_empty() {
821 return error!(EINVAL);
822 }
823 if entry.ioprio != 0 || entry.buf_index() != 0 {
824 return error!(EINVAL);
825 }
826 sys_pwritev2(
827 current_task,
828 entry.fd(),
829 entry.iovec_addr(current_task),
830 entry.iovec_count(),
831 entry.offset(),
832 SyscallArg::default(),
833 entry.op_flags,
834 )
835 .map(Into::into)
836 }
837 Op::ReadFixed => {
838 if !flags.is_empty() {
839 return error!(EINVAL);
840 }
841 if entry.ioprio != 0 {
842 return error!(EINVAL);
843 }
844 self.check_buffer(entry)?;
848 do_read(current_task, entry)
849 }
850 Op::WriteFixed => {
851 if !flags.is_empty() {
852 return error!(EINVAL);
853 }
854 if entry.ioprio != 0 {
855 return error!(EINVAL);
856 }
857 self.check_buffer(entry)?;
861 do_write(current_task, entry)
862 }
863 Op::Read => {
864 if !flags.is_empty() {
865 return error!(EINVAL);
866 }
867 if self.has_registered_buffers() {
868 return error!(EINVAL);
869 }
870 do_read(current_task, entry)
871 }
872 Op::Write => {
873 if !flags.is_empty() {
874 return error!(EINVAL);
875 }
876 if self.has_registered_buffers() {
877 return error!(EINVAL);
878 }
879 do_write(current_task, entry)
880 }
881 Op::SendMsg => {
882 if !flags.is_empty() {
883 return error!(EINVAL);
884 }
885 if entry.ioprio != 0 {
886 return error!(EINVAL);
887 }
888 sys_sendmsg(
889 current_task,
890 entry.fd(),
891 MsgHdrPtr::new(current_task, entry.address()),
892 entry.op_flags,
893 )
894 .map(Into::into)
895 }
896 Op::RecvMsg => {
897 struct RecvMsgBufferInfo {
901 buffer: UserBuffer,
902 header: uapi::io_uring_recvmsg_out,
903 buffer_adjustment: usize,
904 }
905 let mut flags = flags;
906 let mut ioprio = entry.ioprio as u32;
907 let msg_hdr_ptr = MsgHdrPtr::new(current_task, entry.address());
908 let (mut msg_hdr_ref, recv_msg_buffer_info): (
909 MsgHdrRef,
910 Option<RecvMsgBufferInfo>,
911 ) = if flags.contains(SqEntryFlags::BUFFER_SELECT) {
912 flags -= SqEntryFlags::BUFFER_SELECT;
913 let buffer =
916 self.claim_next_buffer(current_task, entry.group(), complete_flags)?;
917 let mut msg_hdr = current_task.read_multi_arch_object(msg_hdr_ptr)?;
918 let headerlen: u32 = std::mem::size_of::<uapi::io_uring_recvmsg_out>() as u32;
924 let namelen: u32 = msg_hdr.name_len.try_into().map_err(|_| errno!(EINVAL))?;
925 let controllen: u32 =
926 msg_hdr.control_len.try_into().map_err(|_| errno!(EINVAL))?;
927 let buffer_adjustment: u32 = headerlen
928 .checked_add(namelen)
929 .and_then(|v| v.checked_add(controllen))
930 .ok_or_else(|| errno!(EINVAL))?;
931 let payloadlen: u32 = (buffer.length as u32)
932 .checked_sub(buffer_adjustment)
933 .ok_or_else(|| errno!(EINVAL))?;
934 let io_uring_hdr = uapi::io_uring_recvmsg_out {
935 namelen,
936 controllen,
937 payloadlen,
938 flags: msg_hdr.flags,
939 };
940
941 let name_addr = (buffer.address + headerlen as usize)?;
942 let control_addr = (name_addr + namelen as usize)?;
943 let payload_addr = (control_addr + controllen as usize)?;
944 msg_hdr.name = name_addr;
945 msg_hdr.control = control_addr;
946
947 let prefix_len = buffer_adjustment as usize;
950 if current_task.zero(buffer.address, prefix_len)? != prefix_len {
951 return error!(EFAULT);
952 }
953
954 let msg_hdr = WithAlternateBuffer::WithAux(
955 msg_hdr,
956 UserBuffer { address: payload_addr, length: payloadlen as usize },
957 );
958 (
959 msg_hdr.into(),
960 Some(RecvMsgBufferInfo {
961 buffer,
962 header: io_uring_hdr,
963 buffer_adjustment: buffer_adjustment as usize,
964 }),
965 )
966 } else {
967 (msg_hdr_ptr.into(), None)
968 };
969 if ioprio & uapi::IORING_RECV_MULTISHOT > 0 {
970 ioprio &= !uapi::IORING_RECV_MULTISHOT;
974 }
975 if !flags.is_empty() {
976 return error!(EINVAL);
977 }
978 if ioprio != 0 {
979 return error!(EINVAL);
980 }
981 let mut count =
982 recvmsg_impl(current_task, entry.fd(), &mut msg_hdr_ref, entry.op_flags)?;
983 if let Some(recv_msg_buffer_info) = recv_msg_buffer_info {
984 current_task.write_object(
989 recv_msg_buffer_info.buffer.address.into(),
990 &recv_msg_buffer_info.header,
991 )?;
992 count += recv_msg_buffer_info.buffer_adjustment;
994 }
995 Ok(count.into())
996 }
997 Op::Send => {
998 if !flags.is_empty() {
999 return error!(EINVAL);
1000 }
1001 if entry.ioprio != 0 {
1002 return error!(EINVAL);
1003 }
1004 sys_sendto(
1005 current_task,
1006 entry.fd(),
1007 entry.address(),
1008 entry.length(),
1009 entry.op_flags,
1010 UserAddress::default(),
1011 socklen_t::default(),
1012 )
1013 .map(Into::into)
1014 }
1015 Op::Recv => {
1016 if !flags.is_empty() {
1017 return error!(EINVAL);
1018 }
1019 if entry.ioprio != 0 {
1020 return error!(EINVAL);
1021 }
1022 sys_recvfrom(
1023 current_task,
1024 entry.fd(),
1025 entry.address(),
1026 entry.length(),
1027 entry.op_flags,
1028 UserAddress::default(),
1029 UserRef::default(),
1030 )
1031 .map(Into::into)
1032 }
1033 }
1034 }
1035
1036 fn claim_next_buffer(
1037 &self,
1038 current_task: &CurrentTask,
1039 bgid: u16,
1040 complete_flags: &mut u32,
1041 ) -> Result<UserBuffer, Errno> {
1042 let mut state = self.state.lock();
1043 let Some(buffer) =
1044 state.registered_iobuffers.iter_mut().find(|buffer| buffer.config.bgid == bgid)
1045 else {
1046 return error!(EINVAL);
1047 };
1048 buffer.claim_next(current_task, complete_flags)
1049 }
1050}
1051
1052#[derive(Debug)]
1053struct IoUringProviderRingBuffer {
1054 config: uapi::io_uring_buf_reg,
1055 tail_ptr: UserRef<u16>,
1056 entries_ptr: UserRef<UserRingBufferEntry>,
1057 head: u16,
1058}
1059
1060impl IoUringProviderRingBuffer {
1061 fn new(config: uapi::io_uring_buf_reg) -> Result<Self, Errno> {
1062 let ring_addr = UserAddress::from(config.ring_addr);
1063 let tail_ptr =
1064 UserRef::<u16>::from((ring_addr + std::mem::offset_of!(UserRingBufferHeader, tail))?);
1065 let entries_ptr = UserRef::<UserRingBufferEntry>::from(ring_addr);
1066 Ok(Self { config, tail_ptr, entries_ptr, head: 0 })
1067 }
1068
1069 fn claim_next(
1070 &mut self,
1071 current_task: &CurrentTask,
1072 complete_flags: &mut u32,
1073 ) -> Result<UserBuffer, Errno> {
1074 let tail = current_task.read_object(self.tail_ptr)?;
1077 if self.head == tail {
1078 return error!(ENOBUFS);
1079 }
1080 let buffer_info = current_task.read_object(
1081 self.entries_ptr.at((self.head as usize) % (self.config.ring_entries as usize))?,
1082 )?;
1083 self.head += 1;
1084 *complete_flags |=
1085 uapi::IORING_CQE_F_BUFFER | ((buffer_info.bid as u32) << uapi::IORING_CQE_BUFFER_SHIFT);
1086 Ok(UserBuffer { address: buffer_info.addr.into(), length: buffer_info.len as usize })
1087 }
1088}
1089
1090fn do_read(current_task: &CurrentTask, entry: &SqEntry) -> Result<SyscallResult, Errno> {
1091 let offset = entry.offset();
1092 if offset == -1 {
1093 sys_read(current_task, entry.fd(), entry.address(), entry.length()).map(Into::into)
1094 } else {
1095 sys_pread64(current_task, entry.fd(), entry.address(), entry.length(), offset)
1096 .map(Into::into)
1097 }
1098}
1099
1100fn do_write(current_task: &CurrentTask, entry: &SqEntry) -> Result<SyscallResult, Errno> {
1101 let offset = entry.offset();
1102 if offset == -1 {
1103 sys_write(current_task, entry.fd(), entry.address(), entry.length()).map(Into::into)
1104 } else {
1105 sys_pwrite64(current_task, entry.fd(), entry.address(), entry.length(), entry.offset())
1106 .map(Into::into)
1107 }
1108}
1109
1110impl FileOps for IoUringFileObject {
1111 fileops_impl_nonseekable!();
1112 fileops_impl_noop_sync!();
1113 fileops_impl_dataless!();
1114
1115 fn can_map_executable(&self) -> bool {
1117 false
1118 }
1119
1120 fn mmap(
1121 &self,
1122 file: &FileObject,
1123 current_task: &CurrentTask,
1124 addr: DesiredAddress,
1125 memory_offset: u64,
1126 length: usize,
1127 prot_flags: ProtectionFlags,
1128 options: MappingOptions,
1129 ) -> Result<UserAddress, Errno> {
1130 if !options.contains(MappingOptions::SHARED) {
1131 return error!(EINVAL);
1132 }
1133 let magic_offset: u32 = memory_offset.try_into().map_err(|_| errno!(EINVAL))?;
1134 let memory = match magic_offset {
1135 IORING_OFF_SQ_RING | IORING_OFF_CQ_RING => self.queue.ring_buffer.clone(),
1136 IORING_OFF_SQES => self.queue.sq_entries.clone(),
1137 _ => return error!(EINVAL),
1138 };
1139 current_task.mm()?.map_memory(
1140 addr,
1141 memory,
1142 0,
1143 length,
1144 prot_flags,
1145 options,
1146 MappingName::File(file.to_mapping(None)?),
1147 )
1148 }
1149}
1150
1151#[cfg(test)]
1152mod tests {
1153 use super::*;
1154
1155 #[::fuchsia::test]
1156 fn test_uring_cmd_not_supported() {
1157 assert_eq!(
1160 Op::from_code(starnix_uapi::io_uring_op_IORING_OP_URING_CMD).err(),
1161 Some(errno!(EOPNOTSUPP))
1162 );
1163 }
1164}