Skip to main content

ntex_io_uring/
squeue.rs

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