linux_io_uring/
lib.rs

1//! The `io_uring` library for Rust.
2//!
3//! The crate only provides a summary of the parameters.
4//! For more detailed documentation, see manpage.
5
6mod util;
7mod register;
8mod submit;
9pub mod squeue;
10pub mod cqueue;
11pub mod opcode;
12
13#[cfg(feature = "concurrent")]
14pub mod concurrent;
15
16use std::{ io, cmp, mem };
17use std::convert::TryInto;
18use std::os::unix::io::{ AsRawFd, RawFd };
19use std::mem::ManuallyDrop;
20use linux_io_uring_sys as sys;
21use util::{ Fd, Mmap };
22pub use submit::Submitter;
23pub use squeue::SubmissionQueue;
24pub use cqueue::CompletionQueue;
25pub use register::{ register as reg, unregister as unreg };
26
27
28/// IoUring instance
29pub struct IoUring {
30    fd: Fd,
31    params: Parameters,
32    memory: ManuallyDrop<MemoryMap>,
33    sq: SubmissionQueue,
34    cq: CompletionQueue
35}
36
37#[allow(dead_code)]
38struct MemoryMap {
39    sq_mmap: Mmap,
40    sqe_mmap: Mmap,
41    cq_mmap: Option<Mmap>
42}
43
44/// IoUring build params
45#[derive(Clone, Default)]
46pub struct Builder(sys::io_uring_params);
47
48#[derive(Clone)]
49pub struct Parameters(sys::io_uring_params);
50
51unsafe impl Send for IoUring {}
52unsafe impl Sync for IoUring {}
53
54impl IoUring {
55    /// Create a IoUring instance
56    ///
57    /// The `entries` sets the size of queue,
58    /// and it value should be the power of two.
59    #[inline]
60    pub fn new(entries: u32) -> io::Result<IoUring> {
61        IoUring::with_params(entries, Default::default())
62    }
63
64    fn with_params(entries: u32, mut p: sys::io_uring_params) -> io::Result<IoUring> {
65        // NOTE: The `SubmissionQueue` and `CompletionQueue` are references,
66        // and their lifetime can never exceed `MemoryMap`.
67        //
68        // I really hope that Rust can safely use self-reference types.
69        #[inline]
70        unsafe fn setup_queue(fd: &Fd, p: &sys::io_uring_params)
71            -> io::Result<(MemoryMap, SubmissionQueue, CompletionQueue)>
72        {
73            let sq_len = p.sq_off.array as usize
74                + p.sq_entries as usize * mem::size_of::<u32>();
75            let cq_len = p.cq_off.cqes as usize
76                + p.cq_entries as usize * mem::size_of::<sys::io_uring_cqe>();
77            let sqe_len = p.sq_entries as usize * mem::size_of::<sys::io_uring_sqe>();
78            let sqe_mmap = Mmap::new(fd, sys::IORING_OFF_SQES as _, sqe_len)?;
79
80            if p.features & sys::IORING_FEAT_SINGLE_MMAP != 0 {
81                let scq_mmap = Mmap::new(fd, sys::IORING_OFF_SQ_RING as _, cmp::max(sq_len, cq_len))?;
82
83                let sq = SubmissionQueue::new(&scq_mmap, &sqe_mmap, p);
84                let cq = CompletionQueue::new(&scq_mmap, p);
85                let mm = MemoryMap {
86                    sq_mmap: scq_mmap,
87                    cq_mmap: None,
88                    sqe_mmap
89                };
90
91                Ok((mm, sq, cq))
92            } else {
93                let sq_mmap = Mmap::new(fd, sys::IORING_OFF_SQ_RING as _, sq_len)?;
94                let cq_mmap = Mmap::new(fd, sys::IORING_OFF_CQ_RING as _, cq_len)?;
95
96                let sq = SubmissionQueue::new(&sq_mmap, &sqe_mmap, p);
97                let cq = CompletionQueue::new(&cq_mmap, p);
98                let mm = MemoryMap {
99                    cq_mmap: Some(cq_mmap),
100                    sq_mmap, sqe_mmap
101                };
102
103                Ok((mm, sq, cq))
104            }
105        }
106
107        let fd: Fd = unsafe {
108            sys::io_uring_setup(entries, &mut p)
109                .try_into()
110                .map_err(|_| io::Error::last_os_error())?
111        };
112
113        let (mm, sq, cq) = unsafe { setup_queue(&fd, &p)? };
114
115        Ok(IoUring {
116            fd, sq, cq,
117            params: Parameters(p),
118            memory: ManuallyDrop::new(mm)
119        })
120    }
121
122    const fn as_submit(&self) -> Submitter<'_> {
123        Submitter::new(&self.fd, self.params.0.flags, &self.sq)
124    }
125
126    pub fn params(&self) -> &Parameters {
127        &self.params
128    }
129
130    /// Register files or user buffers for asynchronous I/O.
131    #[inline]
132    pub unsafe fn register(&self, target: reg::Target<'_>) -> io::Result<()> {
133        self.as_submit().register(target)
134    }
135
136    /// Unregister files or user buffers for asynchronous I/O.
137    #[inline]
138    pub fn unregister(&self, target: unreg::Target) -> io::Result<()> {
139        self.as_submit().unregister(target)
140    }
141
142    /// Initiate and/or complete asynchronous I/O
143    ///
144    /// # Safety
145    ///
146    /// This provides a raw interface so developer must ensure that parameters are correct.
147    #[inline]
148    pub unsafe fn enter(&self, to_submit: u32, min_complete: u32, flag: u32, sig: Option<&libc::sigset_t>)
149        -> io::Result<usize>
150    {
151        self.as_submit().enter(to_submit, min_complete, flag, sig)
152    }
153
154    /// Initiate asynchronous I/O.
155    #[inline]
156    pub fn submit(&self) -> io::Result<usize> {
157        self.as_submit().submit()
158    }
159
160    /// Initiate and/or complete asynchronous I/O
161    #[inline]
162    pub fn submit_and_wait(&self, want: usize) -> io::Result<usize> {
163        self.as_submit().submit_and_wait(want)
164    }
165
166    /// Get submitter and submission queue and completion queue
167    pub fn split(&mut self)
168        -> (Submitter<'_>, &mut SubmissionQueue, &mut CompletionQueue)
169    {
170        let submit = Submitter::new(&self.fd, self.params.0.flags, &self.sq);
171        (submit, &mut self.sq, &mut self.cq)
172    }
173
174    /// Get submission queue
175    pub fn submission(&mut self) -> &mut SubmissionQueue {
176        &mut self.sq
177    }
178
179    /// Get completion queue
180    pub fn completion(&mut self) -> &mut CompletionQueue {
181        &mut self.cq
182    }
183
184    /// Make a concurrent IoUring.
185    #[cfg(feature = "concurrent")]
186    pub fn concurrent(self) -> concurrent::IoUring {
187        concurrent::IoUring::new(self)
188    }
189}
190
191impl Drop for IoUring {
192    fn drop(&mut self) {
193        unsafe {
194            ManuallyDrop::drop(&mut self.memory);
195        }
196    }
197}
198
199impl Builder {
200    pub fn feature_single_mmap(&mut self) -> &mut Self {
201        self.0.features |= sys::IORING_FEAT_SINGLE_MMAP;
202        self
203    }
204
205    #[cfg(feature = "unstable")]
206    pub fn feature_nodrop(&mut self) -> &mut Self {
207        self.0.features |= sys::IORING_FEAT_NODROP;
208        self
209    }
210
211    #[cfg(feature = "unstable")]
212    pub fn feature_submit_stable(&mut self) -> &mut Self {
213        self.0.features |= sys::IORING_FEAT_SUBMIT_STABLE;
214        self
215    }
216
217    /// Perform busy-waiting for an I/O completion,
218    /// as opposed to getting notifications via an asynchronous IRQ (Interrupt Request).
219    pub fn setup_iopoll(&mut self) -> &mut Self {
220        self.0.flags |= sys::IORING_SETUP_IOPOLL;
221        self
222    }
223
224    /// When this flag is specified, a kernel thread is created to perform submission queue polling.
225    /// An io_uring instance configured in this way enables an application to issue I/O
226    /// without ever context switching into the kernel.
227    pub fn setup_sqpoll(&mut self, idle: impl Into<Option<u32>>) -> &mut Self {
228        self.0.flags |= sys::IORING_SETUP_SQPOLL;
229
230        if let Some(n) = idle.into() {
231            self.0.sq_thread_idle = n;
232        }
233
234        self
235    }
236
237    /// If this flag is specified,
238    /// then the poll thread will be bound to the cpu set in the value.
239    /// This flag is only meaningful when [Builder::setup_sqpoll] is enabled.
240    pub fn setup_sqpoll_cpu(&mut self, n: u32) -> &mut Self {
241        self.0.flags |= sys::IORING_SETUP_SQ_AFF;
242        self.0.sq_thread_cpu = n;
243        self
244    }
245
246    /// Create the completion queue with struct `io_uring_params.cq_entries` entries.
247    /// The value must be greater than entries, and may be rounded up to the next power-of-two.
248    #[cfg(feature = "unstable")]
249    pub fn setup_cqsize(&mut self, n: u32) -> &mut Self {
250        self.0.flags |= sys::IORING_SETUP_CQSIZE;
251        self.0.cq_entries = n;
252        self
253    }
254
255    /// Build a [IoUring].
256    #[inline]
257    pub fn build(self, entries: u32) -> io::Result<IoUring> {
258        IoUring::with_params(entries, self.0)
259    }
260}
261
262impl Parameters {
263    pub fn is_setup_sqpoll(&self) -> bool {
264        self.0.flags & sys::IORING_SETUP_SQPOLL != 0
265    }
266
267    #[cfg(feature = "unstable")]
268    pub fn is_feature_nodrop(&self) -> bool {
269        self.0.features & sys::IORING_FEAT_NODROP != 0
270    }
271
272    #[cfg(feature = "unstable")]
273    pub fn is_feature_submit_stable(&self) -> bool {
274        self.0.features & sys::IORING_FEAT_SUBMIT_STABLE != 0
275    }
276
277    pub fn sq_entries(&self) -> u32 {
278        self.0.sq_entries
279    }
280
281    pub fn cq_entries(&self) -> u32 {
282        self.0.cq_entries
283    }
284}
285
286impl AsRawFd for IoUring {
287    fn as_raw_fd(&self) -> RawFd {
288        self.fd.as_raw_fd()
289    }
290}