vmm/io_uring/operation/
mod.rs

1// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Module exposing data structures for working with io_uring operations.
5
6mod cqe;
7mod sqe;
8
9use std::convert::From;
10use std::fmt::{self, Debug};
11
12pub use cqe::Cqe;
13pub(crate) use sqe::Sqe;
14
15use crate::io_uring::generated::{io_uring_op, io_uring_sqe, io_uring_sqe_flags_bit};
16
17/// The index of a registered fd.
18pub type FixedFd = u32;
19
20#[repr(u8)]
21#[derive(Debug, Clone, Copy)]
22// These constants are generated as u32, but we use u8; const try_from() is unstable
23#[allow(clippy::cast_possible_truncation)]
24/// Supported operation types.
25pub enum OpCode {
26    /// Read operation.
27    Read = io_uring_op::IORING_OP_READ as u8,
28    /// Write operation.
29    Write = io_uring_op::IORING_OP_WRITE as u8,
30    /// Fsync operation.
31    Fsync = io_uring_op::IORING_OP_FSYNC as u8,
32}
33
34// Useful for outputting errors.
35impl From<OpCode> for &'static str {
36    fn from(opcode: OpCode) -> Self {
37        match opcode {
38            OpCode::Read => "read",
39            OpCode::Write => "write",
40            OpCode::Fsync => "fsync",
41        }
42    }
43}
44
45/// Operation type for populating the submission queue, parametrised with the `user_data` type `T`.
46/// The `user_data` is used for identifying the operation once completed.
47pub struct Operation<T> {
48    fd: FixedFd,
49    pub(crate) opcode: OpCode,
50    pub(crate) addr: Option<usize>,
51    pub(crate) len: Option<u32>,
52    flags: u8,
53    pub(crate) offset: Option<u64>,
54    pub(crate) user_data: T,
55}
56
57// Needed for proptesting.
58impl<T> fmt::Debug for Operation<T> {
59    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
60        write!(
61            f,
62            "
63            Operation {{
64                opcode: {:?},
65                addr: {:?},
66                len: {:?},
67                offset: {:?},
68            }}
69        ",
70            self.opcode, self.addr, self.len, self.offset
71        )
72    }
73}
74
75#[allow(clippy::len_without_is_empty)]
76impl<T: Debug> Operation<T> {
77    /// Construct a read operation.
78    pub fn read(fd: FixedFd, addr: usize, len: u32, offset: u64, user_data: T) -> Self {
79        Self {
80            fd,
81            opcode: OpCode::Read,
82            addr: Some(addr),
83            len: Some(len),
84            flags: 0,
85            offset: Some(offset),
86            user_data,
87        }
88    }
89
90    /// Construct a write operation.
91    pub fn write(fd: FixedFd, addr: usize, len: u32, offset: u64, user_data: T) -> Self {
92        Self {
93            fd,
94            opcode: OpCode::Write,
95            addr: Some(addr),
96            len: Some(len),
97            flags: 0,
98            offset: Some(offset),
99            user_data,
100        }
101    }
102
103    /// Construct a fsync operation.
104    pub fn fsync(fd: FixedFd, user_data: T) -> Self {
105        Self {
106            fd,
107            opcode: OpCode::Fsync,
108            addr: None,
109            len: None,
110            flags: 0,
111            offset: None,
112            user_data,
113        }
114    }
115
116    pub(crate) fn fd(&self) -> FixedFd {
117        self.fd
118    }
119
120    // Needed for proptesting.
121    #[cfg(test)]
122    pub(crate) fn set_linked(&mut self) {
123        self.flags |= 1 << io_uring_sqe_flags_bit::IOSQE_IO_LINK_BIT;
124    }
125
126    /// Transform the operation into an `Sqe`.
127    /// Note: remember remove user_data from slab or it will leak.
128    pub(crate) fn into_sqe(self, slab: &mut slab::Slab<T>) -> Sqe {
129        // SAFETY:
130        // Safe because all-zero value is valid. The sqe is made up of integers and raw pointers.
131        let mut inner: io_uring_sqe = unsafe { std::mem::zeroed() };
132
133        inner.opcode = self.opcode as u8;
134        inner.fd = i32::try_from(self.fd).unwrap();
135        // Simplifying assumption that we only used pre-registered FDs.
136        inner.flags = self.flags | (1 << io_uring_sqe_flags_bit::IOSQE_FIXED_FILE_BIT);
137
138        if let Some(addr) = self.addr {
139            inner.__bindgen_anon_2.addr = addr as u64;
140        }
141
142        if let Some(len) = self.len {
143            inner.len = len;
144        }
145
146        if let Some(offset) = self.offset {
147            inner.__bindgen_anon_1.off = offset;
148        }
149        inner.user_data = slab.insert(self.user_data) as u64;
150
151        Sqe::new(inner)
152    }
153}