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_LAST,
37    io_uring_op_IORING_OP_NOP, io_uring_op_IORING_OP_READ, io_uring_op_IORING_OP_READ_FIXED,
38    io_uring_op_IORING_OP_READV, io_uring_op_IORING_OP_RECV, io_uring_op_IORING_OP_RECVMSG,
39    io_uring_op_IORING_OP_SEND, io_uring_op_IORING_OP_SENDMSG, io_uring_op_IORING_OP_WRITE,
40    io_uring_op_IORING_OP_WRITE_FIXED, io_uring_op_IORING_OP_WRITEV, io_uring_params, io_uring_sqe,
41    io_uring_sqe_flags_bit_IOSQE_ASYNC_BIT, io_uring_sqe_flags_bit_IOSQE_BUFFER_SELECT_BIT,
42    io_uring_sqe_flags_bit_IOSQE_CQE_SKIP_SUCCESS_BIT, io_uring_sqe_flags_bit_IOSQE_FIXED_FILE_BIT,
43    io_uring_sqe_flags_bit_IOSQE_IO_DRAIN_BIT, io_uring_sqe_flags_bit_IOSQE_IO_HARDLINK_BIT,
44    io_uring_sqe_flags_bit_IOSQE_IO_LINK_BIT, off_t, socklen_t, uapi,
45};
46use std::sync::Arc;
47use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
48
49// See https://github.com/google/gvisor/blob/master/pkg/abi/linux/iouring.go#L47
50pub const IORING_MAX_ENTRIES: u32 = 1 << 15; // 32768
51const IORING_MAX_CQ_ENTRIES: u32 = 2 * IORING_MAX_ENTRIES;
52
53bitflags! {
54    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
55    pub struct IoRingSetupFlags: u32 {
56        const IoPoll = starnix_uapi::IORING_SETUP_IOPOLL;
57        const SqPoll = starnix_uapi::IORING_SETUP_SQPOLL;
58        const SqAff = starnix_uapi::IORING_SETUP_SQ_AFF;
59        const CqSize = starnix_uapi::IORING_SETUP_CQSIZE;
60        const Clamp = starnix_uapi::IORING_SETUP_CLAMP;
61        const AttachWq = starnix_uapi::IORING_SETUP_ATTACH_WQ;
62        const RDisabled = starnix_uapi::IORING_SETUP_R_DISABLED;
63        const SubmitAll = starnix_uapi::IORING_SETUP_SUBMIT_ALL;
64        const CoopTaskRun = starnix_uapi::IORING_SETUP_COOP_TASKRUN;
65        const TaskRunFlag = starnix_uapi::IORING_SETUP_TASKRUN_FLAG;
66        const SqE128 = starnix_uapi::IORING_SETUP_SQE128;
67        const CqE32 = starnix_uapi::IORING_SETUP_CQE32;
68        const SingleIssuer = starnix_uapi::IORING_SETUP_SINGLE_ISSUER;
69        const DeferTaskRun = starnix_uapi::IORING_SETUP_DEFER_TASKRUN;
70        const NoMmap = starnix_uapi::IORING_SETUP_NO_MMAP;
71        const RegisteredFdOnly = starnix_uapi::IORING_SETUP_REGISTERED_FD_ONLY;
72        const NoSqArray = starnix_uapi::IORING_SETUP_NO_SQARRAY;
73
74        /// The flags that we support. Specifying a flag outside of this set will generate an
75        /// error.
76        const SupportedFlags = starnix_uapi::IORING_SETUP_CQSIZE |
77                               starnix_uapi::IORING_SETUP_COOP_TASKRUN |
78                               starnix_uapi::IORING_SETUP_TASKRUN_FLAG |
79                               starnix_uapi::IORING_SETUP_SINGLE_ISSUER |
80                               starnix_uapi::IORING_SETUP_DEFER_TASKRUN;
81
82        /// The flags that we ignore. Specifying a flags in this set will not generate an
83        /// error but will have no effect.
84        // TODO(https://fxbug.dev/297431387): Implement these flags.
85        const IgnoredFlags = 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
91    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
92    struct SqEntryFlags: u8 {
93        const FIXED_FILE = 1 << io_uring_sqe_flags_bit_IOSQE_FIXED_FILE_BIT;
94        const IO_DRAIN = 1 << io_uring_sqe_flags_bit_IOSQE_IO_DRAIN_BIT;
95        const IO_LINK = 1 << io_uring_sqe_flags_bit_IOSQE_IO_LINK_BIT;
96        const IO_HARDLINK = 1 << io_uring_sqe_flags_bit_IOSQE_IO_HARDLINK_BIT;
97        const ASYNC = 1 << io_uring_sqe_flags_bit_IOSQE_ASYNC_BIT;
98        const BUFFER_SELECT = 1 << io_uring_sqe_flags_bit_IOSQE_BUFFER_SELECT_BIT;
99        const CQE_SKIP_SUCCESS = 1 << io_uring_sqe_flags_bit_IOSQE_CQE_SKIP_SUCCESS_BIT;
100    }
101}
102
103impl IoRingSetupFlags {
104    fn build_and_validate_from(value: u32) -> Result<Self, Errno> {
105        let Some(flags) = IoRingSetupFlags::from_bits(value) else {
106            track_stub!(
107                TODO("https://fxbug.dev/297431387"),
108                "io_uring_setup undefined flag(s)",
109                value
110            );
111            return error!(EINVAL);
112        };
113
114        let unsupported_flags = flags.difference(IoRingSetupFlags::SupportedFlags);
115        if !unsupported_flags.is_empty() {
116            track_stub!(
117                TODO("https://fxbug.dev/297431387"),
118                "io_uring_setup unsupported flags",
119                unsupported_flags.bits()
120            );
121            return error!(EINVAL);
122        }
123        let ignored_flags = flags.intersection(IoRingSetupFlags::IgnoredFlags);
124        if !ignored_flags.is_empty() {
125            track_stub!(
126                TODO("https://fxbug.dev/297431387"),
127                "io_uring_setup ignored flags",
128                ignored_flags.bits()
129            );
130        }
131
132        // IORING_SETUP_COOP_TASKRUN requires IORING_SETUP_SINGLE_ISSUER
133        if flags.contains(IoRingSetupFlags::DeferTaskRun)
134            && !flags.contains(IoRingSetupFlags::SingleIssuer)
135        {
136            return error!(EINVAL);
137        }
138
139        return Ok(flags);
140    }
141}
142
143type RingIndex = u32;
144
145type UserRingBufferHeader = uapi::io_uring_buf_ring__bindgen_ty_1__bindgen_ty_1;
146type UserRingBufferEntry = uapi::io_uring_buf;
147
148static_assertions::const_assert_eq!(
149    std::mem::size_of::<u16>(),
150    uapi::size_of_field!(UserRingBufferHeader, tail)
151);
152static_assertions::const_assert_eq!(
153    std::mem::size_of::<UserRingBufferHeader>(),
154    std::mem::size_of::<UserRingBufferEntry>()
155);
156
157/// The control header at the start of the shared buffer.
158///
159/// This structure is not declared in the Linux UAPI. Instead, userspace learns about its structure
160/// from the SQ and CQ offsets returned by `io_uring_setup()`.
161///
162/// We determined this structure by running `io_uring_setup()` and observing the placement of each
163/// field. The total size of the structure is 64 bytes, which we determined by looking at the
164/// offset of the cqes offset. It's likely that many of the bytes at the end of this structure are
165/// just padding for alignment.
166#[repr(C)]
167#[derive(Debug, Default, Copy, Clone, IntoBytes, FromBytes, KnownLayout, Immutable)]
168struct ControlHeader {
169    /// The index of the first element in the submission queue.
170    ///
171    /// These values use the full range of u32, wrapping around on overflow. To find the entry in
172    /// the ring buffer, you need to take this index modulo `sq_ring_entries` or, equivalently,
173    /// mask this value with `sq_ring_mask`.
174    sq_head: u32,
175
176    /// The index of the first element beyond the end of the submission queue.
177    ///
178    /// The number of items in the queue is defined to be `sq_tail` - `sq_head`, which means the
179    /// queue is empty if the head and tail are equal.
180    sq_tail: u32,
181
182    /// The index of the first element in the completion queue.
183    ///
184    /// These values use the full range of u32, wrapping around on overflow. To find the entry in
185    /// the ring buffer, you need to take this index modulo `cq_ring_entries` or, equivalently,
186    /// mask this value with `cq_ring_mask`.
187    cq_head: u32,
188
189    /// The index of the first element beyond the end of the completion queue.
190    ///
191    /// The number of items in the queue is defined to be `cq_tail` - `cq_head`, which means the
192    /// queue is empty if the head and tail are equal.
193    cq_tail: u32,
194
195    /// The mask to apply to map `sq_head` and `sq_tail` into the ring buffer.
196    sq_ring_mask: u32,
197
198    /// The mask to apply to map `cq_head` and `cq_tail` into the ring buffer.
199    cq_ring_mask: u32,
200
201    /// The number of entries in the submission queue.
202    sq_ring_entries: u32,
203
204    /// The number of entries in the completion queue.
205    cq_ring_entries: u32,
206
207    /// The number of submission queue entries that were dropped for being malformed.
208    sq_dropped: u32,
209
210    sq_flags: u32,
211    cq_flags: u32,
212
213    /// The number of completion queue entries that were not placed in the completion queue because
214    /// there were no slots available in the ring buffer.
215    cq_overflow: u32,
216
217    _padding: [u8; 16],
218}
219
220const RING_ALIGNMENT: usize = 64;
221
222// From params.cq_off.cqes reported by sys_io_uring_setup.
223static_assertions::const_assert_eq!(std::mem::size_of::<ControlHeader>(), RING_ALIGNMENT);
224
225/// An entry in the submission queue.
226///
227/// We cannot use the bindgen type generated for `io_uring_sqe` directly because that type contains
228/// unions. Instead, we redefine the type here and assert that the layout matches the one that
229/// defined by bindgen.
230#[repr(C)]
231#[derive(Debug, Default, Copy, Clone, IntoBytes, FromBytes, KnownLayout, Immutable)]
232struct SqEntry {
233    opcode: u8,
234    flags: u8,
235    ioprio: u16,
236    raw_fd: i32,
237    field0: u64,
238    field1: u64,
239    len: u32,
240    op_flags: u32,
241    user_data: u64,
242    buf_index_or_group: u16,
243    personality: u16,
244    field2: u32,
245    field3: [u64; 2usize],
246}
247
248uapi::check_arch_independent_same_layout! {
249    SqEntry = io_uring_sqe {
250        opcode => opcode,
251        flags => flags,
252        ioprio => ioprio,
253        raw_fd => fd,
254        field0 => __bindgen_anon_1,
255        field1 => __bindgen_anon_2,
256        len => len,
257        op_flags => __bindgen_anon_3,
258        user_data => user_data,
259        buf_index_or_group => __bindgen_anon_4,
260        personality => personality,
261        field2 => __bindgen_anon_5,
262        field3 => __bindgen_anon_6,
263    }
264}
265
266uapi::check_arch_independent_layout! {
267    io_uring_recvmsg_out{
268        namelen,
269        controllen,
270        payloadlen,
271        flags,
272    }
273}
274
275impl SqEntry {
276    fn complete(&self, result: Result<SyscallResult, Errno>, flags: u32) -> CqEntry {
277        let res = match result {
278            Ok(return_value) => return_value.value() as i32,
279            Err(errno) => errno.return_value() as i32,
280        };
281        CqEntry { user_data: self.user_data, res, flags }
282    }
283
284    fn fd(&self) -> FdNumber {
285        FdNumber::from_raw(self.raw_fd)
286    }
287
288    fn iovec_addr<Arch: ArchSpecific>(&self, arch: &Arch) -> IOVecPtr {
289        IOVecPtr::new(arch, self.field1)
290    }
291
292    fn iovec_count(&self) -> UserValue<i32> {
293        (self.len as i32).into()
294    }
295
296    fn address(&self) -> UserAddress {
297        self.field1.into()
298    }
299
300    fn length(&self) -> usize {
301        self.len as usize
302    }
303
304    fn offset(&self) -> off_t {
305        self.field0 as off_t
306    }
307
308    fn buf_index(&self) -> usize {
309        self.buf_index_or_group as usize
310    }
311
312    fn group(&self) -> u16 {
313        self.buf_index_or_group
314    }
315}
316
317/// An entry in the completion queue.
318///
319/// We cannot use the bindgen type generated for `io_uring_cqe` directly because that type contains
320/// a variable length array. Instead, we redefine the type here and assert that the layout matches
321/// the one that defined by bindgen.
322#[repr(C)]
323#[derive(Debug, Default, Copy, Clone, IntoBytes, FromBytes, KnownLayout, Immutable)]
324struct CqEntry {
325    pub user_data: u64,
326    pub res: i32,
327    pub flags: u32,
328}
329
330static_assertions::assert_eq_size!(CqEntry, io_uring_cqe);
331static_assertions::const_assert_eq!(
332    std::mem::offset_of!(CqEntry, user_data),
333    std::mem::offset_of!(io_uring_cqe, user_data)
334);
335static_assertions::const_assert_eq!(
336    std::mem::offset_of!(CqEntry, res),
337    std::mem::offset_of!(io_uring_cqe, res)
338);
339static_assertions::const_assert_eq!(
340    std::mem::offset_of!(CqEntry, flags),
341    std::mem::offset_of!(io_uring_cqe, flags)
342);
343
344const CQES_OFFSET: usize = std::mem::size_of::<ControlHeader>();
345
346#[inline]
347fn align_ring_field(offset: usize) -> usize {
348    offset.next_multiple_of(RING_ALIGNMENT)
349}
350struct IoUringMetadata {
351    /// The number of entries in the submission queue.
352    sq_entries: u32,
353
354    /// The number of entries in the completion queue.
355    cq_entries: u32,
356}
357
358impl IoUringMetadata {
359    /// The offset of the compleition queue entry with the given index.
360    ///
361    /// The offset is from the start of the `ring_buffer` VMO.
362    fn cq_entry_offset(&self, index: u32) -> u64 {
363        let index = index % self.cq_entries;
364        (CQES_OFFSET + index as usize * std::mem::size_of::<io_uring_cqe>()) as u64
365    }
366
367    /// The offset of first completion queue entry in the `ring_buffer` VMO.
368    fn cqes_offset(&self) -> usize {
369        CQES_OFFSET
370    }
371
372    /// The offset of submission queue indirection array in the `ring_buffer` VMO.
373    fn array_offset(&self) -> usize {
374        CQES_OFFSET
375            + align_ring_field(self.cq_entries as usize * std::mem::size_of::<io_uring_cqe>())
376    }
377
378    /// The offset of submission queue indirection array entry with the given index in the
379    /// `ring_buffer` VMO.
380    fn array_entry_offset(&self, index: u32) -> u64 {
381        let index = index % self.sq_entries;
382        (self.array_offset() + index as usize * std::mem::size_of::<RingIndex>()) as u64
383    }
384
385    /// The number of bytes in the `ring_buffer` VMO.
386    fn ring_buffer_size(&self) -> usize {
387        self.array_offset() + self.sq_entries as usize * std::mem::size_of::<RingIndex>()
388    }
389
390    /// The offset of the submission queue entry with the given index in the `sq_entries` VMO.
391    ///
392    /// This index is the actual index of the submission queue entry, after indirecting through the
393    /// indirecton array.
394    fn sq_entry_offset(&self, index: u32) -> u64 {
395        let index = index % self.sq_entries;
396        (index as usize * std::mem::size_of::<io_uring_sqe>()) as u64
397    }
398
399    /// The number of bytes in the `sq_entries` VMO.
400    fn sq_entries_size(&self) -> usize {
401        self.sq_entries as usize * std::mem::size_of::<io_uring_sqe>()
402    }
403}
404
405#[repr(u32)]
406enum Op {
407    NOP = io_uring_op_IORING_OP_NOP,
408    Read = io_uring_op_IORING_OP_READ,
409    ReadV = io_uring_op_IORING_OP_READV,
410    ReadFixed = io_uring_op_IORING_OP_READ_FIXED,
411    Recv = io_uring_op_IORING_OP_RECV,
412    RecvMsg = io_uring_op_IORING_OP_RECVMSG,
413    Send = io_uring_op_IORING_OP_SEND,
414    SendMsg = io_uring_op_IORING_OP_SENDMSG,
415    Write = io_uring_op_IORING_OP_WRITE,
416    WriteV = io_uring_op_IORING_OP_WRITEV,
417    WriteFixed = io_uring_op_IORING_OP_WRITE_FIXED,
418}
419
420impl Op {
421    fn from_code(opcode: io_uring_op) -> Result<Op, Errno> {
422        match opcode {
423            io_uring_op_IORING_OP_NOP => Ok(Self::NOP),
424            io_uring_op_IORING_OP_READ => Ok(Self::Read),
425            io_uring_op_IORING_OP_READV => Ok(Self::ReadV),
426            io_uring_op_IORING_OP_READ_FIXED => Ok(Self::ReadFixed),
427            io_uring_op_IORING_OP_RECV => Ok(Self::Recv),
428            io_uring_op_IORING_OP_RECVMSG => Ok(Self::RecvMsg),
429            io_uring_op_IORING_OP_SEND => Ok(Self::Send),
430            io_uring_op_IORING_OP_SENDMSG => Ok(Self::SendMsg),
431            io_uring_op_IORING_OP_WRITE => Ok(Self::Write),
432            io_uring_op_IORING_OP_WRITEV => Ok(Self::WriteV),
433            io_uring_op_IORING_OP_WRITE_FIXED => Ok(Self::WriteFixed),
434            0..io_uring_op_IORING_OP_LAST => {
435                track_stub!(
436                    TODO("https://fxbug.dev/297431387"),
437                    "io_uring_enter unimplemented op",
438                    opcode
439                );
440                error!(EOPNOTSUPP)
441            }
442            _ => error!(EINVAL),
443        }
444    }
445}
446
447// Currently, we read and write the memory shared with userspace via the VMOs. In the future, we
448// will likely want to map the memory for these VMOs into the kernel address space so that we can
449// access their contents more efficiently and so that we can perform the appropriate atomic
450// operations.
451
452// TODO(https://fxbug.dev/297431387): Map `ring_buffer` and `sq_entries` into kernel memory so that
453// this operation becomes memcpy.
454fn read_object<T: FromBytes>(memory_object: &MemoryObject, offset: u64) -> Result<T, Errno> {
455    // SAFETY: read_uninit returns an error if not all the bytes were read.
456    unsafe {
457        read_to_object_as_bytes(|buf| {
458            memory_object.read_uninit(buf, offset).map_err(|_| errno!(EFAULT))?;
459            Ok(())
460        })
461    }
462}
463
464// TODO(https://fxbug.dev/297431387): Map `ring_buffer` and `sq_entries` into kernel memory so that
465// this operation becomes memcpy.
466fn write_object<T: IntoBytes + Immutable>(
467    memory_object: &MemoryObject,
468    offset: u64,
469    value: &T,
470) -> Result<(), Errno> {
471    memory_object.write(value.as_bytes(), offset).map_err(|_| errno!(EFAULT))
472}
473
474/// The memory the IoUring shares with userspace.
475struct IoUringQueue {
476    /// Metadata about the layout of this memory.
477    metadata: IoUringMetadata,
478
479    /// The primary ring buffer.
480    ///
481    /// The ring buffer's memory layout is as follows:
482    ///
483    ///   ControlHeader
484    ///   N completion queue entries
485    ///   An array of u32 values used to indirect indices to the submission queue entries
486    ///
487    /// The ControlHeader is a fixed size, which means the completion queue entries always start
488    /// at the same offset in this VMO.
489    ring_buffer: Arc<MemoryObject>,
490
491    /// A separate VMO for the submission queue entries.
492    ///
493    /// This entries are not necessarily populated in order. Instead, userspace uses the array of
494    /// submission queue indices in the `ring_buffer` in order. That array gives the indices of
495    /// the actual submission queue entries.
496    ///
497    /// IoUring uses this index indirection scheme because submission queue entries do not always
498    /// complete in the same order they were submitted.
499    sq_entries: Arc<MemoryObject>,
500}
501
502impl IoUringQueue {
503    fn new(metadata: IoUringMetadata) -> Result<Self, Errno> {
504        let ring_buffer =
505            zx::Vmo::create(metadata.ring_buffer_size() as u64).map_err(|_| errno!(ENOMEM))?;
506        set_zx_name(&ring_buffer, b"io_uring:ring");
507        let sq_entries =
508            zx::Vmo::create(metadata.sq_entries_size() as u64).map_err(|_| errno!(ENOMEM))?;
509        set_zx_name(&sq_entries, b"io_uring:sqes");
510
511        Ok(Self {
512            metadata,
513            ring_buffer: Arc::new(ring_buffer.into()),
514            sq_entries: Arc::new(sq_entries.into()),
515        })
516    }
517
518    fn write_header(&self, header: ControlHeader) -> Result<(), Errno> {
519        write_object(&self.ring_buffer, 0, &header).map_err(|_| errno!(ENOMEM))
520    }
521
522    fn read_sq_head(&self) -> Result<u32, Errno> {
523        read_object(&self.ring_buffer, std::mem::offset_of!(ControlHeader, sq_head) as u64)
524    }
525
526    fn write_sq_head(&self, value: u32) -> Result<(), Errno> {
527        write_object(&self.ring_buffer, std::mem::offset_of!(ControlHeader, sq_head) as u64, &value)
528    }
529
530    fn read_sq_tail(&self) -> Result<u32, Errno> {
531        // TODO(https://fxbug.dev/297431387): Reading the tail field should be atomic with ordering
532        // acquire once we map these buffers into kernel memory.
533        read_object(&self.ring_buffer, std::mem::offset_of!(ControlHeader, sq_tail) as u64)
534    }
535
536    fn read_cq_head(&self) -> Result<u32, Errno> {
537        // TODO(https://fxbug.dev/297431387): Reading the head field should be atomic with ordering
538        // acquire once we map these buffers into kernel memory.
539        read_object(&self.ring_buffer, std::mem::offset_of!(ControlHeader, cq_head) as u64)
540    }
541
542    fn read_cq_tail(&self) -> Result<u32, Errno> {
543        read_object(&self.ring_buffer, std::mem::offset_of!(ControlHeader, cq_tail) as u64)
544    }
545
546    fn write_cq_tail(&self, value: u32) -> Result<(), Errno> {
547        // TODO(https://fxbug.dev/297431387): Writing the tail field should be atomic with ordering
548        // release once we map these buffers into kernel memory.
549        write_object(&self.ring_buffer, std::mem::offset_of!(ControlHeader, cq_tail) as u64, &value)
550    }
551
552    fn read_array_entry(&self, index: u32) -> Result<u32, Errno> {
553        read_object(&self.ring_buffer, self.metadata.array_entry_offset(index))
554    }
555
556    fn read_sq_entry(&self, index: u32) -> Result<SqEntry, Errno> {
557        let sqe_index = self.read_array_entry(index)?;
558        read_object(&self.sq_entries, self.metadata.sq_entry_offset(sqe_index))
559    }
560
561    fn write_cq_entry(&self, index: u32, entry: &CqEntry) -> Result<(), Errno> {
562        write_object(&self.ring_buffer, self.metadata.cq_entry_offset(index), entry)
563    }
564
565    fn increment_overflow(&self) -> Result<(), Errno> {
566        // TODO(https://fxbug.dev/297431387): Incrementing the overflow count should be an atomic
567        // operation.
568        let offset = std::mem::offset_of!(ControlHeader, cq_overflow) as u64;
569        let mut overflow: u32 = read_object(&self.ring_buffer, offset)?;
570        overflow = overflow.saturating_add(1);
571        write_object(&self.ring_buffer, offset, &overflow)
572    }
573
574    /// Pop an entry off the submission queue and update the head to let userspace queue more
575    /// entries.
576    ///
577    /// Returns `None` if the submission queue is empty.
578    fn pop_sq_entry(&self) -> Result<Option<SqEntry>, Errno> {
579        let tail = self.read_sq_tail()?;
580        let head = self.read_sq_head()?;
581        if head != tail {
582            let sq_entry = self.read_sq_entry(head)?;
583            self.write_sq_head(head.wrapping_add(1))?;
584            Ok(Some(sq_entry))
585        } else {
586            Ok(None)
587        }
588    }
589
590    /// Push an entry onto the completion queue and update the tail to let userspace know a new
591    /// entry is available.
592    ///
593    /// If there is no room in the completion queue, this function will increment the overflow
594    /// counter.
595    fn push_cq_entry(&self, entry: &CqEntry) -> Result<(), Errno> {
596        let head = self.read_cq_head()?;
597        let tail = self.read_cq_tail()?;
598        // Check that the offset for the tail location doesn't collide with the head of the queue.
599        // This can happen because the entries are stored in a ring buffer.
600        if head != tail
601            && self.metadata.cq_entry_offset(tail) == self.metadata.cq_entry_offset(head)
602        {
603            self.increment_overflow()?;
604        } else {
605            self.write_cq_entry(tail, entry)?;
606            self.write_cq_tail(tail.wrapping_add(1))?;
607        }
608        Ok(())
609    }
610}
611
612pub struct IoUringFileObject {
613    queue: IoUringQueue,
614    state: LockDepMutex<IoUringFileMutableState, IoUringStateLock>,
615    _flags: IoRingSetupFlags,
616}
617
618#[derive(Default, Debug)]
619struct IoUringFileMutableState {
620    registered_buffers: UserBuffers,
621    registered_iobuffers: Vec<IoUringProviderRingBuffer>,
622}
623
624impl IoUringFileObject {
625    pub fn new_file(
626        current_task: &CurrentTask,
627        entries: u32,
628        params: &mut io_uring_params,
629    ) -> Result<FileHandle, Errno> {
630        let flags = IoRingSetupFlags::build_and_validate_from(params.flags)?;
631
632        let sq_entries = entries.next_power_of_two();
633        let cq_entries = if flags.contains(IoRingSetupFlags::CqSize) {
634            UserValue::from_raw(params.cq_entries)
635                .validate(sq_entries..IORING_MAX_CQ_ENTRIES)
636                .ok_or_else(|| errno!(EINVAL))?
637                .next_power_of_two()
638        } else {
639            // This operation cannot overflow because sq_entries is capped at IORING_MAX_ENTRIES,
640            // which is only 15 bits.
641            sq_entries * 2
642        };
643
644        let queue =
645            IoUringQueue::new(IoUringMetadata { sq_entries: sq_entries, cq_entries: cq_entries })?;
646
647        queue.write_header(ControlHeader {
648            sq_ring_mask: sq_entries - 1,
649            cq_ring_mask: cq_entries - 1,
650            sq_ring_entries: sq_entries,
651            cq_ring_entries: cq_entries,
652            ..Default::default()
653        })?;
654
655        params.sq_entries = sq_entries;
656        params.cq_entries = cq_entries;
657        params.features = IORING_FEAT_SINGLE_MMAP;
658        params.sq_off = io_sqring_offsets {
659            head: std::mem::offset_of!(ControlHeader, sq_head) as u32,
660            tail: std::mem::offset_of!(ControlHeader, sq_tail) as u32,
661            ring_mask: std::mem::offset_of!(ControlHeader, sq_ring_mask) as u32,
662            ring_entries: std::mem::offset_of!(ControlHeader, sq_ring_entries) as u32,
663            flags: std::mem::offset_of!(ControlHeader, sq_flags) as u32,
664            dropped: std::mem::offset_of!(ControlHeader, sq_dropped) as u32,
665            array: queue.metadata.array_offset() as u32,
666            ..Default::default()
667        };
668        params.cq_off = io_cqring_offsets {
669            head: std::mem::offset_of!(ControlHeader, cq_head) as u32,
670            tail: std::mem::offset_of!(ControlHeader, cq_tail) as u32,
671            ring_mask: std::mem::offset_of!(ControlHeader, cq_ring_mask) as u32,
672            ring_entries: std::mem::offset_of!(ControlHeader, cq_ring_entries) as u32,
673            overflow: std::mem::offset_of!(ControlHeader, cq_overflow) as u32,
674            cqes: queue.metadata.cqes_offset() as u32,
675            flags: std::mem::offset_of!(ControlHeader, cq_flags) as u32,
676            ..Default::default()
677        };
678
679        let object =
680            Box::new(IoUringFileObject { queue, state: Default::default(), _flags: flags });
681        Anon::new_file(current_task, object, OpenFlags::RDWR, "[io_uring]")
682    }
683
684    pub fn register_buffers(&self, buffers: UserBuffers) {
685        // The docs for io_uring_register imply that the kernel should actually map this memory
686        // into its own address space when these buffers are registered. That's probably observable
687        // if the client changes the mappings for these addresses between the time they are
688        // registered and they are used. For now, we just store the addresses.
689        self.state.lock().registered_buffers = buffers;
690    }
691
692    pub fn unregister_buffers(&self) {
693        self.state.lock().registered_buffers.clear();
694    }
695
696    pub fn register_ring_buffers(
697        &self,
698        buffer_definition: uapi::io_uring_buf_reg,
699    ) -> Result<(), Errno> {
700        track_stub!(
701            TODO("https://fxbug.dev/297431387"),
702            "IoUringFileObject::register_ring_buffers"
703        );
704        if !buffer_definition.ring_addr.is_multiple_of(*PAGE_SIZE) {
705            return error!(EINVAL);
706        }
707        if !buffer_definition.ring_entries.is_power_of_two() {
708            return error!(EINVAL);
709        }
710        if buffer_definition.ring_entries > IORING_MAX_ENTRIES {
711            return error!(EINVAL);
712        }
713        self.state
714            .lock()
715            .registered_iobuffers
716            .push(IoUringProviderRingBuffer::new(buffer_definition)?);
717        Ok(())
718    }
719
720    pub fn unregister_ring_buffers(
721        &self,
722        buffer_definition: uapi::io_uring_buf_reg,
723    ) -> Result<(), Errno> {
724        if self
725            .state
726            .lock()
727            .registered_iobuffers
728            .extract_if(.., |buffer| buffer.config.bgid == buffer_definition.bgid)
729            .next()
730            .is_none()
731        {
732            return error!(EINVAL);
733        }
734        Ok(())
735    }
736
737    pub fn ring_buffer_status(
738        &self,
739        buffer_status: &mut uapi::io_uring_buf_status,
740    ) -> Result<(), Errno> {
741        let state = self.state.lock();
742        let Some(buffer) = state
743            .registered_iobuffers
744            .iter()
745            .find(|buffer| buffer.config.bgid as u32 == buffer_status.buf_group)
746        else {
747            return error!(EINVAL);
748        };
749        buffer_status.head = buffer.head as u32;
750        Ok(())
751    }
752
753    pub fn enter(
754        &self,
755        current_task: &CurrentTask,
756        to_submit: u32,
757        _min_complete: u32,
758        _flags: u32,
759    ) -> Result<u32, Errno> {
760        let mut submitted = 0;
761        while let Some(sq_entry) = self.queue.pop_sq_entry()? {
762            submitted += 1;
763            // We currently act as if every SqEntry has IOSQE_IO_DRAIN.
764            let mut complete_flags: u32 = 0;
765            let result = self.execute(current_task, &sq_entry, &mut complete_flags);
766            let cq_entry = sq_entry.complete(result, complete_flags);
767            self.queue.push_cq_entry(&cq_entry)?;
768            if submitted >= to_submit {
769                break;
770            }
771        }
772        Ok(submitted)
773    }
774
775    fn has_registered_buffers(&self) -> bool {
776        !self.state.lock().registered_buffers.is_empty()
777    }
778
779    fn check_buffer(&self, entry: &SqEntry) -> Result<(), Errno> {
780        let index = entry.buf_index();
781        let state = self.state.lock();
782        let buffers = &state.registered_buffers;
783        if buffers.is_empty() {
784            return error!(EFAULT);
785        }
786        let buffer = buffers.get(index).ok_or_else(|| errno!(EINVAL))?;
787        if !buffer.contains(entry.address(), entry.length()) { error!(EFAULT) } else { Ok(()) }
788    }
789
790    fn execute(
791        &self,
792        current_task: &CurrentTask,
793        entry: &SqEntry,
794        complete_flags: &mut u32,
795    ) -> Result<SyscallResult, Errno> {
796        assert_eq!(*complete_flags, 0);
797
798        let flags = SqEntryFlags::from_bits(entry.flags).ok_or_else(|| errno!(EINVAL))?;
799        match Op::from_code(entry.opcode as io_uring_op)? {
800            Op::NOP => Ok(SUCCESS),
801            Op::ReadV => {
802                if !flags.is_empty() {
803                    return error!(EINVAL);
804                }
805                if entry.ioprio != 0 || entry.buf_index() != 0 {
806                    return error!(EINVAL);
807                }
808                sys_preadv2(
809                    current_task,
810                    entry.fd(),
811                    entry.iovec_addr(current_task),
812                    entry.iovec_count(),
813                    entry.offset(),
814                    SyscallArg::default(),
815                    entry.op_flags,
816                )
817                .map(Into::into)
818            }
819            Op::WriteV => {
820                if !flags.is_empty() {
821                    return error!(EINVAL);
822                }
823                if entry.ioprio != 0 || entry.buf_index() != 0 {
824                    return error!(EINVAL);
825                }
826                sys_pwritev2(
827                    current_task,
828                    entry.fd(),
829                    entry.iovec_addr(current_task),
830                    entry.iovec_count(),
831                    entry.offset(),
832                    SyscallArg::default(),
833                    entry.op_flags,
834                )
835                .map(Into::into)
836            }
837            Op::ReadFixed => {
838                if !flags.is_empty() {
839                    return error!(EINVAL);
840                }
841                if entry.ioprio != 0 {
842                    return error!(EINVAL);
843                }
844                // TODO(https://fxbug.dev/297431387): We're supposed to make a kernel mapping
845                // when the buffers are registered and we should be performing this operation using
846                // those kernel mappings rather than using the userspace mappings.
847                self.check_buffer(entry)?;
848                do_read(current_task, entry)
849            }
850            Op::WriteFixed => {
851                if !flags.is_empty() {
852                    return error!(EINVAL);
853                }
854                if entry.ioprio != 0 {
855                    return error!(EINVAL);
856                }
857                // TODO(https://fxbug.dev/297431387): We're supposed to make a kernel mapping
858                // when the buffers are registered and we should be performing this operation using
859                // those kernel mappings rather than using the userspace mappings.
860                self.check_buffer(entry)?;
861                do_write(current_task, entry)
862            }
863            Op::Read => {
864                if !flags.is_empty() {
865                    return error!(EINVAL);
866                }
867                if self.has_registered_buffers() {
868                    return error!(EINVAL);
869                }
870                do_read(current_task, entry)
871            }
872            Op::Write => {
873                if !flags.is_empty() {
874                    return error!(EINVAL);
875                }
876                if self.has_registered_buffers() {
877                    return error!(EINVAL);
878                }
879                do_write(current_task, entry)
880            }
881            Op::SendMsg => {
882                if !flags.is_empty() {
883                    return error!(EINVAL);
884                }
885                if entry.ioprio != 0 {
886                    return error!(EINVAL);
887                }
888                sys_sendmsg(
889                    current_task,
890                    entry.fd(),
891                    MsgHdrPtr::new(current_task, entry.address()),
892                    entry.op_flags,
893                )
894                .map(Into::into)
895            }
896            Op::RecvMsg => {
897                // A struct to hold the information about the provided buffer.
898                // This is needed because the buffer is claimed before the call to `recvmsg_impl`
899                // but the result is adjusted after.
900                struct RecvMsgBufferInfo {
901                    buffer: UserBuffer,
902                    header: uapi::io_uring_recvmsg_out,
903                    buffer_adjustment: usize,
904                }
905                let mut flags = flags;
906                let mut ioprio = entry.ioprio as u32;
907                let msg_hdr_ptr = MsgHdrPtr::new(current_task, entry.address());
908                let (mut msg_hdr_ref, recv_msg_buffer_info): (
909                    MsgHdrRef,
910                    Option<RecvMsgBufferInfo>,
911                ) = if flags.contains(SqEntryFlags::BUFFER_SELECT) {
912                    flags -= SqEntryFlags::BUFFER_SELECT;
913                    // If BUFFER_SELECT is set, the application is providing a buffer for the
914                    // recvmsg operation.
915                    let buffer =
916                        self.claim_next_buffer(current_task, entry.group(), complete_flags)?;
917                    let mut msg_hdr = current_task.read_multi_arch_object(msg_hdr_ptr)?;
918                    // The buffer is laid out as follows:
919                    // - io_uring_recvmsg_out
920                    // - sockaddr (name)
921                    // - msghdr.msg_control
922                    // - payload
923                    let headerlen: u32 = std::mem::size_of::<uapi::io_uring_recvmsg_out>() as u32;
924                    let namelen: u32 = msg_hdr.name_len.try_into().map_err(|_| errno!(EINVAL))?;
925                    let controllen: u32 =
926                        msg_hdr.control_len.try_into().map_err(|_| errno!(EINVAL))?;
927                    let buffer_adjustment: u32 = headerlen
928                        .checked_add(namelen)
929                        .and_then(|v| v.checked_add(controllen))
930                        .ok_or_else(|| errno!(EINVAL))?;
931                    let payloadlen: u32 = (buffer.length as u32)
932                        .checked_sub(buffer_adjustment)
933                        .ok_or_else(|| errno!(EINVAL))?;
934                    let io_uring_hdr = uapi::io_uring_recvmsg_out {
935                        namelen,
936                        controllen,
937                        payloadlen,
938                        flags: msg_hdr.flags,
939                    };
940
941                    let name_addr = (buffer.address + headerlen as usize)?;
942                    let control_addr = (name_addr + namelen as usize)?;
943                    let payload_addr = (control_addr + controllen as usize)?;
944                    msg_hdr.name = name_addr;
945                    msg_hdr.control = control_addr;
946
947                    // Zero out the prefix of the buffer that will contain the header, name and
948                    // control bytes.
949                    let prefix_len = buffer_adjustment as usize;
950                    if current_task.zero(buffer.address, prefix_len)? != prefix_len {
951                        return error!(EFAULT);
952                    }
953
954                    let msg_hdr = WithAlternateBuffer::WithAux(
955                        msg_hdr,
956                        UserBuffer { address: payload_addr, length: payloadlen as usize },
957                    );
958                    (
959                        msg_hdr.into(),
960                        Some(RecvMsgBufferInfo {
961                            buffer,
962                            header: io_uring_hdr,
963                            buffer_adjustment: buffer_adjustment as usize,
964                        }),
965                    )
966                } else {
967                    (msg_hdr_ptr.into(), None)
968                };
969                if ioprio & uapi::IORING_RECV_MULTISHOT > 0 {
970                    // Ignoring IORING_RECV_MULTISHOT
971                    // Because the IORING_CQE_F_BUFFER flags will never be set, the client will
972                    // always have to call the syscall again.
973                    ioprio &= !uapi::IORING_RECV_MULTISHOT;
974                }
975                if !flags.is_empty() {
976                    return error!(EINVAL);
977                }
978                if ioprio != 0 {
979                    return error!(EINVAL);
980                }
981                let mut count =
982                    recvmsg_impl(current_task, entry.fd(), &mut msg_hdr_ref, entry.op_flags)?;
983                if let Some(recv_msg_buffer_info) = recv_msg_buffer_info {
984                    // The result from `recvmsg_impl` is the number of bytes written to the
985                    // payload. The result of the io_uring operation is the number of bytes
986                    // written to the provided buffer.
987                    // 1. Write the io_uring buffer header.
988                    current_task.write_object(
989                        recv_msg_buffer_info.buffer.address.into(),
990                        &recv_msg_buffer_info.header,
991                    )?;
992                    // 2. Adjust the written count.
993                    count += recv_msg_buffer_info.buffer_adjustment;
994                }
995                Ok(count.into())
996            }
997            Op::Send => {
998                if !flags.is_empty() {
999                    return error!(EINVAL);
1000                }
1001                if entry.ioprio != 0 {
1002                    return error!(EINVAL);
1003                }
1004                sys_sendto(
1005                    current_task,
1006                    entry.fd(),
1007                    entry.address(),
1008                    entry.length(),
1009                    entry.op_flags,
1010                    UserAddress::default(),
1011                    socklen_t::default(),
1012                )
1013                .map(Into::into)
1014            }
1015            Op::Recv => {
1016                if !flags.is_empty() {
1017                    return error!(EINVAL);
1018                }
1019                if entry.ioprio != 0 {
1020                    return error!(EINVAL);
1021                }
1022                sys_recvfrom(
1023                    current_task,
1024                    entry.fd(),
1025                    entry.address(),
1026                    entry.length(),
1027                    entry.op_flags,
1028                    UserAddress::default(),
1029                    UserRef::default(),
1030                )
1031                .map(Into::into)
1032            }
1033        }
1034    }
1035
1036    fn claim_next_buffer(
1037        &self,
1038        current_task: &CurrentTask,
1039        bgid: u16,
1040        complete_flags: &mut u32,
1041    ) -> Result<UserBuffer, Errno> {
1042        let mut state = self.state.lock();
1043        let Some(buffer) =
1044            state.registered_iobuffers.iter_mut().find(|buffer| buffer.config.bgid == bgid)
1045        else {
1046            return error!(EINVAL);
1047        };
1048        buffer.claim_next(current_task, complete_flags)
1049    }
1050}
1051
1052#[derive(Debug)]
1053struct IoUringProviderRingBuffer {
1054    config: uapi::io_uring_buf_reg,
1055    tail_ptr: UserRef<u16>,
1056    entries_ptr: UserRef<UserRingBufferEntry>,
1057    head: u16,
1058}
1059
1060impl IoUringProviderRingBuffer {
1061    fn new(config: uapi::io_uring_buf_reg) -> Result<Self, Errno> {
1062        let ring_addr = UserAddress::from(config.ring_addr);
1063        let tail_ptr =
1064            UserRef::<u16>::from((ring_addr + std::mem::offset_of!(UserRingBufferHeader, tail))?);
1065        let entries_ptr = UserRef::<UserRingBufferEntry>::from(ring_addr);
1066        Ok(Self { config, tail_ptr, entries_ptr, head: 0 })
1067    }
1068
1069    fn claim_next(
1070        &mut self,
1071        current_task: &CurrentTask,
1072        complete_flags: &mut u32,
1073    ) -> Result<UserBuffer, Errno> {
1074        // TODO(https://fxbug.dev/297431387): Reading the tail field should be atomic with ordering
1075        // acquire.
1076        let tail = current_task.read_object(self.tail_ptr)?;
1077        if self.head == tail {
1078            return error!(ENOBUFS);
1079        }
1080        let buffer_info = current_task.read_object(
1081            self.entries_ptr.at((self.head as usize) % (self.config.ring_entries as usize))?,
1082        )?;
1083        self.head += 1;
1084        *complete_flags |=
1085            uapi::IORING_CQE_F_BUFFER | ((buffer_info.bid as u32) << uapi::IORING_CQE_BUFFER_SHIFT);
1086        Ok(UserBuffer { address: buffer_info.addr.into(), length: buffer_info.len as usize })
1087    }
1088}
1089
1090fn do_read(current_task: &CurrentTask, entry: &SqEntry) -> Result<SyscallResult, Errno> {
1091    let offset = entry.offset();
1092    if offset == -1 {
1093        sys_read(current_task, entry.fd(), entry.address(), entry.length()).map(Into::into)
1094    } else {
1095        sys_pread64(current_task, entry.fd(), entry.address(), entry.length(), offset)
1096            .map(Into::into)
1097    }
1098}
1099
1100fn do_write(current_task: &CurrentTask, entry: &SqEntry) -> Result<SyscallResult, Errno> {
1101    let offset = entry.offset();
1102    if offset == -1 {
1103        sys_write(current_task, entry.fd(), entry.address(), entry.length()).map(Into::into)
1104    } else {
1105        sys_pwrite64(current_task, entry.fd(), entry.address(), entry.length(), entry.offset())
1106            .map(Into::into)
1107    }
1108}
1109
1110impl FileOps for IoUringFileObject {
1111    fileops_impl_nonseekable!();
1112    fileops_impl_noop_sync!();
1113    fileops_impl_dataless!();
1114
1115    // The rings are mapped through memory shared with Starnix, which isn't executable.
1116    fn can_map_executable(&self) -> bool {
1117        false
1118    }
1119
1120    fn mmap(
1121        &self,
1122        file: &FileObject,
1123        current_task: &CurrentTask,
1124        addr: DesiredAddress,
1125        memory_offset: u64,
1126        length: usize,
1127        prot_flags: ProtectionFlags,
1128        options: MappingOptions,
1129    ) -> Result<UserAddress, Errno> {
1130        if !options.contains(MappingOptions::SHARED) {
1131            return error!(EINVAL);
1132        }
1133        let magic_offset: u32 = memory_offset.try_into().map_err(|_| errno!(EINVAL))?;
1134        let memory = match magic_offset {
1135            IORING_OFF_SQ_RING | IORING_OFF_CQ_RING => self.queue.ring_buffer.clone(),
1136            IORING_OFF_SQES => self.queue.sq_entries.clone(),
1137            _ => return error!(EINVAL),
1138        };
1139        current_task.mm()?.map_memory(
1140            addr,
1141            memory,
1142            0,
1143            length,
1144            prot_flags,
1145            options,
1146            MappingName::File(file.to_mapping(None)?),
1147        )
1148    }
1149}
1150
1151#[cfg(test)]
1152mod tests {
1153    use super::*;
1154
1155    #[::fuchsia::test]
1156    fn test_uring_cmd_not_supported() {
1157        // Implement the `uring_cmd` security hook (https://fxbug.dev/505326826) if Starnix
1158        // supports `IORING_OP_URING_CMD` (https://fxbug.dev/297431387).
1159        assert_eq!(
1160            Op::from_code(starnix_uapi::io_uring_op_IORING_OP_URING_CMD).err(),
1161            Some(errno!(EOPNOTSUPP))
1162        );
1163    }
1164}