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}