Skip to main content

io_uring/
squeue.rs

1//! Submission Queue
2
3use std::error::Error;
4use std::fmt::{self, Debug, Display, Formatter};
5use std::mem;
6use std::sync::atomic;
7
8use crate::sys;
9use crate::util::{private, unsync_load, Mmap};
10
11use bitflags::bitflags;
12
13pub(crate) struct Inner<E: EntryMarker> {
14    pub(crate) head: *const atomic::AtomicU32,
15    pub(crate) tail: *const atomic::AtomicU32,
16    pub(crate) ring_mask: u32,
17    pub(crate) ring_entries: u32,
18    pub(crate) flags: *const atomic::AtomicU32,
19    dropped: *const atomic::AtomicU32,
20
21    pub(crate) sqes: *mut E,
22}
23
24/// An io_uring instance's submission queue. This is used to send I/O requests to the kernel.
25pub struct SubmissionQueue<'a, E: EntryMarker = Entry> {
26    head: u32,
27    tail: u32,
28    queue: &'a Inner<E>,
29}
30
31/// A submission queue entry (SQE), representing a request for an I/O operation.
32///
33/// This is implemented for [`Entry`] and [`Entry128`].
34pub trait EntryMarker: Clone + Debug + From<Entry> + private::Sealed {
35    const BUILD_FLAGS: u32;
36
37    /// Set the application-supplied user data.
38    fn set_user_data(&mut self, user_data: u64);
39
40    /// Get the application-supplied user data.
41    fn get_user_data(&self) -> u64;
42}
43
44/// A 64-byte submission queue entry (SQE), representing a request for an I/O operation.
45///
46/// These can be created via opcodes in [`opcode`](crate::opcode).
47///
48/// # Example
49///
50/// ```
51/// use io_uring::{opcode, types};
52/// use std::ffi::CString;
53///
54/// let path = CString::new("/etc/passwd").unwrap();
55///
56/// // Create an Entry to open /etc/passwd for reading
57/// let entry = opcode::OpenAt::new(types::Fd(libc::AT_FDCWD), path.as_ptr())
58///     .flags(libc::O_RDONLY)
59///     .build()
60///     .user_data(0x42);
61/// ```
62#[repr(C)]
63pub struct Entry(pub(crate) sys::io_uring_sqe);
64
65/// A 128-byte submission queue entry (SQE), representing a request for an I/O operation.
66///
67/// These can be created via opcodes in [`opcode`](crate::opcode), or by converting
68/// from an [`Entry`] using the [`From`] trait.
69///
70/// # Example
71///
72/// ```
73/// use io_uring::{opcode, squeue::Entry128, types};
74/// use std::ffi::CString;
75///
76/// let path = CString::new("/etc/passwd").unwrap();
77///
78/// // Create an Entry128 to open /etc/passwd for reading
79/// let entry = Entry128::from(
80///     opcode::OpenAt::new(types::Fd(libc::AT_FDCWD), path.as_ptr())
81///         .flags(libc::O_RDONLY)
82///         .build()
83///         .user_data(0x42)
84/// );
85/// ```
86#[repr(C)]
87#[derive(Clone)]
88pub struct Entry128(pub(crate) Entry, pub(crate) [u8; 64]);
89
90#[test]
91fn test_entry_sizes() {
92    assert_eq!(mem::size_of::<Entry>(), 64);
93    assert_eq!(mem::size_of::<Entry128>(), 128);
94}
95
96#[test]
97fn test_user_data() {
98    fn replace<E: EntryMarker>(entry: &mut E, user_data: u64) -> u64 {
99        let previous = entry.get_user_data();
100        entry.set_user_data(user_data);
101        previous
102    }
103
104    let mut entry = crate::opcode::Nop::new().build().user_data(1);
105    assert_eq!(replace(&mut entry, 2), 1);
106    assert_eq!(entry.get_user_data(), 2);
107
108    let mut entry = Entry128::from(crate::opcode::Nop::new().build().user_data(3));
109    assert_eq!(replace(&mut entry, 4), 3);
110    assert_eq!(entry.get_user_data(), 4);
111}
112
113bitflags! {
114    /// Submission flags
115    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
116    pub struct Flags: u8 {
117        /// When this flag is specified,
118        /// `fd` is an index into the files array registered with the io_uring instance.
119        #[doc(hidden)]
120        const FIXED_FILE = 1 << sys::IOSQE_FIXED_FILE_BIT;
121
122        /// When this flag is specified,
123        /// the SQE will not be started before previously submitted SQEs have completed,
124        /// and new SQEs will not be started before this one completes.
125        const IO_DRAIN = 1 << sys::IOSQE_IO_DRAIN_BIT;
126
127        /// When this flag is specified,
128        /// it forms a link with the next SQE in the submission ring.
129        /// That next SQE will not be started before this one completes.
130        const IO_LINK = 1 << sys::IOSQE_IO_LINK_BIT;
131
132        /// Like [`IO_LINK`](Self::IO_LINK), but it doesn’t sever regardless of the completion
133        /// result.
134        const IO_HARDLINK = 1 << sys::IOSQE_IO_HARDLINK_BIT;
135
136        /// Normal operation for io_uring is to try and issue an sqe as non-blocking first,
137        /// and if that fails, execute it in an async manner.
138        ///
139        /// To support more efficient overlapped operation of requests
140        /// that the application knows/assumes will always (or most of the time) block,
141        /// the application can ask for an sqe to be issued async from the start.
142        const ASYNC = 1 << sys::IOSQE_ASYNC_BIT;
143
144        /// Conceptually the kernel holds a set of buffers organized into groups. When you issue a
145        /// request with this flag and set `buf_group` to a valid buffer group ID (e.g.
146        /// [`buf_group` on `Read`](crate::opcode::Read::buf_group)) then once the file descriptor
147        /// becomes ready the kernel will try to take a buffer from the group.
148        ///
149        /// If there are no buffers in the group, your request will fail with `-ENOBUFS`. Otherwise,
150        /// the corresponding [`cqueue::Entry::flags`](crate::cqueue::Entry::flags) will contain the
151        /// chosen buffer ID, encoded with:
152        ///
153        /// ```text
154        /// (buffer_id << IORING_CQE_BUFFER_SHIFT) | IORING_CQE_F_BUFFER
155        /// ```
156        ///
157        /// You can use [`buffer_select`](crate::cqueue::buffer_select) to take the buffer ID.
158        ///
159        /// The buffer will then be removed from the group and won't be usable by other requests
160        /// anymore.
161        ///
162        /// You can provide new buffers in a group with
163        /// [`ProvideBuffers`](crate::opcode::ProvideBuffers).
164        ///
165        /// See also [the LWN thread on automatic buffer
166        /// selection](https://lwn.net/Articles/815491/).
167        const BUFFER_SELECT = 1 << sys::IOSQE_BUFFER_SELECT_BIT;
168
169        /// Don't post CQE if request succeeded.
170        const SKIP_SUCCESS = 1 << sys::IOSQE_CQE_SKIP_SUCCESS_BIT;
171    }
172}
173
174impl<E: EntryMarker> Inner<E> {
175    #[rustfmt::skip]
176    pub(crate) unsafe fn new(
177        sq_mmap: &Mmap,
178        sqe_mmap: &Mmap,
179        p: &sys::io_uring_params,
180    ) -> Self {
181        let head         = sq_mmap.offset(p.sq_off.head        ) as *const atomic::AtomicU32;
182        let tail         = sq_mmap.offset(p.sq_off.tail        ) as *const atomic::AtomicU32;
183        let ring_mask    = sq_mmap.offset(p.sq_off.ring_mask   ).cast::<u32>().read();
184        let ring_entries = sq_mmap.offset(p.sq_off.ring_entries).cast::<u32>().read();
185        let flags        = sq_mmap.offset(p.sq_off.flags       ) as *const atomic::AtomicU32;
186        let dropped      = sq_mmap.offset(p.sq_off.dropped     ) as *const atomic::AtomicU32;
187        let sqes         = sqe_mmap.as_mut_ptr() as *mut E;
188
189        // Initialize the SQ array with an identity mapping unless NO_SQARRAY is set, in which case
190        // the kernel consumes SQEs directly by ring index and no array exists.
191        if p.flags & sys::IORING_SETUP_NO_SQARRAY == 0 {
192            let array = sq_mmap.offset(p.sq_off.array) as *mut u32;
193            for i in 0..ring_entries {
194                array.add(i as usize).write_volatile(i);
195            }
196        }
197
198        Self {
199            head,
200            tail,
201            ring_mask,
202            ring_entries,
203            flags,
204            dropped,
205            sqes,
206        }
207    }
208
209    #[inline]
210    pub(crate) unsafe fn borrow_shared(&self) -> SubmissionQueue<'_, E> {
211        SubmissionQueue {
212            head: (*self.head).load(atomic::Ordering::Acquire),
213            tail: unsync_load(self.tail),
214            queue: self,
215        }
216    }
217
218    #[inline]
219    pub(crate) fn borrow(&mut self) -> SubmissionQueue<'_, E> {
220        unsafe { self.borrow_shared() }
221    }
222}
223
224impl<E: EntryMarker> SubmissionQueue<'_, E> {
225    /// Synchronize this type with the real submission queue.
226    ///
227    /// This will flush any entries added by [`push`](Self::push) or
228    /// [`push_multiple`](Self::push_multiple) and will update the queue's length if the kernel has
229    /// consumed some entries in the meantime.
230    #[inline]
231    pub fn sync(&mut self) {
232        unsafe {
233            (*self.queue.tail).store(self.tail, atomic::Ordering::Release);
234            self.head = (*self.queue.head).load(atomic::Ordering::Acquire);
235        }
236    }
237
238    /// When [`is_setup_sqpoll`](crate::Parameters::is_setup_sqpoll) is set, whether the kernel
239    /// threads has gone to sleep and requires a system call to wake it up.
240    ///
241    /// A result of `false` is only meaningful if the function was called after the latest update
242    /// to the queue head. Other interpretations could lead to a race condition where the kernel
243    /// concurrently put the device to sleep and no further progress is made.
244    #[inline]
245    pub fn need_wakeup(&self) -> bool {
246        // See discussions that happened in [#197] and its linked threads in liburing. We need to
247        // ensure that writes to the head have been visible _to the kernel_ if this load results in
248        // decision to sleep. This is solved with a SeqCst fence. There is no common modified
249        // memory location that would provide alternative synchronization.
250        //
251        // The kernel, from its sequencing, first writes the wake flag, then performs a full
252        // barrier (`smp_mb`, or `smp_mb__after_atomic`), then reads the head. We assume that our
253        // user first writes the head and then reads the `need_wakeup` flag as documented. It is
254        // necessary to ensure that at least one observes the other write. By establishing a point
255        // of sequential consistency on both sides between their respective write and read, at
256        // least one coherency order holds. With regards to the interpretation of the atomic memory
257        // model of Rust (that is, that of C++20) we're assuming that an `smp_mb` provides at least
258        // the effect of a `fence(SeqCst)`.
259        //
260        // [#197]: https://github.com/tokio-rs/io-uring/issues/197
261        atomic::fence(atomic::Ordering::SeqCst);
262        unsafe {
263            (*self.queue.flags).load(atomic::Ordering::Relaxed) & sys::IORING_SQ_NEED_WAKEUP != 0
264        }
265    }
266
267    /// The effect of [`Self::need_wakeup`], after synchronization work performed by the caller.
268    ///
269    /// This function should only be called if the caller can guarantee that a `SeqCst` fence has
270    /// been inserted after the last write to the queue's head. The function is then a little more
271    /// efficient by avoiding to perform one itself.
272    ///
273    /// Failure to uphold the precondition can result in an effective dead-lock due to a sleeping
274    /// device.
275    #[inline]
276    pub fn need_wakeup_after_intermittent_seqcst(&self) -> bool {
277        unsafe {
278            (*self.queue.flags).load(atomic::Ordering::Relaxed) & sys::IORING_SQ_NEED_WAKEUP != 0
279        }
280    }
281
282    /// The number of invalid submission queue entries that have been encountered in the ring
283    /// buffer.
284    pub fn dropped(&self) -> u32 {
285        unsafe { (*self.queue.dropped).load(atomic::Ordering::Acquire) }
286    }
287
288    /// Returns `true` if the completion queue ring is overflown.
289    pub fn cq_overflow(&self) -> bool {
290        unsafe {
291            (*self.queue.flags).load(atomic::Ordering::Acquire) & sys::IORING_SQ_CQ_OVERFLOW != 0
292        }
293    }
294
295    /// Returns `true` if completions are pending that should be processed. Only relevant when used
296    /// in conjuction with the `setup_taskrun_flag` function. Available since 5.19.
297    pub fn taskrun(&self) -> bool {
298        unsafe { (*self.queue.flags).load(atomic::Ordering::Acquire) & sys::IORING_SQ_TASKRUN != 0 }
299    }
300
301    /// Get the total number of entries in the submission queue ring buffer.
302    #[inline]
303    pub fn capacity(&self) -> usize {
304        self.queue.ring_entries as usize
305    }
306
307    /// Get the number of submission queue events in the ring buffer.
308    #[inline]
309    pub fn len(&self) -> usize {
310        self.tail.wrapping_sub(self.head) as usize
311    }
312
313    /// Returns `true` if the submission queue ring buffer is empty.
314    #[inline]
315    pub fn is_empty(&self) -> bool {
316        self.len() == 0
317    }
318
319    /// Returns `true` if the submission queue ring buffer has reached capacity, and no more events
320    /// can be added before the kernel consumes some.
321    #[inline]
322    pub fn is_full(&self) -> bool {
323        self.len() == self.capacity()
324    }
325
326    /// Attempts to push an entry into the queue.
327    /// If the queue is full, an error is returned.
328    ///
329    /// # Safety
330    ///
331    /// Developers must ensure that parameters of the entry (such as buffer) are valid and will
332    /// be valid for the entire duration of the operation, otherwise it may cause memory problems.
333    #[inline]
334    pub unsafe fn push(&mut self, entry: &E) -> Result<(), PushError> {
335        if !self.is_full() {
336            self.push_unchecked(entry);
337            Ok(())
338        } else {
339            Err(PushError)
340        }
341    }
342
343    /// Attempts to push several entries into the queue.
344    /// If the queue does not have space for all of the entries, an error is returned.
345    ///
346    /// # Safety
347    ///
348    /// Developers must ensure that parameters of all the entries (such as buffer) are valid and
349    /// will be valid for the entire duration of the operation, otherwise it may cause memory
350    /// problems.
351    #[inline]
352    pub unsafe fn push_multiple(&mut self, entries: &[E]) -> Result<(), PushError> {
353        if self.capacity() - self.len() < entries.len() {
354            return Err(PushError);
355        }
356
357        for entry in entries {
358            self.push_unchecked(entry);
359        }
360
361        Ok(())
362    }
363
364    #[inline]
365    unsafe fn push_unchecked(&mut self, entry: &E) {
366        *self
367            .queue
368            .sqes
369            .add((self.tail & self.queue.ring_mask) as usize) = entry.clone();
370        self.tail = self.tail.wrapping_add(1);
371    }
372}
373
374impl<E: EntryMarker> Drop for SubmissionQueue<'_, E> {
375    #[inline]
376    fn drop(&mut self) {
377        unsafe { &*self.queue.tail }.store(self.tail, atomic::Ordering::Release);
378    }
379}
380
381impl Entry {
382    /// Set the submission event's [flags](Flags).
383    #[inline]
384    pub fn flags(mut self, flags: Flags) -> Entry {
385        self.0.flags |= flags.bits();
386        self
387    }
388
389    /// Clear the submission event's [flags](Flags).
390    #[inline]
391    pub fn clear_flags(mut self) -> Entry {
392        self.0.flags = 0;
393        self
394    }
395
396    /// Set the user data. This is an application-supplied value that will be passed straight
397    /// through into the [completion queue entry](crate::cqueue::Entry::user_data).
398    #[inline]
399    pub fn user_data(mut self, user_data: u64) -> Entry {
400        self.0.user_data = user_data;
401        self
402    }
403
404    /// Set the user_data without consuming the entry.
405    #[inline]
406    pub fn set_user_data(&mut self, user_data: u64) {
407        self.0.user_data = user_data;
408    }
409
410    /// Get the previously application-supplied user data.
411    #[inline]
412    pub fn get_user_data(&self) -> u64 {
413        self.0.user_data
414    }
415
416    /// Get the opcode associated with this entry.
417    #[inline]
418    pub fn get_opcode(&self) -> u32 {
419        self.0.opcode.into()
420    }
421
422    /// Set the personality of this event. You can obtain a personality using
423    /// [`Submitter::register_personality`](crate::Submitter::register_personality).
424    pub fn personality(mut self, personality: u16) -> Entry {
425        self.0.personality = personality;
426        self
427    }
428}
429
430impl private::Sealed for Entry {}
431
432impl EntryMarker for Entry {
433    const BUILD_FLAGS: u32 = 0;
434
435    #[inline]
436    fn set_user_data(&mut self, user_data: u64) {
437        Entry::set_user_data(self, user_data);
438    }
439
440    #[inline]
441    fn get_user_data(&self) -> u64 {
442        Entry::get_user_data(self)
443    }
444}
445
446impl Clone for Entry {
447    #[inline(always)]
448    fn clone(&self) -> Entry {
449        // io_uring_sqe doesn't implement Clone due to the 'cmd' incomplete array field.
450        Entry(unsafe { mem::transmute_copy(&self.0) })
451    }
452}
453
454impl Debug for Entry {
455    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
456        f.debug_struct("Entry")
457            .field("op_code", &self.0.opcode)
458            .field("flags", &self.0.flags)
459            .field("user_data", &self.0.user_data)
460            .finish()
461    }
462}
463
464impl Entry128 {
465    /// Set the submission event's [flags](Flags).
466    #[inline]
467    pub fn flags(mut self, flags: Flags) -> Entry128 {
468        self.0 .0.flags |= flags.bits();
469        self
470    }
471
472    /// Clear the submission event's [flags](Flags).
473    #[inline]
474    pub fn clear_flags(mut self) -> Entry128 {
475        self.0 .0.flags = 0;
476        self
477    }
478
479    /// Set the user data. This is an application-supplied value that will be passed straight
480    /// through into the [completion queue entry](crate::cqueue::Entry::user_data).
481    #[inline]
482    pub fn user_data(mut self, user_data: u64) -> Entry128 {
483        self.0 .0.user_data = user_data;
484        self
485    }
486
487    /// Set the user data without consuming the entry.
488    #[inline]
489    pub fn set_user_data(&mut self, user_data: u64) {
490        self.0 .0.user_data = user_data;
491    }
492
493    /// Get the previously application-supplied user data.
494    #[inline]
495    pub fn get_user_data(&self) -> u64 {
496        self.0 .0.user_data
497    }
498
499    /// Set the personality of this event. You can obtain a personality using
500    /// [`Submitter::register_personality`](crate::Submitter::register_personality).
501    #[inline]
502    pub fn personality(mut self, personality: u16) -> Entry128 {
503        self.0 .0.personality = personality;
504        self
505    }
506
507    /// Get the opcode associated with this entry.
508    #[inline]
509    pub fn get_opcode(&self) -> u32 {
510        self.0 .0.opcode.into()
511    }
512}
513
514impl private::Sealed for Entry128 {}
515
516impl EntryMarker for Entry128 {
517    const BUILD_FLAGS: u32 = sys::IORING_SETUP_SQE128;
518
519    #[inline]
520    fn set_user_data(&mut self, user_data: u64) {
521        Entry128::set_user_data(self, user_data);
522    }
523
524    #[inline]
525    fn get_user_data(&self) -> u64 {
526        Entry128::get_user_data(self)
527    }
528}
529
530impl From<Entry> for Entry128 {
531    fn from(entry: Entry) -> Entry128 {
532        Entry128(entry, [0u8; 64])
533    }
534}
535
536impl Debug for Entry128 {
537    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
538        f.debug_struct("Entry128")
539            .field("op_code", &self.0 .0.opcode)
540            .field("flags", &self.0 .0.flags)
541            .field("user_data", &self.0 .0.user_data)
542            .finish()
543    }
544}
545
546/// An error pushing to the submission queue due to it being full.
547#[derive(Debug, Clone, PartialEq, Eq)]
548#[non_exhaustive]
549pub struct PushError;
550
551impl Display for PushError {
552    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
553        f.write_str("submission queue is full")
554    }
555}
556
557impl Error for PushError {}
558
559impl<E: EntryMarker> Debug for SubmissionQueue<'_, E> {
560    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
561        let mut d = f.debug_list();
562        let mut pos = self.head;
563        while pos != self.tail {
564            let entry: &E = unsafe { &*self.queue.sqes.add((pos & self.queue.ring_mask) as usize) };
565            d.entry(&entry);
566            pos = pos.wrapping_add(1);
567        }
568        d.finish()
569    }
570}