Skip to main content

ntex_io_uring/
lib.rs

1//! The `io_uring` library for Rust.
2//!
3//! The crate only provides a summary of the parameters.
4//! For more detailed documentation, see manpage.
5//!
6//! ## Supported Architectures
7//!
8//! ### Default Support
9//!
10//! The following architectures are supported by default with prebuilt bindings:
11//!
12//! * `x86_64`
13//! * `aarch64`
14//! * `riscv64`
15//! * `loongarch64`
16//! * `powerpc64`
17//!
18//! ### Custom Bindings for Unsupported Architectures
19//!
20//! If you need to build for a target architecture that is not supported by default, you have two options:
21//!
22//! **Option 1: Use the `bindgen` feature**
23//!
24//! ```toml
25//! [dependencies]
26//! io-uring = { version = "0.7", features = ["bindgen"] }
27//! ```
28//!
29//! This will generate bindings at build time for your specific architecture.
30//!
31//! **Option 2: Use your own bindings with `io_uring_use_own_sys`**
32//!
33//! If you have custom bindings that you want to use:
34//!
35//! 1. Generate or obtain the appropriate `sys.rs` bindings for your target architecture
36//! 2. Set the `IO_URING_OWN_SYS_BINDING` environment variable to point to your binding file
37//! 3. Build with the `io_uring_use_own_sys` cfg flag:
38//!
39//! ```bash
40//! export IO_URING_OWN_SYS_BINDING=/path/to/your/custom/sys.rs
41//! cargo build --cfg io_uring_use_own_sys
42//! ```
43//!
44//! This approach allows you to provide your own bindings without relying on bindgen or the prebuilt bindings.
45
46#[macro_use]
47mod util;
48pub mod cqueue;
49pub mod opcode;
50pub mod opcode2;
51pub mod register;
52pub mod squeue;
53mod submit;
54mod sys;
55pub mod types;
56
57use std::marker::PhantomData;
58use std::mem::ManuallyDrop;
59use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
60use std::{cmp, io, mem};
61
62#[cfg(feature = "io_safety")]
63use std::os::unix::io::{AsFd, BorrowedFd};
64
65pub use cqueue::CompletionQueue;
66pub use register::Probe;
67pub use squeue::SubmissionQueue;
68pub use submit::EnterFlags;
69pub use submit::Submitter;
70use util::{Mmap, OwnedFd};
71
72/// IoUring instance
73///
74/// - `S`: The ring's submission queue entry (SQE) type, either [`squeue::Entry`] or
75///   [`squeue::Entry128`];
76/// - `C`: The ring's completion queue entry (CQE) type, either [`cqueue::Entry`] or
77///   [`cqueue::Entry32`].
78pub struct IoUring<S = squeue::Entry, C = cqueue::Entry>
79where
80    S: squeue::EntryMarker,
81    C: cqueue::EntryMarker,
82{
83    sq: squeue::Inner<S>,
84    cq: cqueue::Inner<C>,
85    fd: OwnedFd,
86    params: Parameters,
87    memory: ManuallyDrop<MemoryMap>,
88}
89
90#[allow(dead_code)]
91struct MemoryMap {
92    sq_mmap: Mmap,
93    sqe_mmap: Mmap,
94    cq_mmap: Option<Mmap>,
95}
96
97/// IoUring build params
98#[derive(Clone, Default)]
99pub struct Builder<S = squeue::Entry, C = cqueue::Entry>
100where
101    S: squeue::EntryMarker,
102    C: cqueue::EntryMarker,
103{
104    dontfork: bool,
105    params: sys::io_uring_params,
106    phantom: PhantomData<(S, C)>,
107}
108
109/// The parameters that were used to construct an [`IoUring`].
110///
111/// This type is a transparent wrapper over the system structure `io_uring_params`. A value can be
112/// (unsafely) created from any properly laid-out and initialized memory representation.
113#[derive(Clone)]
114#[repr(transparent)]
115pub struct Parameters(sys::io_uring_params);
116
117unsafe impl<S: squeue::EntryMarker, C: cqueue::EntryMarker> Send for IoUring<S, C> {}
118unsafe impl<S: squeue::EntryMarker, C: cqueue::EntryMarker> Sync for IoUring<S, C> {}
119
120impl IoUring<squeue::Entry, cqueue::Entry> {
121    /// Create a new `IoUring` instance with default configuration parameters. See [`Builder`] to
122    /// customize it further.
123    ///
124    /// The `entries` sets the size of queue,
125    /// and its value should be the power of two.
126    pub fn new(entries: u32) -> io::Result<Self> {
127        Self::builder().build(entries)
128    }
129
130    /// Create an `IoUring` instance from a pre-opened file descriptor.
131    ///
132    /// # Safety
133    ///
134    /// The caller must uphold that the file descriptor is owned and refers to a uring. The
135    /// `params` argument must be equivalent to the those previously filled in by the kernel when
136    /// the provided ring was created.
137    pub unsafe fn from_fd(fd: RawFd, params: Parameters) -> io::Result<Self> {
138        Self::with_fd_and_params(OwnedFd::from_raw_fd(fd), params.0)
139    }
140}
141
142impl<S: squeue::EntryMarker, C: cqueue::EntryMarker> IoUring<S, C> {
143    /// Create a [`Builder`] for an `IoUring` instance.
144    ///
145    /// This allows for further customization than [`new`](Self::new).
146    ///
147    /// Unlike [`IoUring::new`], this function is available for any combination of submission
148    /// queue entry (SQE) and completion queue entry (CQE) types.
149    #[must_use]
150    pub fn builder() -> Builder<S, C> {
151        Builder {
152            dontfork: false,
153            params: sys::io_uring_params {
154                flags: S::BUILD_FLAGS | C::BUILD_FLAGS,
155                ..Default::default()
156            },
157            phantom: PhantomData,
158        }
159    }
160
161    fn with_params(entries: u32, mut p: sys::io_uring_params) -> io::Result<Self> {
162        let fd: OwnedFd = unsafe { OwnedFd::from_raw_fd(sys::io_uring_setup(entries, &mut p)?) };
163        unsafe { Self::with_fd_and_params(fd, p) }
164    }
165
166    unsafe fn with_fd_and_params(fd: OwnedFd, p: sys::io_uring_params) -> io::Result<Self> {
167        // NOTE: The `SubmissionQueue` and `CompletionQueue` are references,
168        // and their lifetime can never exceed `MemoryMap`.
169        //
170        // The memory mapped regions of `MemoryMap` never move,
171        // so `SubmissionQueue` and `CompletionQueue` are `Unpin`.
172        //
173        // I really hope that Rust can safely use self-reference types.
174        #[inline]
175        unsafe fn setup_queue<S: squeue::EntryMarker, C: cqueue::EntryMarker>(
176            fd: &OwnedFd,
177            p: &sys::io_uring_params,
178        ) -> io::Result<(MemoryMap, squeue::Inner<S>, cqueue::Inner<C>)> {
179            let sq_len = p.sq_off.array as usize + p.sq_entries as usize * mem::size_of::<u32>();
180            let cq_len = p.cq_off.cqes as usize + p.cq_entries as usize * mem::size_of::<C>();
181            let sqe_len = p.sq_entries as usize * mem::size_of::<S>();
182            let sqe_mmap = Mmap::new(fd, sys::IORING_OFF_SQES as _, sqe_len)?;
183
184            if p.features & sys::IORING_FEAT_SINGLE_MMAP != 0 {
185                let scq_mmap =
186                    Mmap::new(fd, sys::IORING_OFF_SQ_RING as _, cmp::max(sq_len, cq_len))?;
187
188                let sq = squeue::Inner::new(&scq_mmap, &sqe_mmap, p);
189                let cq = cqueue::Inner::new(&scq_mmap, p);
190                let mm = MemoryMap {
191                    sq_mmap: scq_mmap,
192                    cq_mmap: None,
193                    sqe_mmap,
194                };
195
196                Ok((mm, sq, cq))
197            } else {
198                let sq_mmap = Mmap::new(fd, sys::IORING_OFF_SQ_RING as _, sq_len)?;
199                let cq_mmap = Mmap::new(fd, sys::IORING_OFF_CQ_RING as _, cq_len)?;
200
201                let sq = squeue::Inner::new(&sq_mmap, &sqe_mmap, p);
202                let cq = cqueue::Inner::new(&cq_mmap, p);
203                let mm = MemoryMap {
204                    cq_mmap: Some(cq_mmap),
205                    sq_mmap,
206                    sqe_mmap,
207                };
208
209                Ok((mm, sq, cq))
210            }
211        }
212
213        let (mm, sq, cq) = unsafe { setup_queue(&fd, &p)? };
214
215        Ok(IoUring {
216            sq,
217            cq,
218            fd,
219            params: Parameters(p),
220            memory: ManuallyDrop::new(mm),
221        })
222    }
223
224    /// Get the submitter of this io_uring instance, which can be used to submit submission queue
225    /// events to the kernel for execution and to register files or buffers with it.
226    #[inline]
227    pub fn submitter(&self) -> Submitter<'_> {
228        Submitter::new(
229            &self.fd,
230            &self.params,
231            self.sq.head,
232            self.sq.tail,
233            self.sq.flags,
234        )
235    }
236
237    /// Get the parameters that were used to construct this instance.
238    #[inline]
239    pub fn params(&self) -> &Parameters {
240        &self.params
241    }
242
243    /// Initiate asynchronous I/O. See [`Submitter::submit`] for more details.
244    #[inline]
245    pub fn submit(&self) -> io::Result<usize> {
246        self.submitter().submit()
247    }
248
249    /// Initiate and/or complete asynchronous I/O. See [`Submitter::submit_and_wait`] for more
250    /// details.
251    #[inline]
252    pub fn submit_and_wait(&self, want: usize) -> io::Result<usize> {
253        self.submitter().submit_and_wait(want)
254    }
255
256    /// Get the submitter, submission queue and completion queue of the io_uring instance. This can
257    /// be used to operate on the different parts of the io_uring instance independently.
258    ///
259    /// If you use this method to obtain `sq` and `cq`,
260    /// please note that you need to `drop` or `sync` the queue before and after submit,
261    /// otherwise the queue will not be updated.
262    #[inline]
263    pub fn split(
264        &mut self,
265    ) -> (
266        Submitter<'_>,
267        SubmissionQueue<'_, S>,
268        CompletionQueue<'_, C>,
269    ) {
270        let submit = Submitter::new(
271            &self.fd,
272            &self.params,
273            self.sq.head,
274            self.sq.tail,
275            self.sq.flags,
276        );
277        (submit, self.sq.borrow(), self.cq.borrow())
278    }
279
280    /// Get the submission queue of the io_uring instance. This is used to send I/O requests to the
281    /// kernel.
282    #[inline]
283    pub fn submission(&self) -> SubmissionQueue<'_, S> {
284        self.sq.borrow()
285    }
286
287    #[deprecated]
288    /// Get the submission queue of the io_uring instance from a shared reference.
289    ///
290    /// # Safety
291    ///
292    /// No other [`SubmissionQueue`]s may exist when calling this function.
293    #[inline]
294    pub fn submission_shared(&self) -> SubmissionQueue<'_, S> {
295        self.sq.borrow()
296    }
297
298    /// Get completion queue of the io_uring instance. This is used to receive I/O completion
299    /// events from the kernel.
300    #[inline]
301    pub fn completion(&mut self) -> CompletionQueue<'_, C> {
302        self.cq.borrow()
303    }
304
305    /// Get the completion queue of the io_uring instance from a shared reference.
306    ///
307    /// # Safety
308    ///
309    /// No other [`CompletionQueue`]s may exist when calling this function.
310    #[inline]
311    pub unsafe fn completion_shared(&self) -> CompletionQueue<'_, C> {
312        self.cq.borrow_shared()
313    }
314}
315
316impl<S: squeue::EntryMarker, C: cqueue::EntryMarker> Drop for IoUring<S, C> {
317    fn drop(&mut self) {
318        // Ensure that `MemoryMap` is released before `fd`.
319        unsafe {
320            ManuallyDrop::drop(&mut self.memory);
321        }
322    }
323}
324
325impl<S: squeue::EntryMarker, C: cqueue::EntryMarker> Builder<S, C> {
326    /// Do not make this io_uring instance accessible by child processes after a fork.
327    pub fn dontfork(&mut self) -> &mut Self {
328        self.dontfork = true;
329        self
330    }
331
332    /// Perform busy-waiting for I/O completion events, as opposed to getting notifications via an
333    /// asynchronous IRQ (Interrupt Request). This will reduce latency, but increases CPU usage.
334    ///
335    /// This is only usable on file systems that support polling and files opened with `O_DIRECT`.
336    pub fn setup_iopoll(&mut self) -> &mut Self {
337        self.params.flags |= sys::IORING_SETUP_IOPOLL;
338        self
339    }
340
341    /// Use a kernel thread to perform submission queue polling. This allows your application to
342    /// issue I/O without ever context switching into the kernel, however it does use up a lot more
343    /// CPU. You should use it when you are expecting very large amounts of I/O.
344    ///
345    /// After `idle` milliseconds, the kernel thread will go to sleep and you will have to wake it up
346    /// again with a system call (this is handled by [`Submitter::submit`] and
347    /// [`Submitter::submit_and_wait`] automatically).
348    ///
349    /// Before version 5.11 of the Linux kernel, to successfully use this feature, the application
350    /// must register a set of files to be used for IO through io_uring_register(2) using the
351    /// IORING_REGISTER_FILES opcode. Failure to do so will result in submitted IO being errored
352    /// with EBADF. The presence of this feature can be detected by the IORING_FEAT_SQPOLL_NONFIXED
353    /// feature flag. In version 5.11 and later, it is no longer necessary to register files to use
354    /// this feature. 5.11 also allows using this as non-root, if the user has the CAP_SYS_NICE
355    /// capability. In 5.13 this requirement was also relaxed, and no special privileges are needed
356    /// for SQPOLL in newer kernels. Certain stable kernels older than 5.13 may also support
357    /// unprivileged SQPOLL.
358    pub fn setup_sqpoll(&mut self, idle: u32) -> &mut Self {
359        self.params.flags |= sys::IORING_SETUP_SQPOLL;
360        self.params.sq_thread_idle = idle;
361        self
362    }
363
364    /// Bind the kernel's poll thread to the specified cpu. This flag is only meaningful when
365    /// [`Builder::setup_sqpoll`] is enabled.
366    pub fn setup_sqpoll_cpu(&mut self, cpu: u32) -> &mut Self {
367        self.params.flags |= sys::IORING_SETUP_SQ_AFF;
368        self.params.sq_thread_cpu = cpu;
369        self
370    }
371
372    /// Create the completion queue with the specified number of entries. The value must be greater
373    /// than `entries`, and may be rounded up to the next power-of-two.
374    pub fn setup_cqsize(&mut self, entries: u32) -> &mut Self {
375        self.params.flags |= sys::IORING_SETUP_CQSIZE;
376        self.params.cq_entries = entries;
377        self
378    }
379
380    /// Clamp the sizes of the submission queue and completion queue at their maximum values instead
381    /// of returning an error when you attempt to resize them beyond their maximum values.
382    pub fn setup_clamp(&mut self) -> &mut Self {
383        self.params.flags |= sys::IORING_SETUP_CLAMP;
384        self
385    }
386
387    /// Share the asynchronous worker thread backend of this io_uring with the specified io_uring
388    /// file descriptor instead of creating a new thread pool.
389    pub fn setup_attach_wq(&mut self, fd: RawFd) -> &mut Self {
390        self.params.flags |= sys::IORING_SETUP_ATTACH_WQ;
391        self.params.wq_fd = fd as _;
392        self
393    }
394
395    /// Start the io_uring instance with all its rings disabled. This allows you to register
396    /// restrictions, buffers and files before the kernel starts processing submission queue
397    /// events. You are only able to [register restrictions](Submitter::register_restrictions) when
398    /// the rings are disabled due to concurrency issues. You can enable the rings with
399    /// [`Submitter::register_enable_rings`]. Available since 5.10.
400    pub fn setup_r_disabled(&mut self) -> &mut Self {
401        self.params.flags |= sys::IORING_SETUP_R_DISABLED;
402        self
403    }
404
405    /// Normally io_uring stops submitting a batch of request, if one of these requests results in
406    /// an error. This can cause submission of less than what is expected, if a request ends in
407    /// error while being submitted. If the ring is created with this flag, io_uring_enter(2) will
408    /// continue submitting requests even if it encounters an error submitting a request. CQEs are
409    /// still posted for errored request regardless of whether or not this flag is set at ring
410    /// creation time, the only difference is if the submit sequence is halted or continued when an
411    /// error is observed. Available since 5.18.
412    pub fn setup_submit_all(&mut self) -> &mut Self {
413        self.params.flags |= sys::IORING_SETUP_SUBMIT_ALL;
414        self
415    }
416
417    /// By default, io_uring will interrupt a task running in userspace when a completion event
418    /// comes in. This is to ensure that completions run in a timely manner. For a lot of use
419    /// cases, this is overkill and can cause reduced performance from both the inter-processor
420    /// interrupt used to do this, the kernel/user transition, the needless interruption of the
421    /// tasks userspace activities, and reduced batching if completions come in at a rapid rate.
422    /// Most applications don't need the forceful interruption, as the events are processed at any
423    /// kernel/user transition. The exception are setups where the application uses multiple
424    /// threads operating on the same ring, where the application waiting on completions isn't the
425    /// one that submitted them. For most other use cases, setting this flag will improve
426    /// performance. Available since 5.19.
427    pub fn setup_coop_taskrun(&mut self) -> &mut Self {
428        self.params.flags |= sys::IORING_SETUP_COOP_TASKRUN;
429        self
430    }
431
432    /// Used in conjunction with IORING_SETUP_COOP_TASKRUN, this provides a flag,
433    /// IORING_SQ_TASKRUN, which is set in the SQ ring flags whenever completions are pending that
434    /// should be processed. As an example, liburing will check for this flag even when doing
435    /// io_uring_peek_cqe(3) and enter the kernel to process them, and applications can do the
436    /// same. This makes IORING_SETUP_TASKRUN_FLAG safe to use even when applications rely on a
437    /// peek style operation on the CQ ring to see if anything might be pending to reap. Available
438    /// since 5.19.
439    pub fn setup_taskrun_flag(&mut self) -> &mut Self {
440        self.params.flags |= sys::IORING_SETUP_TASKRUN_FLAG;
441        self
442    }
443
444    /// By default, io_uring will process all outstanding work at the end of any system call or
445    /// thread interrupt. This can delay the application from making other progress. Setting this
446    /// flag will hint to io_uring that it should defer work until an io_uring_enter(2) call with
447    /// the IORING_ENTER_GETEVENTS flag set. This allows the application to request work to run
448    /// just just before it wants to process completions. This flag requires the
449    /// IORING_SETUP_SINGLE_ISSUER flag to be set, and also enforces that the call to
450    /// io_uring_enter(2) is called from the same thread that submitted requests. Note that if this
451    /// flag is set then it is the application's responsibility to periodically trigger work (for
452    /// example via any of the CQE waiting functions) or else completions may not be delivered.
453    /// Available since 6.1.
454    pub fn setup_defer_taskrun(&mut self) -> &mut Self {
455        self.params.flags |= sys::IORING_SETUP_DEFER_TASKRUN;
456        self
457    }
458
459    /// Hint the kernel that a single task will submit requests. Used for optimizations. This is
460    /// enforced by the kernel, and request that don't respect that will fail with -EEXIST.
461    /// If [`Builder::setup_sqpoll`] is enabled, the polling task is doing the submissions and multiple
462    /// userspace tasks can call [`Submitter::enter`] and higher level APIs. Available since 6.0.
463    pub fn setup_single_issuer(&mut self) -> &mut Self {
464        self.params.flags |= sys::IORING_SETUP_SINGLE_ISSUER;
465        self
466    }
467
468    /// Build an [IoUring], with the specified number of entries in the submission queue and
469    /// completion queue unless [`setup_cqsize`](Self::setup_cqsize) has been called.
470    pub fn build(&self, entries: u32) -> io::Result<IoUring<S, C>> {
471        let ring = IoUring::with_params(entries, self.params)?;
472
473        if self.dontfork {
474            ring.memory.sq_mmap.dontfork()?;
475            ring.memory.sqe_mmap.dontfork()?;
476            if let Some(cq_mmap) = ring.memory.cq_mmap.as_ref() {
477                cq_mmap.dontfork()?;
478            }
479        }
480
481        Ok(ring)
482    }
483}
484
485impl Parameters {
486    /// Whether a kernel thread is performing queue polling. Enabled with [`Builder::setup_sqpoll`].
487    pub fn is_setup_sqpoll(&self) -> bool {
488        self.0.flags & sys::IORING_SETUP_SQPOLL != 0
489    }
490
491    /// Whether waiting for completion events is done with a busy loop instead of using IRQs.
492    /// Enabled with [`Builder::setup_iopoll`].
493    pub fn is_setup_iopoll(&self) -> bool {
494        self.0.flags & sys::IORING_SETUP_IOPOLL != 0
495    }
496
497    /// Whether the single issuer hint is enabled. Enabled with [`Builder::setup_single_issuer`].
498    pub fn is_setup_single_issuer(&self) -> bool {
499        self.0.flags & sys::IORING_SETUP_SINGLE_ISSUER != 0
500    }
501
502    /// If this flag is set, the SQ and CQ rings were mapped with a single `mmap(2)` call. This
503    /// means that only two syscalls were used instead of three.
504    pub fn is_feature_single_mmap(&self) -> bool {
505        self.0.features & sys::IORING_FEAT_SINGLE_MMAP != 0
506    }
507
508    /// If this flag is set, io_uring supports never dropping completion events. If a completion
509    /// event occurs and the CQ ring is full, the kernel stores the event internally until such a
510    /// time that the CQ ring has room for more entries.
511    pub fn is_feature_nodrop(&self) -> bool {
512        self.0.features & sys::IORING_FEAT_NODROP != 0
513    }
514
515    /// If this flag is set, applications can be certain that any data for async offload has been
516    /// consumed when the kernel has consumed the SQE.
517    pub fn is_feature_submit_stable(&self) -> bool {
518        self.0.features & sys::IORING_FEAT_SUBMIT_STABLE != 0
519    }
520
521    /// If this flag is set, applications can specify offset == -1 with [`Readv`](opcode::Readv),
522    /// [`Writev`](opcode::Writev), [`ReadFixed`](opcode::ReadFixed),
523    /// [`WriteFixed`](opcode::WriteFixed), [`Read`](opcode::Read) and [`Write`](opcode::Write),
524    /// which behaves exactly like setting offset == -1 in `preadv2(2)` and `pwritev2(2)`: it’ll use
525    /// (and update) the current file position.
526    ///
527    /// This obviously comes with the caveat that if the application has multiple reads or writes in flight,
528    /// then the end result will not be as expected.
529    /// This is similar to threads sharing a file descriptor and doing IO using the current file position.
530    pub fn is_feature_rw_cur_pos(&self) -> bool {
531        self.0.features & sys::IORING_FEAT_RW_CUR_POS != 0
532    }
533
534    /// If this flag is set, then io_uring guarantees that both sync and async execution of
535    /// a request assumes the credentials of the task that called [`Submitter::enter`] to queue the requests.
536    /// If this flag isn’t set, then requests are issued with the credentials of the task that originally registered the io_uring.
537    /// If only one task is using a ring, then this flag doesn’t matter as the credentials will always be the same.
538    ///
539    /// Note that this is the default behavior, tasks can still register different personalities
540    /// through [`Submitter::register_personality`].
541    pub fn is_feature_cur_personality(&self) -> bool {
542        self.0.features & sys::IORING_FEAT_CUR_PERSONALITY != 0
543    }
544
545    /// Whether async pollable I/O is fast.
546    ///
547    /// See [the commit message that introduced
548    /// it](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=d7718a9d25a61442da8ee8aeeff6a0097f0ccfd6)
549    /// for more details.
550    ///
551    /// If this flag is set, then io_uring supports using an internal poll mechanism to drive
552    /// data/space readiness. This means that requests that cannot read or write data to a file no
553    /// longer need to be punted to an async thread for handling, instead they will begin operation
554    /// when the file is ready. This is similar to doing poll + read/write in userspace, but
555    /// eliminates the need to do so. If this flag is set, requests waiting on space/data consume a
556    /// lot less resources doing so as they are not blocking a thread. Available since kernel 5.7.
557    pub fn is_feature_fast_poll(&self) -> bool {
558        self.0.features & sys::IORING_FEAT_FAST_POLL != 0
559    }
560
561    /// Whether poll events are stored using 32 bits instead of 16. This allows the user to use
562    /// `EPOLLEXCLUSIVE`.
563    ///
564    /// If this flag is set, the IORING_OP_POLL_ADD command accepts the full 32-bit range of epoll
565    /// based flags. Most notably EPOLLEXCLUSIVE which allows exclusive (waking single waiters)
566    /// behavior. Available since kernel 5.9.
567    pub fn is_feature_poll_32bits(&self) -> bool {
568        self.0.features & sys::IORING_FEAT_POLL_32BITS != 0
569    }
570
571    /// If this flag is set, the IORING_SETUP_SQPOLL feature no longer requires the use of fixed
572    /// files. Any normal file descriptor can be used for IO commands without needing registration.
573    /// Available since kernel 5.11.
574    pub fn is_feature_sqpoll_nonfixed(&self) -> bool {
575        self.0.features & sys::IORING_FEAT_SQPOLL_NONFIXED != 0
576    }
577
578    /// If this flag is set, then the io_uring_enter(2) system call supports passing in an extended
579    /// argument instead of just the sigset_t of earlier kernels. This extended argument is of type
580    /// struct io_uring_getevents_arg and allows the caller to pass in both a sigset_t and a
581    /// timeout argument for waiting on events. The struct layout is as follows:
582    ///
583    /// // struct io_uring_getevents_arg {
584    /// //     __u64 sigmask;
585    /// //     __u32 sigmask_sz;
586    /// //     __u32 pad;
587    /// //     __u64 ts;
588    /// // };
589    ///
590    /// and a pointer to this struct must be passed in if IORING_ENTER_EXT_ARG is set in the flags
591    /// for the enter system call. Available since kernel 5.11.
592    pub fn is_feature_ext_arg(&self) -> bool {
593        self.0.features & sys::IORING_FEAT_EXT_ARG != 0
594    }
595
596    /// If this flag is set, io_uring is using native workers for its async helpers. Previous
597    /// kernels used kernel threads that assumed the identity of the original io_uring owning task,
598    /// but later kernels will actively create what looks more like regular process threads
599    /// instead. Available since kernel 5.12.
600    pub fn is_feature_native_workers(&self) -> bool {
601        self.0.features & sys::IORING_FEAT_NATIVE_WORKERS != 0
602    }
603
604    /// Whether the kernel supports tagging resources.
605    ///
606    /// If this flag is set, then io_uring supports a variety of features related to fixed files
607    /// and buffers. In particular, it indicates that registered buffers can be updated in-place,
608    /// whereas before the full set would have to be unregistered first. Available since kernel
609    /// 5.13.
610    pub fn is_feature_resource_tagging(&self) -> bool {
611        self.0.features & sys::IORING_FEAT_RSRC_TAGS != 0
612    }
613
614    /// Whether the kernel supports `IOSQE_CQE_SKIP_SUCCESS`.
615    ///
616    /// This feature allows skipping the generation of a CQE if a SQE executes normally. Available
617    /// since kernel 5.17.
618    pub fn is_feature_skip_cqe_on_success(&self) -> bool {
619        self.0.features & sys::IORING_FEAT_CQE_SKIP != 0
620    }
621
622    /// Whether the kernel supports deferred file assignment.
623    ///
624    /// If this flag is set, then io_uring supports sane assignment of files for SQEs that have
625    /// dependencies. For example, if a chain of SQEs are submitted with IOSQE_IO_LINK, then
626    /// kernels without this flag will prepare the file for each link upfront. If a previous link
627    /// opens a file with a known index, eg if direct descriptors are used with open or accept,
628    /// then file assignment needs to happen post execution of that SQE. If this flag is set, then
629    /// the kernel will defer file assignment until execution of a given request is started.
630    /// Available since kernel 5.17.
631    pub fn is_feature_linked_file(&self) -> bool {
632        self.0.features & sys::IORING_FEAT_LINKED_FILE != 0
633    }
634
635    /// Whether the kernel supports `IORING_RECVSEND_BUNDLE`.
636    ///
637    /// This feature allows sending and recieving multiple buffers as a single bundle. Available
638    /// since kernel 6.10.
639    pub fn is_feature_recvsend_bundle(&self) -> bool {
640        self.0.features & sys::IORING_FEAT_RECVSEND_BUNDLE != 0
641    }
642
643    /// If this flag is set, applications can use the
644    /// [`SubmitArgs::min_wait_usec`](types::SubmitArgs::min_wait_usec) method to specify a timeout
645    /// after which the kernel will return as soon as a single completion is received instead of
646    /// waiting for the minimum specified by the application. Available since kernel 6.12.
647    pub fn is_feature_min_timeout(&self) -> bool {
648        self.0.features & sys::IORING_FEAT_MIN_TIMEOUT != 0
649    }
650
651    /// The number of submission queue entries allocated.
652    pub fn sq_entries(&self) -> u32 {
653        self.0.sq_entries
654    }
655
656    /// The idle time of the SQ poll thread in milliseconds.
657    pub fn sq_thread_idle(&self) -> u32 {
658        self.0.sq_thread_idle
659    }
660
661    /// The number of completion queue entries allocated.
662    pub fn cq_entries(&self) -> u32 {
663        self.0.cq_entries
664    }
665}
666
667impl std::fmt::Debug for Parameters {
668    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
669        f.debug_struct("Parameters")
670            .field("is_setup_sqpoll", &self.is_setup_sqpoll())
671            .field("is_setup_iopoll", &self.is_setup_iopoll())
672            .field("is_setup_single_issuer", &self.is_setup_single_issuer())
673            .field("is_feature_single_mmap", &self.is_feature_single_mmap())
674            .field("is_feature_nodrop", &self.is_feature_nodrop())
675            .field("is_feature_submit_stable", &self.is_feature_submit_stable())
676            .field("is_feature_rw_cur_pos", &self.is_feature_rw_cur_pos())
677            .field(
678                "is_feature_cur_personality",
679                &self.is_feature_cur_personality(),
680            )
681            .field("is_feature_poll_32bits", &self.is_feature_poll_32bits())
682            .field("sq_entries", &self.0.sq_entries)
683            .field("cq_entries", &self.0.cq_entries)
684            .finish()
685    }
686}
687
688impl<S: squeue::EntryMarker, C: cqueue::EntryMarker> AsRawFd for IoUring<S, C> {
689    fn as_raw_fd(&self) -> RawFd {
690        self.fd.as_raw_fd()
691    }
692}
693
694#[cfg(feature = "io_safety")]
695impl<S: squeue::EntryMarker, C: cqueue::EntryMarker> AsFd for IoUring<S, C> {
696    fn as_fd(&self) -> BorrowedFd<'_> {
697        self.fd.as_fd()
698    }
699}