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, NamespaceNode, fileops_impl_dataless,
24 fileops_impl_nonseekable, fileops_impl_noop_sync,
25};
26use starnix_logging::{set_zx_name, track_stub};
27use starnix_sync::{
28 FileOpsCore, IoUringStateLock, LockEqualOrBefore, Locked, OrderedMutex, Unlocked,
29};
30use starnix_syscalls::{SUCCESS, SyscallArg, SyscallResult};
31use starnix_types::user_buffer::{UserBuffer, UserBuffers};
32use starnix_uapi::errors::Errno;
33use starnix_uapi::file_mode::Access;
34use starnix_uapi::open_flags::OpenFlags;
35use starnix_uapi::user_address::{ArchSpecific, UserAddress, UserRef};
36use starnix_uapi::user_value::UserValue;
37use starnix_uapi::{
38 IORING_FEAT_SINGLE_MMAP, IORING_OFF_CQ_RING, IORING_OFF_SQ_RING, IORING_OFF_SQES, errno, error,
39 io_cqring_offsets, io_sqring_offsets, io_uring_cqe, io_uring_op, io_uring_op_IORING_OP_ACCEPT,
40 io_uring_op_IORING_OP_ASYNC_CANCEL, io_uring_op_IORING_OP_CLOSE, io_uring_op_IORING_OP_CONNECT,
41 io_uring_op_IORING_OP_EPOLL_CTL, io_uring_op_IORING_OP_FADVISE,
42 io_uring_op_IORING_OP_FALLOCATE, io_uring_op_IORING_OP_FILES_UPDATE,
43 io_uring_op_IORING_OP_FSYNC, io_uring_op_IORING_OP_LINK_TIMEOUT, io_uring_op_IORING_OP_MADVISE,
44 io_uring_op_IORING_OP_NOP, io_uring_op_IORING_OP_OPENAT, io_uring_op_IORING_OP_OPENAT2,
45 io_uring_op_IORING_OP_POLL_ADD, io_uring_op_IORING_OP_POLL_REMOVE, io_uring_op_IORING_OP_READ,
46 io_uring_op_IORING_OP_READ_FIXED, io_uring_op_IORING_OP_READV, io_uring_op_IORING_OP_RECV,
47 io_uring_op_IORING_OP_RECVMSG, io_uring_op_IORING_OP_SEND, io_uring_op_IORING_OP_SENDMSG,
48 io_uring_op_IORING_OP_STATX, io_uring_op_IORING_OP_SYNC_FILE_RANGE,
49 io_uring_op_IORING_OP_TIMEOUT, io_uring_op_IORING_OP_TIMEOUT_REMOVE,
50 io_uring_op_IORING_OP_WRITE, io_uring_op_IORING_OP_WRITE_FIXED, io_uring_op_IORING_OP_WRITEV,
51 io_uring_params, io_uring_sqe, io_uring_sqe_flags_bit_IOSQE_ASYNC_BIT,
52 io_uring_sqe_flags_bit_IOSQE_BUFFER_SELECT_BIT,
53 io_uring_sqe_flags_bit_IOSQE_CQE_SKIP_SUCCESS_BIT, io_uring_sqe_flags_bit_IOSQE_FIXED_FILE_BIT,
54 io_uring_sqe_flags_bit_IOSQE_IO_DRAIN_BIT, io_uring_sqe_flags_bit_IOSQE_IO_HARDLINK_BIT,
55 io_uring_sqe_flags_bit_IOSQE_IO_LINK_BIT, off_t, socklen_t, uapi,
56};
57use std::sync::Arc;
58use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
59
60pub const IORING_MAX_ENTRIES: u32 = 1 << 15; const IORING_MAX_CQ_ENTRIES: u32 = 2 * IORING_MAX_ENTRIES;
63
64bitflags! {
65 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
66 pub struct IoRingSetupFlags: u32 {
67 const IoPoll = starnix_uapi::IORING_SETUP_IOPOLL;
68 const SqPoll = starnix_uapi::IORING_SETUP_SQPOLL;
69 const SqAff = starnix_uapi::IORING_SETUP_SQ_AFF;
70 const CqSize = starnix_uapi::IORING_SETUP_CQSIZE;
71 const Clamp = starnix_uapi::IORING_SETUP_CLAMP;
72 const AttachWq = starnix_uapi::IORING_SETUP_ATTACH_WQ;
73 const RDisabled = starnix_uapi::IORING_SETUP_R_DISABLED;
74 const SubmitAll = starnix_uapi::IORING_SETUP_SUBMIT_ALL;
75 const CoopTaskRun = starnix_uapi::IORING_SETUP_COOP_TASKRUN;
76 const TaskRunFlag = starnix_uapi::IORING_SETUP_TASKRUN_FLAG;
77 const SqE128 = starnix_uapi::IORING_SETUP_SQE128;
78 const CqE32 = starnix_uapi::IORING_SETUP_CQE32;
79 const SingleIssuer = starnix_uapi::IORING_SETUP_SINGLE_ISSUER;
80 const DeferTaskRun = starnix_uapi::IORING_SETUP_DEFER_TASKRUN;
81 const NoMmap = starnix_uapi::IORING_SETUP_NO_MMAP;
82 const RegisteredFdOnly = starnix_uapi::IORING_SETUP_REGISTERED_FD_ONLY;
83 const NoSqArray = starnix_uapi::IORING_SETUP_NO_SQARRAY;
84
85 const SupportedFlags = starnix_uapi::IORING_SETUP_CQSIZE |
88 starnix_uapi::IORING_SETUP_COOP_TASKRUN |
89 starnix_uapi::IORING_SETUP_TASKRUN_FLAG |
90 starnix_uapi::IORING_SETUP_SINGLE_ISSUER |
91 starnix_uapi::IORING_SETUP_DEFER_TASKRUN;
92
93 const IgnoredFlags = starnix_uapi::IORING_SETUP_COOP_TASKRUN |
97 starnix_uapi::IORING_SETUP_TASKRUN_FLAG |
98 starnix_uapi::IORING_SETUP_SINGLE_ISSUER |
99 starnix_uapi::IORING_SETUP_DEFER_TASKRUN;
100 }
101
102 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
103 struct SqEntryFlags: u8 {
104 const FIXED_FILE = 1 << io_uring_sqe_flags_bit_IOSQE_FIXED_FILE_BIT;
105 const IO_DRAIN = 1 << io_uring_sqe_flags_bit_IOSQE_IO_DRAIN_BIT;
106 const IO_LINK = 1 << io_uring_sqe_flags_bit_IOSQE_IO_LINK_BIT;
107 const IO_HARDLINK = 1 << io_uring_sqe_flags_bit_IOSQE_IO_HARDLINK_BIT;
108 const ASYNC = 1 << io_uring_sqe_flags_bit_IOSQE_ASYNC_BIT;
109 const BUFFER_SELECT = 1 << io_uring_sqe_flags_bit_IOSQE_BUFFER_SELECT_BIT;
110 const CQE_SKIP_SUCCESS = 1 << io_uring_sqe_flags_bit_IOSQE_CQE_SKIP_SUCCESS_BIT;
111 }
112}
113
114impl IoRingSetupFlags {
115 fn build_and_validate_from(value: u32) -> Result<Self, Errno> {
116 let Some(flags) = IoRingSetupFlags::from_bits(value) else {
117 track_stub!(
118 TODO("https://fxbug.dev/297431387"),
119 "io_uring_setup undefined flag(s)",
120 value
121 );
122 return error!(EINVAL);
123 };
124
125 let unsupported_flags = flags.difference(IoRingSetupFlags::SupportedFlags);
126 if !unsupported_flags.is_empty() {
127 track_stub!(
128 TODO("https://fxbug.dev/297431387"),
129 "io_uring_setup unsupported flags",
130 unsupported_flags.bits()
131 );
132 return error!(EINVAL);
133 }
134 let ignored_flags = flags.intersection(IoRingSetupFlags::IgnoredFlags);
135 if !ignored_flags.is_empty() {
136 track_stub!(
137 TODO("https://fxbug.dev/297431387"),
138 "io_uring_setup ignored flags",
139 ignored_flags.bits()
140 );
141 }
142
143 if flags.contains(IoRingSetupFlags::DeferTaskRun)
145 && !flags.contains(IoRingSetupFlags::SingleIssuer)
146 {
147 return error!(EINVAL);
148 }
149
150 return Ok(flags);
151 }
152}
153
154type RingIndex = u32;
155
156type UserRingBufferHeader = uapi::io_uring_buf_ring__bindgen_ty_1__bindgen_ty_1;
157type UserRingBufferEntry = uapi::io_uring_buf;
158
159static_assertions::const_assert_eq!(
160 std::mem::size_of::<u16>(),
161 uapi::size_of_field!(UserRingBufferHeader, tail)
162);
163static_assertions::const_assert_eq!(
164 std::mem::size_of::<UserRingBufferHeader>(),
165 std::mem::size_of::<UserRingBufferEntry>()
166);
167
168#[repr(C)]
178#[derive(Debug, Default, Copy, Clone, IntoBytes, FromBytes, KnownLayout, Immutable)]
179struct ControlHeader {
180 sq_head: u32,
186
187 sq_tail: u32,
192
193 cq_head: u32,
199
200 cq_tail: u32,
205
206 sq_ring_mask: u32,
208
209 cq_ring_mask: u32,
211
212 sq_ring_entries: u32,
214
215 cq_ring_entries: u32,
217
218 sq_dropped: u32,
220
221 sq_flags: u32,
222 cq_flags: u32,
223
224 cq_overflow: u32,
227
228 _padding: [u8; 16],
229}
230
231const RING_ALIGNMENT: usize = 64;
232
233static_assertions::const_assert_eq!(std::mem::size_of::<ControlHeader>(), RING_ALIGNMENT);
235
236#[repr(C)]
242#[derive(Debug, Default, Copy, Clone, IntoBytes, FromBytes, KnownLayout, Immutable)]
243struct SqEntry {
244 opcode: u8,
245 flags: u8,
246 ioprio: u16,
247 raw_fd: i32,
248 field0: u64,
249 field1: u64,
250 len: u32,
251 op_flags: u32,
252 user_data: u64,
253 buf_index_or_group: u16,
254 personality: u16,
255 field2: u32,
256 field3: [u64; 2usize],
257}
258
259uapi::check_arch_independent_same_layout! {
260 SqEntry = io_uring_sqe {
261 opcode => opcode,
262 flags => flags,
263 ioprio => ioprio,
264 raw_fd => fd,
265 field0 => __bindgen_anon_1,
266 field1 => __bindgen_anon_2,
267 len => len,
268 op_flags => __bindgen_anon_3,
269 user_data => user_data,
270 buf_index_or_group => __bindgen_anon_4,
271 personality => personality,
272 field2 => __bindgen_anon_5,
273 field3 => __bindgen_anon_6,
274 }
275}
276
277uapi::check_arch_independent_layout! {
278 io_uring_recvmsg_out{
279 namelen,
280 controllen,
281 payloadlen,
282 flags,
283 }
284}
285
286impl SqEntry {
287 fn complete(&self, result: Result<SyscallResult, Errno>, flags: u32) -> CqEntry {
288 let res = match result {
289 Ok(return_value) => return_value.value() as i32,
290 Err(errno) => errno.return_value() as i32,
291 };
292 CqEntry { user_data: self.user_data, res, flags }
293 }
294
295 fn fd(&self) -> FdNumber {
296 FdNumber::from_raw(self.raw_fd)
297 }
298
299 fn iovec_addr<Arch: ArchSpecific>(&self, arch: &Arch) -> IOVecPtr {
300 IOVecPtr::new(arch, self.field1)
301 }
302
303 fn iovec_count(&self) -> UserValue<i32> {
304 (self.len as i32).into()
305 }
306
307 fn address(&self) -> UserAddress {
308 self.field1.into()
309 }
310
311 fn length(&self) -> usize {
312 self.len as usize
313 }
314
315 fn offset(&self) -> off_t {
316 self.field0 as off_t
317 }
318
319 fn buf_index(&self) -> usize {
320 self.buf_index_or_group as usize
321 }
322
323 fn group(&self) -> u16 {
324 self.buf_index_or_group
325 }
326}
327
328#[repr(C)]
334#[derive(Debug, Default, Copy, Clone, IntoBytes, FromBytes, KnownLayout, Immutable)]
335struct CqEntry {
336 pub user_data: u64,
337 pub res: i32,
338 pub flags: u32,
339}
340
341static_assertions::assert_eq_size!(CqEntry, io_uring_cqe);
342static_assertions::const_assert_eq!(
343 std::mem::offset_of!(CqEntry, user_data),
344 std::mem::offset_of!(io_uring_cqe, user_data)
345);
346static_assertions::const_assert_eq!(
347 std::mem::offset_of!(CqEntry, res),
348 std::mem::offset_of!(io_uring_cqe, res)
349);
350static_assertions::const_assert_eq!(
351 std::mem::offset_of!(CqEntry, flags),
352 std::mem::offset_of!(io_uring_cqe, flags)
353);
354
355const CQES_OFFSET: usize = std::mem::size_of::<ControlHeader>();
356
357#[inline]
358fn align_ring_field(offset: usize) -> usize {
359 offset.next_multiple_of(RING_ALIGNMENT)
360}
361struct IoUringMetadata {
362 sq_entries: u32,
364
365 cq_entries: u32,
367}
368
369impl IoUringMetadata {
370 fn cq_entry_offset(&self, index: u32) -> u64 {
374 let index = index % self.cq_entries;
375 (CQES_OFFSET + index as usize * std::mem::size_of::<io_uring_cqe>()) as u64
376 }
377
378 fn cqes_offset(&self) -> usize {
380 CQES_OFFSET
381 }
382
383 fn array_offset(&self) -> usize {
385 CQES_OFFSET
386 + align_ring_field(self.cq_entries as usize * std::mem::size_of::<io_uring_cqe>())
387 }
388
389 fn array_entry_offset(&self, index: u32) -> u64 {
392 let index = index % self.sq_entries;
393 (self.array_offset() + index as usize * std::mem::size_of::<RingIndex>()) as u64
394 }
395
396 fn ring_buffer_size(&self) -> usize {
398 self.array_offset() + self.sq_entries as usize * std::mem::size_of::<RingIndex>()
399 }
400
401 fn sq_entry_offset(&self, index: u32) -> u64 {
406 let index = index % self.sq_entries;
407 (index as usize * std::mem::size_of::<io_uring_sqe>()) as u64
408 }
409
410 fn sq_entries_size(&self) -> usize {
412 self.sq_entries as usize * std::mem::size_of::<io_uring_sqe>()
413 }
414}
415
416#[repr(u32)]
417enum Op {
418 Accept = io_uring_op_IORING_OP_ACCEPT,
419 AsyncCancel = io_uring_op_IORING_OP_ASYNC_CANCEL,
420 Close = io_uring_op_IORING_OP_CLOSE,
421 Connect = io_uring_op_IORING_OP_CONNECT,
422 EpollCtl = io_uring_op_IORING_OP_EPOLL_CTL,
423 FAdvise = io_uring_op_IORING_OP_FADVISE,
424 FAllocate = io_uring_op_IORING_OP_FALLOCATE,
425 FilesUpdate = io_uring_op_IORING_OP_FILES_UPDATE,
426 FSync = io_uring_op_IORING_OP_FSYNC,
427 LinkTimeout = io_uring_op_IORING_OP_LINK_TIMEOUT,
428 MAdvise = io_uring_op_IORING_OP_MADVISE,
429 NOP = io_uring_op_IORING_OP_NOP,
430 OpenAt = io_uring_op_IORING_OP_OPENAT,
431 OpenAt2 = io_uring_op_IORING_OP_OPENAT2,
432 PollAdd = io_uring_op_IORING_OP_POLL_ADD,
433 PollRemove = io_uring_op_IORING_OP_POLL_REMOVE,
434 Read = io_uring_op_IORING_OP_READ,
435 ReadV = io_uring_op_IORING_OP_READV,
436 ReadFixed = io_uring_op_IORING_OP_READ_FIXED,
437 Recv = io_uring_op_IORING_OP_RECV,
438 RecvMsg = io_uring_op_IORING_OP_RECVMSG,
439 Send = io_uring_op_IORING_OP_SEND,
440 SendMsg = io_uring_op_IORING_OP_SENDMSG,
441 StatX = io_uring_op_IORING_OP_STATX,
442 SyncFileRange = io_uring_op_IORING_OP_SYNC_FILE_RANGE,
443 Timeout = io_uring_op_IORING_OP_TIMEOUT,
444 TimeoutRemove = io_uring_op_IORING_OP_TIMEOUT_REMOVE,
445 Write = io_uring_op_IORING_OP_WRITE,
446 WriteV = io_uring_op_IORING_OP_WRITEV,
447 WriteFixed = io_uring_op_IORING_OP_WRITE_FIXED,
448}
449
450impl Op {
451 fn from_code(opcode: io_uring_op) -> Result<Op, Errno> {
452 match opcode {
453 io_uring_op_IORING_OP_ACCEPT => Ok(Self::Accept),
454 io_uring_op_IORING_OP_ASYNC_CANCEL => Ok(Self::AsyncCancel),
455 io_uring_op_IORING_OP_CLOSE => Ok(Self::Close),
456 io_uring_op_IORING_OP_CONNECT => Ok(Self::Connect),
457 io_uring_op_IORING_OP_EPOLL_CTL => Ok(Self::EpollCtl),
458 io_uring_op_IORING_OP_FADVISE => Ok(Self::FAdvise),
459 io_uring_op_IORING_OP_FALLOCATE => Ok(Self::FAllocate),
460 io_uring_op_IORING_OP_FILES_UPDATE => Ok(Self::FilesUpdate),
461 io_uring_op_IORING_OP_FSYNC => Ok(Self::FSync),
462 io_uring_op_IORING_OP_LINK_TIMEOUT => Ok(Self::LinkTimeout),
463 io_uring_op_IORING_OP_MADVISE => Ok(Self::MAdvise),
464 io_uring_op_IORING_OP_NOP => Ok(Self::NOP),
465 io_uring_op_IORING_OP_OPENAT => Ok(Self::OpenAt),
466 io_uring_op_IORING_OP_OPENAT2 => Ok(Self::OpenAt2),
467 io_uring_op_IORING_OP_POLL_ADD => Ok(Self::PollAdd),
468 io_uring_op_IORING_OP_POLL_REMOVE => Ok(Self::PollRemove),
469 io_uring_op_IORING_OP_READ => Ok(Self::Read),
470 io_uring_op_IORING_OP_READV => Ok(Self::ReadV),
471 io_uring_op_IORING_OP_READ_FIXED => Ok(Self::ReadFixed),
472 io_uring_op_IORING_OP_RECV => Ok(Self::Recv),
473 io_uring_op_IORING_OP_RECVMSG => Ok(Self::RecvMsg),
474 io_uring_op_IORING_OP_SEND => Ok(Self::Send),
475 io_uring_op_IORING_OP_SENDMSG => Ok(Self::SendMsg),
476 io_uring_op_IORING_OP_STATX => Ok(Self::StatX),
477 io_uring_op_IORING_OP_SYNC_FILE_RANGE => Ok(Self::SyncFileRange),
478 io_uring_op_IORING_OP_TIMEOUT => Ok(Self::Timeout),
479 io_uring_op_IORING_OP_TIMEOUT_REMOVE => Ok(Self::TimeoutRemove),
480 io_uring_op_IORING_OP_WRITE => Ok(Self::Write),
481 io_uring_op_IORING_OP_WRITEV => Ok(Self::WriteV),
482 io_uring_op_IORING_OP_WRITE_FIXED => Ok(Self::WriteFixed),
483 _ => error!(EINVAL),
484 }
485 }
486}
487
488fn read_object<T: FromBytes>(memory_object: &MemoryObject, offset: u64) -> Result<T, Errno> {
496 unsafe {
498 read_to_object_as_bytes(|buf| {
499 memory_object.read_uninit(buf, offset).map_err(|_| errno!(EFAULT))?;
500 Ok(())
501 })
502 }
503}
504
505fn write_object<T: IntoBytes + Immutable>(
508 memory_object: &MemoryObject,
509 offset: u64,
510 value: &T,
511) -> Result<(), Errno> {
512 memory_object.write(value.as_bytes(), offset).map_err(|_| errno!(EFAULT))
513}
514
515struct IoUringQueue {
517 metadata: IoUringMetadata,
519
520 ring_buffer: Arc<MemoryObject>,
531
532 sq_entries: Arc<MemoryObject>,
541}
542
543impl IoUringQueue {
544 fn new(metadata: IoUringMetadata) -> Result<Self, Errno> {
545 let ring_buffer =
546 zx::Vmo::create(metadata.ring_buffer_size() as u64).map_err(|_| errno!(ENOMEM))?;
547 set_zx_name(&ring_buffer, b"io_uring:ring");
548 let sq_entries =
549 zx::Vmo::create(metadata.sq_entries_size() as u64).map_err(|_| errno!(ENOMEM))?;
550 set_zx_name(&sq_entries, b"io_uring:sqes");
551
552 Ok(Self {
553 metadata,
554 ring_buffer: Arc::new(ring_buffer.into()),
555 sq_entries: Arc::new(sq_entries.into()),
556 })
557 }
558
559 fn write_header(&self, header: ControlHeader) -> Result<(), Errno> {
560 write_object(&self.ring_buffer, 0, &header).map_err(|_| errno!(ENOMEM))
561 }
562
563 fn read_sq_head(&self) -> Result<u32, Errno> {
564 read_object(&self.ring_buffer, std::mem::offset_of!(ControlHeader, sq_head) as u64)
565 }
566
567 fn write_sq_head(&self, value: u32) -> Result<(), Errno> {
568 write_object(&self.ring_buffer, std::mem::offset_of!(ControlHeader, sq_head) as u64, &value)
569 }
570
571 fn read_sq_tail(&self) -> Result<u32, Errno> {
572 read_object(&self.ring_buffer, std::mem::offset_of!(ControlHeader, sq_tail) as u64)
575 }
576
577 fn read_cq_head(&self) -> Result<u32, Errno> {
578 read_object(&self.ring_buffer, std::mem::offset_of!(ControlHeader, cq_head) as u64)
581 }
582
583 fn read_cq_tail(&self) -> Result<u32, Errno> {
584 read_object(&self.ring_buffer, std::mem::offset_of!(ControlHeader, cq_tail) as u64)
585 }
586
587 fn write_cq_tail(&self, value: u32) -> Result<(), Errno> {
588 write_object(&self.ring_buffer, std::mem::offset_of!(ControlHeader, cq_tail) as u64, &value)
591 }
592
593 fn read_array_entry(&self, index: u32) -> Result<u32, Errno> {
594 read_object(&self.ring_buffer, self.metadata.array_entry_offset(index))
595 }
596
597 fn read_sq_entry(&self, index: u32) -> Result<SqEntry, Errno> {
598 let sqe_index = self.read_array_entry(index)?;
599 read_object(&self.sq_entries, self.metadata.sq_entry_offset(sqe_index))
600 }
601
602 fn write_cq_entry(&self, index: u32, entry: &CqEntry) -> Result<(), Errno> {
603 write_object(&self.ring_buffer, self.metadata.cq_entry_offset(index), entry)
604 }
605
606 fn increment_overflow(&self) -> Result<(), Errno> {
607 let offset = std::mem::offset_of!(ControlHeader, cq_overflow) as u64;
610 let mut overflow: u32 = read_object(&self.ring_buffer, offset)?;
611 overflow = overflow.saturating_add(1);
612 write_object(&self.ring_buffer, offset, &overflow)
613 }
614
615 fn pop_sq_entry(&self) -> Result<Option<SqEntry>, Errno> {
620 let tail = self.read_sq_tail()?;
621 let head = self.read_sq_head()?;
622 if head != tail {
623 let sq_entry = self.read_sq_entry(head)?;
624 self.write_sq_head(head.wrapping_add(1))?;
625 Ok(Some(sq_entry))
626 } else {
627 Ok(None)
628 }
629 }
630
631 fn push_cq_entry(&self, entry: &CqEntry) -> Result<(), Errno> {
637 let head = self.read_cq_head()?;
638 let tail = self.read_cq_tail()?;
639 if head != tail
642 && self.metadata.cq_entry_offset(tail) == self.metadata.cq_entry_offset(head)
643 {
644 self.increment_overflow()?;
645 } else {
646 self.write_cq_entry(tail, entry)?;
647 self.write_cq_tail(tail.wrapping_add(1))?;
648 }
649 Ok(())
650 }
651}
652
653pub struct IoUringFileObject {
654 queue: IoUringQueue,
655 state: OrderedMutex<IoUringFileMutableState, IoUringStateLock>,
656 _flags: IoRingSetupFlags,
657}
658
659#[derive(Default, Debug)]
660struct IoUringFileMutableState {
661 registered_buffers: UserBuffers,
662 registered_iobuffers: Vec<IoUringProviderRingBuffer>,
663}
664
665impl IoUringFileObject {
666 pub fn new_file<L>(
667 locked: &mut Locked<L>,
668 current_task: &CurrentTask,
669 entries: u32,
670 params: &mut io_uring_params,
671 ) -> Result<FileHandle, Errno>
672 where
673 L: LockEqualOrBefore<FileOpsCore>,
674 {
675 let flags = IoRingSetupFlags::build_and_validate_from(params.flags)?;
676
677 let sq_entries = entries.next_power_of_two();
678 let cq_entries = if flags.contains(IoRingSetupFlags::CqSize) {
679 UserValue::from_raw(params.cq_entries)
680 .validate(sq_entries..IORING_MAX_CQ_ENTRIES)
681 .ok_or_else(|| errno!(EINVAL))?
682 .next_power_of_two()
683 } else {
684 sq_entries * 2
687 };
688
689 let queue =
690 IoUringQueue::new(IoUringMetadata { sq_entries: sq_entries, cq_entries: cq_entries })?;
691
692 queue.write_header(ControlHeader {
693 sq_ring_mask: sq_entries - 1,
694 cq_ring_mask: cq_entries - 1,
695 sq_ring_entries: sq_entries,
696 cq_ring_entries: cq_entries,
697 ..Default::default()
698 })?;
699
700 params.sq_entries = sq_entries;
701 params.cq_entries = cq_entries;
702 params.features = IORING_FEAT_SINGLE_MMAP;
703 params.sq_off = io_sqring_offsets {
704 head: std::mem::offset_of!(ControlHeader, sq_head) as u32,
705 tail: std::mem::offset_of!(ControlHeader, sq_tail) as u32,
706 ring_mask: std::mem::offset_of!(ControlHeader, sq_ring_mask) as u32,
707 ring_entries: std::mem::offset_of!(ControlHeader, sq_ring_entries) as u32,
708 flags: std::mem::offset_of!(ControlHeader, sq_flags) as u32,
709 dropped: std::mem::offset_of!(ControlHeader, sq_dropped) as u32,
710 array: queue.metadata.array_offset() as u32,
711 ..Default::default()
712 };
713 params.cq_off = io_cqring_offsets {
714 head: std::mem::offset_of!(ControlHeader, cq_head) as u32,
715 tail: std::mem::offset_of!(ControlHeader, cq_tail) as u32,
716 ring_mask: std::mem::offset_of!(ControlHeader, cq_ring_mask) as u32,
717 ring_entries: std::mem::offset_of!(ControlHeader, cq_ring_entries) as u32,
718 overflow: std::mem::offset_of!(ControlHeader, cq_overflow) as u32,
719 cqes: queue.metadata.cqes_offset() as u32,
720 flags: std::mem::offset_of!(ControlHeader, cq_flags) as u32,
721 ..Default::default()
722 };
723
724 let object =
725 Box::new(IoUringFileObject { queue, state: Default::default(), _flags: flags });
726 Anon::new_file(locked, current_task, object, OpenFlags::RDWR, "[io_uring]")
727 }
728
729 pub fn register_buffers(&self, locked: &mut Locked<Unlocked>, buffers: UserBuffers) {
730 self.state.lock(locked).registered_buffers = buffers;
735 }
736
737 pub fn unregister_buffers(&self, locked: &mut Locked<Unlocked>) {
738 self.state.lock(locked).registered_buffers.clear();
739 }
740
741 pub fn register_ring_buffers(
742 &self,
743 locked: &mut Locked<Unlocked>,
744 buffer_definition: uapi::io_uring_buf_reg,
745 ) -> Result<(), Errno> {
746 track_stub!(
747 TODO("https://fxbug.dev/297431387"),
748 "IoUringFileObject::register_ring_buffers"
749 );
750 if !buffer_definition.ring_addr.is_multiple_of(*PAGE_SIZE) {
751 return error!(EINVAL);
752 }
753 if !buffer_definition.ring_entries.is_power_of_two() {
754 return error!(EINVAL);
755 }
756 if buffer_definition.ring_entries > IORING_MAX_ENTRIES {
757 return error!(EINVAL);
758 }
759 self.state
760 .lock(locked)
761 .registered_iobuffers
762 .push(IoUringProviderRingBuffer::new(buffer_definition)?);
763 Ok(())
764 }
765
766 pub fn unregister_ring_buffers(
767 &self,
768 locked: &mut Locked<Unlocked>,
769 buffer_definition: uapi::io_uring_buf_reg,
770 ) -> Result<(), Errno> {
771 if self
772 .state
773 .lock(locked)
774 .registered_iobuffers
775 .extract_if(.., |buffer| buffer.config.bgid == buffer_definition.bgid)
776 .next()
777 .is_none()
778 {
779 return error!(EINVAL);
780 }
781 Ok(())
782 }
783
784 pub fn ring_buffer_status(
785 &self,
786 locked: &mut Locked<Unlocked>,
787 buffer_status: &mut uapi::io_uring_buf_status,
788 ) -> Result<(), Errno> {
789 let state = self.state.lock(locked);
790 let Some(buffer) = state
791 .registered_iobuffers
792 .iter()
793 .find(|buffer| buffer.config.bgid as u32 == buffer_status.buf_group)
794 else {
795 return error!(EINVAL);
796 };
797 buffer_status.head = buffer.head as u32;
798 Ok(())
799 }
800
801 pub fn enter(
802 &self,
803 locked: &mut Locked<Unlocked>,
804 current_task: &CurrentTask,
805 to_submit: u32,
806 _min_complete: u32,
807 _flags: u32,
808 ) -> Result<u32, Errno> {
809 let mut submitted = 0;
810 while let Some(sq_entry) = self.queue.pop_sq_entry()? {
811 submitted += 1;
812 let mut complete_flags: u32 = 0;
814 let result = self.execute(locked, current_task, &sq_entry, &mut complete_flags);
815 let cq_entry = sq_entry.complete(result, complete_flags);
816 self.queue.push_cq_entry(&cq_entry)?;
817 if submitted >= to_submit {
818 break;
819 }
820 }
821 Ok(submitted)
822 }
823
824 fn has_registered_buffers(&self, locked: &mut Locked<Unlocked>) -> bool {
825 !self.state.lock(locked).registered_buffers.is_empty()
826 }
827
828 fn check_buffer(&self, locked: &mut Locked<Unlocked>, entry: &SqEntry) -> Result<(), Errno> {
829 let index = entry.buf_index();
830 let state = self.state.lock(locked);
831 let buffers = &state.registered_buffers;
832 if buffers.is_empty() {
833 return error!(EFAULT);
834 }
835 let buffer = buffers.get(index).ok_or_else(|| errno!(EINVAL))?;
836 if !buffer.contains(entry.address(), entry.length()) { error!(EFAULT) } else { Ok(()) }
837 }
838
839 fn execute(
840 &self,
841 locked: &mut Locked<Unlocked>,
842 current_task: &CurrentTask,
843 entry: &SqEntry,
844 complete_flags: &mut u32,
845 ) -> Result<SyscallResult, Errno> {
846 assert_eq!(*complete_flags, 0);
847
848 let flags = SqEntryFlags::from_bits(entry.flags).ok_or_else(|| errno!(EINVAL))?;
849 match Op::from_code(entry.opcode as io_uring_op)? {
850 Op::NOP => Ok(SUCCESS),
851 Op::ReadV => {
852 if !flags.is_empty() {
853 return error!(EINVAL);
854 }
855 if entry.ioprio != 0 || entry.buf_index() != 0 {
856 return error!(EINVAL);
857 }
858 sys_preadv2(
859 locked,
860 current_task,
861 entry.fd(),
862 entry.iovec_addr(current_task),
863 entry.iovec_count(),
864 entry.offset(),
865 SyscallArg::default(),
866 entry.op_flags,
867 )
868 .map(Into::into)
869 }
870 Op::WriteV => {
871 if !flags.is_empty() {
872 return error!(EINVAL);
873 }
874 if entry.ioprio != 0 || entry.buf_index() != 0 {
875 return error!(EINVAL);
876 }
877 sys_pwritev2(
878 locked,
879 current_task,
880 entry.fd(),
881 entry.iovec_addr(current_task),
882 entry.iovec_count(),
883 entry.offset(),
884 SyscallArg::default(),
885 entry.op_flags,
886 )
887 .map(Into::into)
888 }
889 Op::ReadFixed => {
890 if !flags.is_empty() {
891 return error!(EINVAL);
892 }
893 if entry.ioprio != 0 {
894 return error!(EINVAL);
895 }
896 self.check_buffer(locked, entry)?;
900 do_read(locked, current_task, entry)
901 }
902 Op::WriteFixed => {
903 if !flags.is_empty() {
904 return error!(EINVAL);
905 }
906 if entry.ioprio != 0 {
907 return error!(EINVAL);
908 }
909 self.check_buffer(locked, entry)?;
913 do_write(locked, current_task, entry)
914 }
915 Op::Read => {
916 if !flags.is_empty() {
917 return error!(EINVAL);
918 }
919 if self.has_registered_buffers(locked) {
920 return error!(EINVAL);
921 }
922 do_read(locked, current_task, entry)
923 }
924 Op::Write => {
925 if !flags.is_empty() {
926 return error!(EINVAL);
927 }
928 if self.has_registered_buffers(locked) {
929 return error!(EINVAL);
930 }
931 do_write(locked, current_task, entry)
932 }
933 Op::SendMsg => {
934 if !flags.is_empty() {
935 return error!(EINVAL);
936 }
937 if entry.ioprio != 0 {
938 return error!(EINVAL);
939 }
940 sys_sendmsg(
941 locked,
942 current_task,
943 entry.fd(),
944 MsgHdrPtr::new(current_task, entry.address()),
945 entry.op_flags,
946 )
947 .map(Into::into)
948 }
949 Op::RecvMsg => {
950 struct RecvMsgBufferInfo {
954 buffer: UserBuffer,
955 header: uapi::io_uring_recvmsg_out,
956 buffer_adjustment: usize,
957 }
958 let mut flags = flags;
959 let mut ioprio = entry.ioprio as u32;
960 let msg_hdr_ptr = MsgHdrPtr::new(current_task, entry.address());
961 let (mut msg_hdr_ref, recv_msg_buffer_info): (
962 MsgHdrRef,
963 Option<RecvMsgBufferInfo>,
964 ) = if flags.contains(SqEntryFlags::BUFFER_SELECT) {
965 flags -= SqEntryFlags::BUFFER_SELECT;
966 let buffer = self.claim_next_buffer(
969 locked,
970 current_task,
971 entry.group(),
972 complete_flags,
973 )?;
974 let mut msg_hdr = current_task.read_multi_arch_object(msg_hdr_ptr)?;
975 let headerlen: u32 = std::mem::size_of::<uapi::io_uring_recvmsg_out>() as u32;
981 let namelen: u32 = msg_hdr.name_len.try_into().map_err(|_| errno!(EINVAL))?;
982 let controllen: u32 =
983 msg_hdr.control_len.try_into().map_err(|_| errno!(EINVAL))?;
984 let buffer_adjustment: u32 = headerlen
985 .checked_add(namelen)
986 .and_then(|v| v.checked_add(controllen))
987 .ok_or_else(|| errno!(EINVAL))?;
988 let payloadlen: u32 = (buffer.length as u32)
989 .checked_sub(buffer_adjustment)
990 .ok_or_else(|| errno!(EINVAL))?;
991 let io_uring_hdr = uapi::io_uring_recvmsg_out {
992 namelen,
993 controllen,
994 payloadlen,
995 flags: msg_hdr.flags,
996 };
997
998 let name_addr = (buffer.address + headerlen as usize)?;
999 let control_addr = (name_addr + namelen as usize)?;
1000 let payload_addr = (control_addr + controllen as usize)?;
1001 msg_hdr.name = name_addr;
1002 msg_hdr.control = control_addr;
1003
1004 current_task.zero(buffer.address, buffer_adjustment as usize)?;
1007
1008 let msg_hdr = WithAlternateBuffer::WithAux(
1009 msg_hdr,
1010 UserBuffer { address: payload_addr, length: payloadlen as usize },
1011 );
1012 (
1013 msg_hdr.into(),
1014 Some(RecvMsgBufferInfo {
1015 buffer,
1016 header: io_uring_hdr,
1017 buffer_adjustment: buffer_adjustment as usize,
1018 }),
1019 )
1020 } else {
1021 (msg_hdr_ptr.into(), None)
1022 };
1023 if ioprio & uapi::IORING_RECV_MULTISHOT > 0 {
1024 ioprio &= !uapi::IORING_RECV_MULTISHOT;
1028 }
1029 if !flags.is_empty() {
1030 return error!(EINVAL);
1031 }
1032 if ioprio != 0 {
1033 return error!(EINVAL);
1034 }
1035 let mut count = recvmsg_impl(
1036 locked,
1037 current_task,
1038 entry.fd(),
1039 &mut msg_hdr_ref,
1040 entry.op_flags,
1041 )?;
1042 if let Some(recv_msg_buffer_info) = recv_msg_buffer_info {
1043 current_task.write_object(
1048 recv_msg_buffer_info.buffer.address.into(),
1049 &recv_msg_buffer_info.header,
1050 )?;
1051 count += recv_msg_buffer_info.buffer_adjustment;
1053 }
1054 Ok(count.into())
1055 }
1056 Op::Send => {
1057 if !flags.is_empty() {
1058 return error!(EINVAL);
1059 }
1060 if entry.ioprio != 0 {
1061 return error!(EINVAL);
1062 }
1063 sys_sendto(
1064 locked,
1065 current_task,
1066 entry.fd(),
1067 entry.address(),
1068 entry.length(),
1069 entry.op_flags,
1070 UserAddress::default(),
1071 socklen_t::default(),
1072 )
1073 .map(Into::into)
1074 }
1075 Op::Recv => {
1076 if !flags.is_empty() {
1077 return error!(EINVAL);
1078 }
1079 if entry.ioprio != 0 {
1080 return error!(EINVAL);
1081 }
1082 sys_recvfrom(
1083 locked,
1084 current_task,
1085 entry.fd(),
1086 entry.address(),
1087 entry.length(),
1088 entry.op_flags,
1089 UserAddress::default(),
1090 UserRef::default(),
1091 )
1092 .map(Into::into)
1093 }
1094 Op::FSync
1095 | Op::PollAdd
1096 | Op::PollRemove
1097 | Op::SyncFileRange
1098 | Op::Timeout
1099 | Op::TimeoutRemove
1100 | Op::Accept
1101 | Op::AsyncCancel
1102 | Op::LinkTimeout
1103 | Op::Connect
1104 | Op::FAllocate
1105 | Op::OpenAt
1106 | Op::Close
1107 | Op::FilesUpdate
1108 | Op::StatX
1109 | Op::FAdvise
1110 | Op::MAdvise
1111 | Op::OpenAt2
1112 | Op::EpollCtl => error!(EOPNOTSUPP),
1113 }
1114 }
1115
1116 fn claim_next_buffer(
1117 &self,
1118 locked: &mut Locked<Unlocked>,
1119 current_task: &CurrentTask,
1120 bgid: u16,
1121 complete_flags: &mut u32,
1122 ) -> Result<UserBuffer, Errno> {
1123 let mut state = self.state.lock(locked);
1124 let Some(buffer) =
1125 state.registered_iobuffers.iter_mut().find(|buffer| buffer.config.bgid == bgid)
1126 else {
1127 return error!(EINVAL);
1128 };
1129 buffer.claim_next(current_task, complete_flags)
1130 }
1131}
1132
1133#[derive(Debug)]
1134struct IoUringProviderRingBuffer {
1135 config: uapi::io_uring_buf_reg,
1136 tail_ptr: UserRef<u16>,
1137 entries_ptr: UserRef<UserRingBufferEntry>,
1138 head: u16,
1139}
1140
1141impl IoUringProviderRingBuffer {
1142 fn new(config: uapi::io_uring_buf_reg) -> Result<Self, Errno> {
1143 let ring_addr = UserAddress::from(config.ring_addr);
1144 let tail_ptr =
1145 UserRef::<u16>::from((ring_addr + std::mem::offset_of!(UserRingBufferHeader, tail))?);
1146 let entries_ptr = UserRef::<UserRingBufferEntry>::from(ring_addr);
1147 Ok(Self { config, tail_ptr, entries_ptr, head: 0 })
1148 }
1149
1150 fn claim_next(
1151 &mut self,
1152 current_task: &CurrentTask,
1153 complete_flags: &mut u32,
1154 ) -> Result<UserBuffer, Errno> {
1155 let tail = current_task.read_object(self.tail_ptr)?;
1158 if self.head == tail {
1159 return error!(ENOBUFS);
1160 }
1161 let buffer_info = current_task.read_object(
1162 self.entries_ptr.at((self.head as usize) % (self.config.ring_entries as usize))?,
1163 )?;
1164 self.head += 1;
1165 *complete_flags |=
1166 uapi::IORING_CQE_F_BUFFER | ((buffer_info.bid as u32) << uapi::IORING_CQE_BUFFER_SHIFT);
1167 Ok(UserBuffer { address: buffer_info.addr.into(), length: buffer_info.len as usize })
1168 }
1169}
1170
1171fn do_read(
1172 locked: &mut Locked<Unlocked>,
1173 current_task: &CurrentTask,
1174 entry: &SqEntry,
1175) -> Result<SyscallResult, Errno> {
1176 let offset = entry.offset();
1177 if offset == -1 {
1178 sys_read(locked, current_task, entry.fd(), entry.address(), entry.length()).map(Into::into)
1179 } else {
1180 sys_pread64(locked, current_task, entry.fd(), entry.address(), entry.length(), offset)
1181 .map(Into::into)
1182 }
1183}
1184
1185fn do_write(
1186 locked: &mut Locked<Unlocked>,
1187 current_task: &CurrentTask,
1188 entry: &SqEntry,
1189) -> Result<SyscallResult, Errno> {
1190 let offset = entry.offset();
1191 if offset == -1 {
1192 sys_write(locked, current_task, entry.fd(), entry.address(), entry.length()).map(Into::into)
1193 } else {
1194 sys_pwrite64(
1195 locked,
1196 current_task,
1197 entry.fd(),
1198 entry.address(),
1199 entry.length(),
1200 entry.offset(),
1201 )
1202 .map(Into::into)
1203 }
1204}
1205
1206impl FileOps for IoUringFileObject {
1207 fileops_impl_nonseekable!();
1208 fileops_impl_noop_sync!();
1209 fileops_impl_dataless!();
1210
1211 fn mmap(
1212 &self,
1213 _locked: &mut Locked<FileOpsCore>,
1214 _file: &FileObject,
1215 current_task: &CurrentTask,
1216 addr: DesiredAddress,
1217 memory_offset: u64,
1218 length: usize,
1219 prot_flags: ProtectionFlags,
1220 options: MappingOptions,
1221 filename: NamespaceNode,
1222 ) -> Result<UserAddress, Errno> {
1223 if !options.contains(MappingOptions::SHARED) {
1224 return error!(EINVAL);
1225 }
1226 let magic_offset: u32 = memory_offset.try_into().map_err(|_| errno!(EINVAL))?;
1227 let memory = match magic_offset {
1228 IORING_OFF_SQ_RING | IORING_OFF_CQ_RING => self.queue.ring_buffer.clone(),
1229 IORING_OFF_SQES => self.queue.sq_entries.clone(),
1230 _ => return error!(EINVAL),
1231 };
1232 current_task.mm()?.map_memory(
1233 addr,
1234 memory,
1235 0,
1236 length,
1237 prot_flags,
1238 Access::rwx(),
1239 options,
1240 MappingName::File(filename.into_mapping(None)?),
1241 )
1242 }
1243}
1244
1245#[cfg(test)]
1246mod tests {
1247 use super::*;
1248
1249 #[::fuchsia::test]
1250 fn test_uring_cmd_not_supported() {
1251 assert!(Op::from_code(starnix_uapi::io_uring_op_IORING_OP_URING_CMD).is_err());
1254 }
1255}