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