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}