Skip to main content

starnix_modules_iouring/
io_uring.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#![allow(non_upper_case_globals)]
6// Expects are used for programming errors.
7#![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
60// See https://github.com/google/gvisor/blob/master/pkg/abi/linux/iouring.go#L47
61pub const IORING_MAX_ENTRIES: u32 = 1 << 15; // 32768
62const 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        /// The flags that we support. Specifying a flag outside of this set will generate an
86        /// error.
87        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        /// The flags that we ignore. Specifying a flags in this set will not generate an
94        /// error but will have no effect.
95        // TODO(https://fxbug.dev/297431387): Implement these flags.
96        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        // IORING_SETUP_COOP_TASKRUN requires IORING_SETUP_SINGLE_ISSUER
144        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/// The control header at the start of the shared buffer.
169///
170/// This structure is not declared in the Linux UAPI. Instead, userspace learns about its structure
171/// from the SQ and CQ offsets returned by `io_uring_setup()`.
172///
173/// We determined this structure by running `io_uring_setup()` and observing the placement of each
174/// field. The total size of the structure is 64 bytes, which we determined by looking at the
175/// offset of the cqes offset. It's likely that many of the bytes at the end of this structure are
176/// just padding for alignment.
177#[repr(C)]
178#[derive(Debug, Default, Copy, Clone, IntoBytes, FromBytes, KnownLayout, Immutable)]
179struct ControlHeader {
180    /// The index of the first element in the submission queue.
181    ///
182    /// These values use the full range of u32, wrapping around on overflow. To find the entry in
183    /// the ring buffer, you need to take this index modulo `sq_ring_entries` or, equivalently,
184    /// mask this value with `sq_ring_mask`.
185    sq_head: u32,
186
187    /// The index of the first element beyond the end of the submission queue.
188    ///
189    /// The number of items in the queue is defined to be `sq_tail` - `sq_head`, which means the
190    /// queue is empty if the head and tail are equal.
191    sq_tail: u32,
192
193    /// The index of the first element in the completion queue.
194    ///
195    /// These values use the full range of u32, wrapping around on overflow. To find the entry in
196    /// the ring buffer, you need to take this index modulo `cq_ring_entries` or, equivalently,
197    /// mask this value with `cq_ring_mask`.
198    cq_head: u32,
199
200    /// The index of the first element beyond the end of the completion queue.
201    ///
202    /// The number of items in the queue is defined to be `cq_tail` - `cq_head`, which means the
203    /// queue is empty if the head and tail are equal.
204    cq_tail: u32,
205
206    /// The mask to apply to map `sq_head` and `sq_tail` into the ring buffer.
207    sq_ring_mask: u32,
208
209    /// The mask to apply to map `cq_head` and `cq_tail` into the ring buffer.
210    cq_ring_mask: u32,
211
212    /// The number of entries in the submission queue.
213    sq_ring_entries: u32,
214
215    /// The number of entries in the completion queue.
216    cq_ring_entries: u32,
217
218    /// The number of submission queue entries that were dropped for being malformed.
219    sq_dropped: u32,
220
221    sq_flags: u32,
222    cq_flags: u32,
223
224    /// The number of completion queue entries that were not placed in the completion queue because
225    /// there were no slots available in the ring buffer.
226    cq_overflow: u32,
227
228    _padding: [u8; 16],
229}
230
231const RING_ALIGNMENT: usize = 64;
232
233// From params.cq_off.cqes reported by sys_io_uring_setup.
234static_assertions::const_assert_eq!(std::mem::size_of::<ControlHeader>(), RING_ALIGNMENT);
235
236/// An entry in the submission queue.
237///
238/// We cannot use the bindgen type generated for `io_uring_sqe` directly because that type contains
239/// unions. Instead, we redefine the type here and assert that the layout matches the one that
240/// defined by bindgen.
241#[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/// An entry in the completion queue.
329///
330/// We cannot use the bindgen type generated for `io_uring_cqe` directly because that type contains
331/// a variable length array. Instead, we redefine the type here and assert that the layout matches
332/// the one that defined by bindgen.
333#[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    /// The number of entries in the submission queue.
363    sq_entries: u32,
364
365    /// The number of entries in the completion queue.
366    cq_entries: u32,
367}
368
369impl IoUringMetadata {
370    /// The offset of the compleition queue entry with the given index.
371    ///
372    /// The offset is from the start of the `ring_buffer` VMO.
373    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    /// The offset of first completion queue entry in the `ring_buffer` VMO.
379    fn cqes_offset(&self) -> usize {
380        CQES_OFFSET
381    }
382
383    /// The offset of submission queue indirection array in the `ring_buffer` VMO.
384    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    /// The offset of submission queue indirection array entry with the given index in the
390    /// `ring_buffer` VMO.
391    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    /// The number of bytes in the `ring_buffer` VMO.
397    fn ring_buffer_size(&self) -> usize {
398        self.array_offset() + self.sq_entries as usize * std::mem::size_of::<RingIndex>()
399    }
400
401    /// The offset of the submission queue entry with the given index in the `sq_entries` VMO.
402    ///
403    /// This index is the actual index of the submission queue entry, after indirecting through the
404    /// indirecton array.
405    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    /// The number of bytes in the `sq_entries` VMO.
411    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
488// Currently, we read and write the memory shared with userspace via the VMOs. In the future, we
489// will likely want to map the memory for these VMOs into the kernel address space so that we can
490// access their contents more efficiently and so that we can perform the appropriate atomic
491// operations.
492
493// TODO(https://fxbug.dev/297431387): Map `ring_buffer` and `sq_entries` into kernel memory so that
494// this operation becomes memcpy.
495fn read_object<T: FromBytes>(memory_object: &MemoryObject, offset: u64) -> Result<T, Errno> {
496    // SAFETY: read_uninit returns an error if not all the bytes were read.
497    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
505// TODO(https://fxbug.dev/297431387): Map `ring_buffer` and `sq_entries` into kernel memory so that
506// this operation becomes memcpy.
507fn 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
515/// The memory the IoUring shares with userspace.
516struct IoUringQueue {
517    /// Metadata about the layout of this memory.
518    metadata: IoUringMetadata,
519
520    /// The primary ring buffer.
521    ///
522    /// The ring buffer's memory layout is as follows:
523    ///
524    ///   ControlHeader
525    ///   N completion queue entries
526    ///   An array of u32 values used to indirect indices to the submission queue entries
527    ///
528    /// The ControlHeader is a fixed size, which means the completion queue entries always start
529    /// at the same offset in this VMO.
530    ring_buffer: Arc<MemoryObject>,
531
532    /// A separate VMO for the submission queue entries.
533    ///
534    /// This entries are not necessarily populated in order. Instead, userspace uses the array of
535    /// submission queue indices in the `ring_buffer` in order. That array gives the indices of
536    /// the actual submission queue entries.
537    ///
538    /// IoUring uses this index indirection scheme because submission queue entries do not always
539    /// complete in the same order they were submitted.
540    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        // TODO(https://fxbug.dev/297431387): Reading the tail field should be atomic with ordering
573        // acquire once we map these buffers into kernel memory.
574        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        // TODO(https://fxbug.dev/297431387): Reading the head field should be atomic with ordering
579        // acquire once we map these buffers into kernel memory.
580        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        // TODO(https://fxbug.dev/297431387): Writing the tail field should be atomic with ordering
589        // release once we map these buffers into kernel memory.
590        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        // TODO(https://fxbug.dev/297431387): Incrementing the overflow count should be an atomic
608        // operation.
609        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    /// Pop an entry off the submission queue and update the head to let userspace queue more
616    /// entries.
617    ///
618    /// Returns `None` if the submission queue is empty.
619    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    /// Push an entry onto the completion queue and update the tail to let userspace know a new
632    /// entry is available.
633    ///
634    /// If there is no room in the completion queue, this function will increment the overflow
635    /// counter.
636    fn push_cq_entry(&self, entry: &CqEntry) -> Result<(), Errno> {
637        let head = self.read_cq_head()?;
638        let tail = self.read_cq_tail()?;
639        // Check that the offset for the tail location doesn't collide with the head of the queue.
640        // This can happen because the entries are stored in a ring buffer.
641        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            // This operation cannot overflow because sq_entries is capped at IORING_MAX_ENTRIES,
685            // which is only 15 bits.
686            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        // The docs for io_uring_register imply that the kernel should actually map this memory
731        // into its own address space when these buffers are registered. That's probably observable
732        // if the client changes the mappings for these addresses between the time they are
733        // registered and they are used. For now, we just store the addresses.
734        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            // We currently act as if every SqEntry has IOSQE_IO_DRAIN.
813            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                // TODO(https://fxbug.dev/297431387): We're supposed to make a kernel mapping
897                // when the buffers are registered and we should be performing this operation using
898                // those kernel mappings rather than using the userspace mappings.
899                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                // TODO(https://fxbug.dev/297431387): We're supposed to make a kernel mapping
910                // when the buffers are registered and we should be performing this operation using
911                // those kernel mappings rather than using the userspace mappings.
912                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                // A struct to hold the information about the provided buffer.
951                // This is needed because the buffer is claimed before the call to `recvmsg_impl`
952                // but the result is adjusted after.
953                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                    // If BUFFER_SELECT is set, the application is providing a buffer for the
967                    // recvmsg operation.
968                    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                    // The buffer is laid out as follows:
976                    // - io_uring_recvmsg_out
977                    // - sockaddr (name)
978                    // - msghdr.msg_control
979                    // - payload
980                    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                    // Zero out the prefix of the buffer that will contain the header, name and
1005                    // control bytes.
1006                    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                    // Ignoring IORING_RECV_MULTISHOT
1025                    // Because the IORING_CQE_F_BUFFER flags will never be set, the client will
1026                    // always have to call the syscall again.
1027                    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                    // The result from `recvmsg_impl` is the number of bytes written to the
1044                    // payload. The result of the io_uring operation is the number of bytes
1045                    // written to the provided buffer.
1046                    // 1. Write the io_uring buffer header.
1047                    current_task.write_object(
1048                        recv_msg_buffer_info.buffer.address.into(),
1049                        &recv_msg_buffer_info.header,
1050                    )?;
1051                    // 2. Adjust the written count.
1052                    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        // TODO(https://fxbug.dev/297431387): Reading the tail field should be atomic with ordering
1156        // acquire.
1157        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        // TODO(https://fxbug.dev/505326826): If the uring_cmd operation is supported,
1252        // add the necessary security checks.
1253        assert!(Op::from_code(starnix_uapi::io_uring_op_IORING_OP_URING_CMD).is_err());
1254    }
1255}