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