Skip to main content

io_uring/
cqueue.rs

1//! Completion Queue
2
3use std::fmt::{self, Debug};
4use std::mem;
5use std::mem::MaybeUninit;
6use std::sync::atomic;
7
8use crate::sys;
9use crate::util::{private, unsync_load, Mmap};
10
11pub(crate) struct Inner<E: EntryMarker> {
12    head: *const atomic::AtomicU32,
13    tail: *const atomic::AtomicU32,
14    ring_mask: u32,
15    ring_entries: u32,
16
17    overflow: *const atomic::AtomicU32,
18
19    cqes: *const E,
20
21    #[allow(dead_code)]
22    flags: *const atomic::AtomicU32,
23}
24
25/// An io_uring instance's completion queue. This stores all the I/O operations that have completed.
26pub struct CompletionQueue<'a, E: EntryMarker = Entry> {
27    head: u32,
28    tail: u32,
29    queue: &'a Inner<E>,
30}
31
32/// A read-only view of whether the completion queue has entries pending.
33///
34/// Obtained from [`CompletionQueue::status`]. Unlike the queue itself this
35/// borrows nothing, so it can be captured once at startup and consulted from
36/// anywhere afterwards. The check costs two loads, with no system call, no
37/// locking, and no access to the ring — which is what a thread-per-core runtime
38/// needs on its preemption check, where reaching the queue would cost more than
39/// the comparison does.
40///
41/// # Staleness
42///
43/// The answer is stale as soon as it is returned: the kernel may post a
44/// completion immediately after the load. This is inherent — any check that
45/// does not enter the kernel is a snapshot — and it makes this suitable for
46/// hints such as "should I stop what I am doing and poll?", where being one
47/// iteration late costs nothing. It is not a synchronization primitive, and
48/// [`CompletionQueue::is_empty`] carries exactly the same caveat.
49pub struct CompletionStatus {
50    head: *const atomic::AtomicU32,
51    tail: *const atomic::AtomicU32,
52}
53
54impl CompletionStatus {
55    /// Returns `true` if the kernel has posted no completions beyond those
56    /// already consumed.
57    ///
58    /// See the note on staleness in the type documentation.
59    #[inline]
60    pub fn is_empty(&self) -> bool {
61        // SAFETY: both pointers are valid for the life of the ring, per the
62        // contract on `CompletionQueue::status`, and are only read here. The
63        // head is advanced solely by this library from the owning thread, so a
64        // non-atomic load is sound; the tail is written by the kernel, so it
65        // needs `Acquire` to order against the entries it publishes.
66        unsafe { unsync_load(self.head) == (*self.tail).load(atomic::Ordering::Acquire) }
67    }
68}
69
70impl Debug for CompletionStatus {
71    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72        f.debug_struct("CompletionStatus")
73            .field("is_empty", &self.is_empty())
74            .finish()
75    }
76}
77
78/// A completion queue entry (CQE), representing a complete I/O operation.
79///
80/// This is implemented for [`Entry`] and [`Entry32`].
81pub trait EntryMarker: Clone + Debug + Into<Entry> + private::Sealed {
82    const BUILD_FLAGS: u32;
83
84    /// Get the application-supplied user data.
85    fn user_data(&self) -> u64;
86
87    /// Replace the application-supplied user data.
88    fn set_user_data(&mut self, user_data: u64);
89}
90
91/// A 16-byte completion queue entry (CQE), representing a complete I/O operation.
92#[repr(C)]
93pub struct Entry(pub(crate) sys::io_uring_cqe);
94
95/// A 32-byte completion queue entry (CQE), representing a complete I/O operation.
96#[repr(C)]
97#[derive(Clone)]
98pub struct Entry32(pub(crate) Entry, pub(crate) [u64; 2]);
99
100#[test]
101fn test_entry_sizes() {
102    assert_eq!(mem::size_of::<Entry>(), 16);
103    assert_eq!(mem::size_of::<Entry32>(), 32);
104}
105
106#[test]
107fn test_user_data() {
108    fn cqe(user_data: u64) -> Entry {
109        Entry(sys::io_uring_cqe {
110            user_data,
111            res: 0,
112            flags: 0,
113            big_cqe: sys::__IncompleteArrayField::new(),
114        })
115    }
116
117    fn replace<E: EntryMarker>(entry: &mut E, user_data: u64) -> u64 {
118        let previous = entry.user_data();
119        entry.set_user_data(user_data);
120        previous
121    }
122
123    let mut entry = cqe(1);
124    assert_eq!(replace(&mut entry, 2), 1);
125    assert_eq!(entry.user_data(), 2);
126
127    let mut entry = Entry32(cqe(3), [0; 2]);
128    assert_eq!(replace(&mut entry, 4), 3);
129    assert_eq!(entry.user_data(), 4);
130}
131
132impl<E: EntryMarker> Inner<E> {
133    #[rustfmt::skip]
134    pub(crate) unsafe fn new(cq_mmap: &Mmap, p: &sys::io_uring_params) -> Self {
135        let head         = cq_mmap.offset(p.cq_off.head         ) as *const atomic::AtomicU32;
136        let tail         = cq_mmap.offset(p.cq_off.tail         ) as *const atomic::AtomicU32;
137        let ring_mask    = cq_mmap.offset(p.cq_off.ring_mask    ).cast::<u32>().read();
138        let ring_entries = cq_mmap.offset(p.cq_off.ring_entries ).cast::<u32>().read();
139        let overflow     = cq_mmap.offset(p.cq_off.overflow     ) as *const atomic::AtomicU32;
140        let cqes         = cq_mmap.offset(p.cq_off.cqes         ) as *const E;
141        let flags        = cq_mmap.offset(p.cq_off.flags        ) as *const atomic::AtomicU32;
142
143        Self {
144            head,
145            tail,
146            ring_mask,
147            ring_entries,
148            overflow,
149            cqes,
150            flags,
151        }
152    }
153
154    #[inline]
155    pub(crate) unsafe fn borrow_shared(&self) -> CompletionQueue<'_, E> {
156        CompletionQueue {
157            head: unsync_load(self.head),
158            tail: (*self.tail).load(atomic::Ordering::Acquire),
159            queue: self,
160        }
161    }
162
163    #[inline]
164    pub(crate) fn borrow(&mut self) -> CompletionQueue<'_, E> {
165        unsafe { self.borrow_shared() }
166    }
167}
168
169impl<E: EntryMarker> CompletionQueue<'_, E> {
170    /// Returns a [`CompletionStatus`], with which a caller can observe that the
171    /// kernel has posted completions without borrowing this queue.
172    ///
173    /// # Safety
174    ///
175    /// The returned value borrows nothing, and so must not outlive the
176    /// [`IoUring`](crate::IoUring) this queue came from.
177    #[inline]
178    pub unsafe fn status(&self) -> CompletionStatus {
179        CompletionStatus {
180            head: self.queue.head,
181            tail: self.queue.tail,
182        }
183    }
184
185    /// Synchronize this type with the real completion queue.
186    ///
187    /// This will flush any entries consumed in this iterator and will make available new entries
188    /// in the queue if the kernel has produced some entries in the meantime.
189    #[inline]
190    pub fn sync(&mut self) {
191        unsafe {
192            (*self.queue.head).store(self.head, atomic::Ordering::Release);
193            self.tail = (*self.queue.tail).load(atomic::Ordering::Acquire);
194        }
195    }
196
197    /// If queue is full and [`is_feature_nodrop`](crate::Parameters::is_feature_nodrop) is not set,
198    /// new events may be dropped. This records the number of dropped events.
199    pub fn overflow(&self) -> u32 {
200        unsafe { (*self.queue.overflow).load(atomic::Ordering::Acquire) }
201    }
202
203    /// Whether eventfd notifications are disabled when a request is completed and queued to the CQ
204    /// ring. This library currently does not provide a way to set it, so this will always be
205    /// `false`.
206    pub fn eventfd_disabled(&self) -> bool {
207        unsafe {
208            (*self.queue.flags).load(atomic::Ordering::Acquire) & sys::IORING_CQ_EVENTFD_DISABLED
209                != 0
210        }
211    }
212
213    /// Get the total number of entries in the completion queue ring buffer.
214    #[inline]
215    pub fn capacity(&self) -> usize {
216        self.queue.ring_entries as usize
217    }
218
219    /// Returns `true` if there are no completion queue events to be processed.
220    #[inline]
221    pub fn is_empty(&self) -> bool {
222        self.len() == 0
223    }
224
225    /// Returns `true` if the completion queue is at maximum capacity. If
226    /// [`is_feature_nodrop`](crate::Parameters::is_feature_nodrop) is not set, this will cause any
227    /// new completion queue events to be dropped by the kernel.
228    #[inline]
229    pub fn is_full(&self) -> bool {
230        self.len() == self.capacity()
231    }
232
233    #[inline]
234    pub fn fill<'a>(&mut self, entries: &'a mut [MaybeUninit<E>]) -> &'a mut [E] {
235        let len = std::cmp::min(self.len(), entries.len());
236
237        for entry in &mut entries[..len] {
238            entry.write(unsafe { self.pop() });
239        }
240
241        unsafe { std::slice::from_raw_parts_mut(entries as *mut _ as *mut E, len) }
242    }
243
244    #[inline]
245    unsafe fn pop(&mut self) -> E {
246        let entry = &*self
247            .queue
248            .cqes
249            .add((self.head & self.queue.ring_mask) as usize);
250        self.head = self.head.wrapping_add(1);
251        entry.clone()
252    }
253}
254
255impl<E: EntryMarker> Drop for CompletionQueue<'_, E> {
256    #[inline]
257    fn drop(&mut self) {
258        unsafe { &*self.queue.head }.store(self.head, atomic::Ordering::Release);
259    }
260}
261
262impl<E: EntryMarker> Iterator for CompletionQueue<'_, E> {
263    type Item = E;
264
265    #[inline]
266    fn next(&mut self) -> Option<Self::Item> {
267        if self.head != self.tail {
268            Some(unsafe { self.pop() })
269        } else {
270            None
271        }
272    }
273
274    #[inline]
275    fn size_hint(&self) -> (usize, Option<usize>) {
276        (self.len(), Some(self.len()))
277    }
278}
279
280impl<E: EntryMarker> ExactSizeIterator for CompletionQueue<'_, E> {
281    #[inline]
282    fn len(&self) -> usize {
283        self.tail.wrapping_sub(self.head) as usize
284    }
285}
286
287impl Entry {
288    /// The operation-specific result code. For example, for a [`Read`](crate::opcode::Read)
289    /// operation this is equivalent to the return value of the `read(2)` system call.
290    #[inline]
291    pub fn result(&self) -> i32 {
292        self.0.res
293    }
294
295    /// The user data of the request, as set by
296    /// [`Entry::user_data`](crate::squeue::Entry::user_data) on the submission queue event.
297    #[inline]
298    pub fn user_data(&self) -> u64 {
299        self.0.user_data
300    }
301
302    /// Replace the application-supplied user data.
303    #[inline]
304    pub fn set_user_data(&mut self, user_data: u64) {
305        self.0.user_data = user_data;
306    }
307
308    /// Metadata related to the operation.
309    ///
310    /// This is currently used for:
311    /// - Storing the selected buffer ID, if one was selected. See
312    ///   [`BUFFER_SELECT`](crate::squeue::Flags::BUFFER_SELECT) for more info.
313    #[inline]
314    pub fn flags(&self) -> u32 {
315        self.0.flags
316    }
317}
318
319impl private::Sealed for Entry {}
320
321impl EntryMarker for Entry {
322    const BUILD_FLAGS: u32 = 0;
323
324    #[inline]
325    fn user_data(&self) -> u64 {
326        Entry::user_data(self)
327    }
328
329    #[inline]
330    fn set_user_data(&mut self, user_data: u64) {
331        Entry::set_user_data(self, user_data);
332    }
333}
334
335impl Clone for Entry {
336    fn clone(&self) -> Entry {
337        // io_uring_cqe doesn't implement Clone due to the 'big_cqe' incomplete array field.
338        Entry(unsafe { mem::transmute_copy(&self.0) })
339    }
340}
341
342impl Debug for Entry {
343    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
344        f.debug_struct("Entry")
345            .field("result", &self.result())
346            .field("user_data", &self.user_data())
347            .field("flags", &self.flags())
348            .finish()
349    }
350}
351
352impl Entry32 {
353    /// The operation-specific result code. For example, for a [`Read`](crate::opcode::Read)
354    /// operation this is equivalent to the return value of the `read(2)` system call.
355    #[inline]
356    pub fn result(&self) -> i32 {
357        self.0 .0.res
358    }
359
360    /// The user data of the request, as set by
361    /// [`Entry::user_data`](crate::squeue::Entry::user_data) on the submission queue event.
362    #[inline]
363    pub fn user_data(&self) -> u64 {
364        self.0 .0.user_data
365    }
366
367    /// Replace the application-supplied user data.
368    #[inline]
369    pub fn set_user_data(&mut self, user_data: u64) {
370        self.0 .0.user_data = user_data;
371    }
372
373    /// Metadata related to the operation.
374    ///
375    /// This is currently used for:
376    /// - Storing the selected buffer ID, if one was selected. See
377    ///   [`BUFFER_SELECT`](crate::squeue::Flags::BUFFER_SELECT) for more info.
378    #[inline]
379    pub fn flags(&self) -> u32 {
380        self.0 .0.flags
381    }
382
383    /// Additional data available in 32-byte completion queue entries (CQEs).
384    #[inline]
385    pub fn big_cqe(&self) -> &[u64; 2] {
386        &self.1
387    }
388}
389
390impl private::Sealed for Entry32 {}
391
392impl EntryMarker for Entry32 {
393    const BUILD_FLAGS: u32 = sys::IORING_SETUP_CQE32;
394
395    #[inline]
396    fn user_data(&self) -> u64 {
397        Entry32::user_data(self)
398    }
399
400    #[inline]
401    fn set_user_data(&mut self, user_data: u64) {
402        Entry32::set_user_data(self, user_data);
403    }
404}
405
406impl From<Entry32> for Entry {
407    fn from(entry32: Entry32) -> Self {
408        entry32.0
409    }
410}
411
412impl Debug for Entry32 {
413    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
414        f.debug_struct("Entry32")
415            .field("result", &self.result())
416            .field("user_data", &self.user_data())
417            .field("flags", &self.flags())
418            .field("big_cqe", &self.big_cqe())
419            .finish()
420    }
421}
422
423/// Return whether the buffer will be reused by future CQE completions
424///
425/// This corresponds to the `IORING_CQE_BUF_MORE` flag, and it signals to
426/// the consumer that it should expect further completions involging the
427/// related buffer ID when the registered buffer ring was setup with
428/// the `IOU_PBUF_RING_INC` flag.
429pub fn buffer_more(flags: u32) -> bool {
430    flags & sys::IORING_CQE_F_BUF_MORE != 0
431}
432
433/// Return which dynamic buffer was used by this operation.
434///
435/// This corresponds to the `IORING_CQE_F_BUFFER` flag (and related bit-shifting),
436/// and it signals to the consumer which provided contains the result of this
437/// operation.
438pub fn buffer_select(flags: u32) -> Option<u16> {
439    if flags & sys::IORING_CQE_F_BUFFER != 0 {
440        let id = flags >> sys::IORING_CQE_BUFFER_SHIFT;
441
442        // FIXME
443        //
444        // Should we return u16? maybe kernel will change value of `IORING_CQE_BUFFER_SHIFT` in future.
445        Some(id as u16)
446    } else {
447        None
448    }
449}
450
451/// Return whether further completion events will be submitted for
452/// this same operation.
453///
454/// This corresponds to the `IORING_CQE_F_MORE` flag, and it signals to
455/// the consumer that it should expect further CQE entries after this one,
456/// still from the same original SQE request (e.g. for multishot operations).
457pub fn more(flags: u32) -> bool {
458    flags & sys::IORING_CQE_F_MORE != 0
459}
460
461/// Return whether socket has more data ready to read.
462///
463/// This corresponds to the `IORING_CQE_F_SOCK_NONEMPTY` flag, and it signals to
464/// the consumer that the socket has more data that can be read immediately.
465///
466/// The io_uring documentation says recv, recv-multishot, recvmsg, and recvmsg-multishot
467/// can provide this bit in their respective CQE.
468pub fn sock_nonempty(flags: u32) -> bool {
469    flags & sys::IORING_CQE_F_SOCK_NONEMPTY != 0
470}
471
472/// Returns whether this completion event is a notification.
473///
474/// This corresponds to the `IORING_CQE_F_NOTIF` flag,
475/// currently used by the [SendZc](crate::opcode::SendZc) operation.
476pub fn notif(flags: u32) -> bool {
477    flags & sys::IORING_CQE_F_NOTIF != 0
478}