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, 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
57// See https://github.com/google/gvisor/blob/master/pkg/abi/linux/iouring.go#L47
58pub const IORING_MAX_ENTRIES: u32 = 1 << 15; // 32768
59const 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        /// The flags that we support. Specifying a flag outside of this set will generate an
83        /// error.
84        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        /// The flags that we ignore. Specifying a flags in this set will not generate an
91        /// error but will have no effect.
92        // TODO(https://fxbug.dev/297431387): Implement these flags.
93        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        // IORING_SETUP_COOP_TASKRUN requires IORING_SETUP_SINGLE_ISSUER
141        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/// The control header at the start of the shared buffer.
166///
167/// This structure is not declared in the Linux UAPI. Instead, userspace learns about its structure
168/// from the SQ and CQ offsets returned by `io_uring_setup()`.
169///
170/// We determined this structure by running `io_uring_setup()` and observing the placement of each
171/// field. The total size of the structure is 64 bytes, which we determined by looking at the
172/// offset of the cqes offset. It's likely that many of the bytes at the end of this structure are
173/// just padding for alignment.
174#[repr(C)]
175#[derive(Debug, Default, Copy, Clone, IntoBytes, FromBytes, KnownLayout, Immutable)]
176struct ControlHeader {
177    /// The index of the first element in the submission queue.
178    ///
179    /// These values use the full range of u32, wrapping around on overflow. To find the entry in
180    /// the ring buffer, you need to take this index modulo `sq_ring_entries` or, equivalently,
181    /// mask this value with `sq_ring_mask`.
182    sq_head: u32,
183
184    /// The index of the first element beyond the end of the submission queue.
185    ///
186    /// The number of items in the queue is defined to be `sq_tail` - `sq_head`, which means the
187    /// queue is empty if the head and tail are equal.
188    sq_tail: u32,
189
190    /// The index of the first element in the completion queue.
191    ///
192    /// These values use the full range of u32, wrapping around on overflow. To find the entry in
193    /// the ring buffer, you need to take this index modulo `cq_ring_entries` or, equivalently,
194    /// mask this value with `cq_ring_mask`.
195    cq_head: u32,
196
197    /// The index of the first element beyond the end of the completion queue.
198    ///
199    /// The number of items in the queue is defined to be `cq_tail` - `cq_head`, which means the
200    /// queue is empty if the head and tail are equal.
201    cq_tail: u32,
202
203    /// The mask to apply to map `sq_head` and `sq_tail` into the ring buffer.
204    sq_ring_mask: u32,
205
206    /// The mask to apply to map `cq_head` and `cq_tail` into the ring buffer.
207    cq_ring_mask: u32,
208
209    /// The number of entries in the submission queue.
210    sq_ring_entries: u32,
211
212    /// The number of entries in the completion queue.
213    cq_ring_entries: u32,
214
215    /// The number of submission queue entries that were dropped for being malformed.
216    sq_dropped: u32,
217
218    sq_flags: u32,
219    cq_flags: u32,
220
221    /// The number of completion queue entries that were not placed in the completion queue because
222    /// there were no slots available in the ring buffer.
223    cq_overflow: u32,
224
225    _padding: [u8; 16],
226}
227
228const RING_ALIGNMENT: usize = 64;
229
230// From params.cq_off.cqes reported by sys_io_uring_setup.
231static_assertions::const_assert_eq!(std::mem::size_of::<ControlHeader>(), RING_ALIGNMENT);
232
233/// An entry in the submission queue.
234///
235/// We cannot use the bindgen type generated for `io_uring_sqe` directly because that type contains
236/// unions. Instead, we redefine the type here and assert that the layout matches the one that
237/// defined by bindgen.
238#[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/// An entry in the completion queue.
326///
327/// We cannot use the bindgen type generated for `io_uring_cqe` directly because that type contains
328/// a variable length array. Instead, we redefine the type here and assert that the layout matches
329/// the one that defined by bindgen.
330#[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    /// The number of entries in the submission queue.
360    sq_entries: u32,
361
362    /// The number of entries in the completion queue.
363    cq_entries: u32,
364}
365
366impl IoUringMetadata {
367    /// The offset of the compleition queue entry with the given index.
368    ///
369    /// The offset is from the start of the `ring_buffer` VMO.
370    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    /// The offset of first completion queue entry in the `ring_buffer` VMO.
376    fn cqes_offset(&self) -> usize {
377        CQES_OFFSET
378    }
379
380    /// The offset of submission queue indirection array in the `ring_buffer` VMO.
381    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    /// The offset of submission queue indirection array entry with the given index in the
387    /// `ring_buffer` VMO.
388    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    /// The number of bytes in the `ring_buffer` VMO.
394    fn ring_buffer_size(&self) -> usize {
395        self.array_offset() + self.sq_entries as usize * std::mem::size_of::<RingIndex>()
396    }
397
398    /// The offset of the submission queue entry with the given index in the `sq_entries` VMO.
399    ///
400    /// This index is the actual index of the submission queue entry, after indirecting through the
401    /// indirecton array.
402    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    /// The number of bytes in the `sq_entries` VMO.
408    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
485// Currently, we read and write the memory shared with userspace via the VMOs. In the future, we
486// will likely want to map the memory for these VMOs into the kernel address space so that we can
487// access their contents more efficiently and so that we can perform the appropriate atomic
488// operations.
489
490// TODO(https://fxbug.dev/297431387): Map `ring_buffer` and `sq_entries` into kernel memory so that
491// this operation becomes memcpy.
492fn read_object<T: FromBytes>(memory_object: &MemoryObject, offset: u64) -> Result<T, Errno> {
493    // SAFETY: read_uninit returns an error if not all the bytes were read.
494    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
502// TODO(https://fxbug.dev/297431387): Map `ring_buffer` and `sq_entries` into kernel memory so that
503// this operation becomes memcpy.
504fn 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
512/// The memory the IoUring shares with userspace.
513struct IoUringQueue {
514    /// Metadata about the layout of this memory.
515    metadata: IoUringMetadata,
516
517    /// The primary ring buffer.
518    ///
519    /// The ring buffer's memory layout is as follows:
520    ///
521    ///   ControlHeader
522    ///   N completion queue entries
523    ///   An array of u32 values used to indirect indices to the submission queue entries
524    ///
525    /// The ControlHeader is a fixed size, which means the completion queue entries always start
526    /// at the same offset in this VMO.
527    ring_buffer: Arc<MemoryObject>,
528
529    /// A separate VMO for the submission queue entries.
530    ///
531    /// This entries are not necessarily populated in order. Instead, userspace uses the array of
532    /// submission queue indices in the `ring_buffer` in order. That array gives the indices of
533    /// the actual submission queue entries.
534    ///
535    /// IoUring uses this index indirection scheme because submission queue entries do not always
536    /// complete in the same order they were submitted.
537    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        // TODO(https://fxbug.dev/297431387): Reading the tail field should be atomic with ordering
570        // acquire once we map these buffers into kernel memory.
571        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        // TODO(https://fxbug.dev/297431387): Reading the head field should be atomic with ordering
576        // acquire once we map these buffers into kernel memory.
577        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        // TODO(https://fxbug.dev/297431387): Writing the tail field should be atomic with ordering
586        // release once we map these buffers into kernel memory.
587        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        // TODO(https://fxbug.dev/297431387): Incrementing the overflow count should be an atomic
605        // operation.
606        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    /// Pop an entry off the submission queue and update the head to let userspace queue more
613    /// entries.
614    ///
615    /// Returns `None` if the submission queue is empty.
616    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    /// Push an entry onto the completion queue and update the tail to let userspace know a new
629    /// entry is available.
630    ///
631    /// If there is no room in the completion queue, this function will increment the overflow
632    /// counter.
633    fn push_cq_entry(&self, entry: &CqEntry) -> Result<(), Errno> {
634        let head = self.read_cq_head()?;
635        let tail = self.read_cq_tail()?;
636        // Check that the offset for the tail location doesn't collide with the head of the queue.
637        // This can happen because the entries are stored in a ring buffer.
638        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            // This operation cannot overflow because sq_entries is capped at IORING_MAX_ENTRIES,
678            // which is only 15 bits.
679            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        // The docs for io_uring_register imply that the kernel should actually map this memory
724        // into its own address space when these buffers are registered. That's probably observable
725        // if the client changes the mappings for these addresses between the time they are
726        // registered and they are used. For now, we just store the addresses.
727        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            // We currently act as if every SqEntry has IOSQE_IO_DRAIN.
802            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                // TODO(https://fxbug.dev/297431387): We're supposed to make a kernel mapping
883                // when the buffers are registered and we should be performing this operation using
884                // those kernel mappings rather than using the userspace mappings.
885                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                // TODO(https://fxbug.dev/297431387): We're supposed to make a kernel mapping
896                // when the buffers are registered and we should be performing this operation using
897                // those kernel mappings rather than using the userspace mappings.
898                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                // A struct to hold the information about the provided buffer.
936                // This is needed because the buffer is claimed before the call to `recvmsg_impl`
937                // but the result is adjusted after.
938                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                    // If BUFFER_SELECT is set, the application is providing a buffer for the
952                    // recvmsg operation.
953                    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                    // The buffer is laid out as follows:
957                    // - io_uring_recvmsg_out
958                    // - sockaddr (name)
959                    // - msghdr.msg_control
960                    // - payload
961                    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                    // Zero out the prefix of the buffer that will contain the header, name and
986                    // control bytes.
987                    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                    // Ignoring IORING_RECV_MULTISHOT
1006                    // Because the IORING_CQE_F_BUFFER flags will never be set, the client will
1007                    // always have to call the syscall again.
1008                    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                    // The result from `recvmsg_impl` is the number of bytes written to the
1020                    // payload. The result of the io_uring operation is the number of bytes
1021                    // written to the provided buffer.
1022                    // 1. Write the io_uring buffer header.
1023                    current_task.write_object(
1024                        recv_msg_buffer_info.buffer.address.into(),
1025                        &recv_msg_buffer_info.header,
1026                    )?;
1027                    // 2. Adjust the written count.
1028                    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        // TODO(https://fxbug.dev/297431387): Reading the tail field should be atomic with ordering
1129        // acquire.
1130        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        // TODO(https://fxbug.dev/505326826): If the uring_cmd operation is supported,
1207        // add the necessary security checks.
1208        assert!(Op::from_code(starnix_uapi::io_uring_op_IORING_OP_URING_CMD).is_err());
1209    }
1210}