Skip to main content

io_uring/
submit.rs

1use std::os::unix::io::{AsRawFd, RawFd};
2use std::sync::atomic;
3use std::{io, mem, ptr};
4
5use crate::register::{execute, Probe, RegisterRing};
6use crate::sys;
7use crate::types::{CancelBuilder, CloneBuffersFlags, Napi, Timespec};
8use crate::util::{cast_ptr, OwnedFd};
9use crate::Parameters;
10use bitflags::bitflags;
11
12use crate::register::Restriction;
13
14use crate::types;
15
16bitflags!(
17    /// See man page for complete description:
18    /// https://man7.org/linux/man-pages/man2/io_uring_enter.2.html
19    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
20    pub struct EnterFlags: u32 {
21        /// Wait for at least `min_complete` events to complete.
22        const GETEVENTS = sys::IORING_ENTER_GETEVENTS;
23
24        /// If the kernel thread is sleeping, wake it up.
25        const SQ_WAKEUP = sys::IORING_ENTER_SQ_WAKEUP;
26
27        /// Wait for at least one submission queue entry to be available.
28        const SQ_WAIT = sys::IORING_ENTER_SQ_WAIT;
29
30        /// Use the extended argument structure.
31        const EXT_ARG = sys::IORING_ENTER_EXT_ARG;
32
33        /// Submit using registered submission queue ring.
34        const REGISTERED_RING = sys::IORING_ENTER_REGISTERED_RING;
35
36        /// Timeout argument interpreted as absolute time.
37        const ABS_TIMER = sys::IORING_ENTER_ABS_TIMER;
38
39        /// Arg is offset into an area of wait regions previously registered.
40        const EXT_ARG_REG = sys::IORING_ENTER_EXT_ARG_REG;
41
42        /// Don't mark waiting task as being in iowait in certain cases.
43        const NO_IOWAIT = sys::IORING_ENTER_NO_IOWAIT;
44    }
45);
46
47/// Interface for submitting submission queue events in an io_uring instance to the kernel for
48/// executing and registering files or buffers with the instance.
49///
50/// io_uring supports both directly performing I/O on buffers and file descriptors and registering
51/// them beforehand. Registering is slow, but it makes performing the actual I/O much faster.
52pub struct Submitter<'a> {
53    fd: &'a OwnedFd,
54    params: &'a Parameters,
55    enter_ring_fd: i32,
56
57    sq_head: *const atomic::AtomicU32,
58    sq_tail: *const atomic::AtomicU32,
59    sq_flags: *const atomic::AtomicU32,
60}
61
62impl<'a> Submitter<'a> {
63    #[inline]
64    pub(crate) const fn new(
65        fd: &'a OwnedFd,
66        params: &'a Parameters,
67        sq_head: *const atomic::AtomicU32,
68        sq_tail: *const atomic::AtomicU32,
69        sq_flags: *const atomic::AtomicU32,
70    ) -> Submitter<'a> {
71        Submitter {
72            fd,
73            params,
74            enter_ring_fd: -1,
75            sq_head,
76            sq_tail,
77            sq_flags,
78        }
79    }
80
81    #[inline]
82    fn sq_len(&self) -> usize {
83        unsafe {
84            let head = (*self.sq_head).load(atomic::Ordering::Acquire);
85            let tail = (*self.sq_tail).load(atomic::Ordering::Acquire);
86
87            tail.wrapping_sub(head) as usize
88        }
89    }
90
91    /// Whether the kernel thread has gone to sleep because it waited for too long without
92    /// submission queue entries.
93    #[inline]
94    fn sq_need_wakeup(&self) -> bool {
95        unsafe {
96            (*self.sq_flags).load(atomic::Ordering::Relaxed) & sys::IORING_SQ_NEED_WAKEUP != 0
97        }
98    }
99
100    /// Load flags published by the kernel in the submission queue ring.
101    #[inline]
102    fn load_sq_flags(&self) -> u32 {
103        unsafe { (*self.sq_flags).load(atomic::Ordering::Acquire) }
104    }
105
106    #[inline]
107    fn execute_register(
108        &self,
109        opcode: libc::c_uint,
110        arg: *const libc::c_void,
111        len: libc::c_uint,
112    ) -> io::Result<i32> {
113        execute(self.register_ring(), opcode, arg, len)
114    }
115
116    #[inline]
117    fn register_ring(&self) -> RegisterRing {
118        let enter_ring_fd = self.enter_ring_fd;
119        if enter_ring_fd >= 0 && self.params.is_feature_reg_reg_ring() {
120            RegisterRing::RegisteredIndex(enter_ring_fd)
121        } else {
122            RegisterRing::RawFd(self.fd.as_raw_fd())
123        }
124    }
125
126    /// Initiate and/or complete asynchronous I/O. This is a low-level wrapper around
127    /// `io_uring_enter` - see `man io_uring_enter` (or [its online
128    /// version](https://manpages.debian.org/unstable/liburing-dev/io_uring_enter.2.en.html) for
129    /// more details.
130    ///
131    /// You will probably want to use a more high-level API such as
132    /// [`submit`](Self::submit) or [`submit_and_wait`](Self::submit_and_wait).
133    ///
134    /// # Safety
135    ///
136    /// This provides a raw interface so developer must ensure that parameters are correct.
137    pub unsafe fn enter<T: Sized>(
138        &self,
139        to_submit: u32,
140        min_complete: u32,
141        flag: u32,
142        arg: Option<&T>,
143    ) -> io::Result<usize> {
144        let arg = arg
145            .map(|arg| cast_ptr(arg).cast())
146            .unwrap_or_else(ptr::null);
147        let size = mem::size_of::<T>();
148
149        // If a ring fd has been registered with [`register_ring_fd`](Self::register_ring_fd),
150        // `enter_ring_fd` holds its index (otherwise `-1`); pass it together with
151        // `IORING_ENTER_REGISTERED_RING` instead of the raw file descriptor to avoid the per-call
152        // fd lookup in the kernel.
153        let enter_ring_fd = self.enter_ring_fd;
154        let (fd, flag) = if enter_ring_fd >= 0 {
155            (enter_ring_fd, flag | sys::IORING_ENTER_REGISTERED_RING)
156        } else {
157            (self.fd.as_raw_fd(), flag)
158        };
159
160        sys::io_uring_enter(fd, to_submit, min_complete, flag, arg, size).map(|res| res as _)
161    }
162
163    /// Submit all queued submission queue events to the kernel.
164    #[inline]
165    pub fn submit(&self) -> io::Result<usize> {
166        self.submit_and_wait(0)
167    }
168
169    /// Submit all queued submission queue events to the kernel and wait for at least `want`
170    /// completion events to complete.
171    pub fn submit_and_wait(&self, want: usize) -> io::Result<usize> {
172        let len = self.sq_len();
173        let mut flags = EnterFlags::empty();
174
175        // This logic suffers from the fact the sq_cq_overflow and sq_need_wakeup
176        // each cause an atomic load of the same variable, self.sq_flags.
177        // In the hottest paths, when a server is running with sqpoll,
178        // this is going to be hit twice, when once would be sufficient.
179        // However, consider that the `SeqCst` barrier required for interpreting
180        // the IORING_ENTER_SQ_WAKEUP bit is required in all paths where sqpoll
181        // is setup when consolidating the reads.
182
183        let sq_flags = self.load_sq_flags();
184        let sq_cq_overflow = sq_flags & sys::IORING_SQ_CQ_OVERFLOW != 0;
185        let sq_taskrun = sq_flags & sys::IORING_SQ_TASKRUN != 0;
186
187        // When IORING_FEAT_NODROP is enabled and CQ overflows, the kernel buffers
188        // completion events internally but doesn't automatically flush them when
189        // CQ space becomes available. We must explicitly call io_uring_enter()
190        // to flush these buffered events, even with SQPOLL enabled.
191        //
192        // Without this, completions remain stuck in kernel's internal buffer
193        // after draining CQ, causing missing completion notifications.
194        let need_syscall = (sq_cq_overflow && self.params.is_feature_nodrop()) || sq_taskrun;
195
196        // Deferred task work is only run by an enter carrying GETEVENTS.
197        if want > 0 || self.params.is_setup_iopoll() || sq_cq_overflow || sq_taskrun {
198            flags.insert(EnterFlags::GETEVENTS);
199        }
200
201        if self.params.is_setup_sqpoll() {
202            // See discussion in [`SubmissionQueue::need_wakeup`].
203            atomic::fence(atomic::Ordering::SeqCst);
204            if self.sq_need_wakeup() {
205                flags.insert(EnterFlags::SQ_WAKEUP);
206            } else if want == 0 && !need_syscall {
207                // The kernel thread is polling and hasn't fallen asleep, so we don't need to tell
208                // it to process events or wake it up
209
210                // However, if the CQ ring is overflown, we need to tell the kernel to process events
211                // by calling io_uring_enter with the IORING_ENTER_GETEVENTS flag.
212                return Ok(len);
213            }
214        }
215
216        unsafe { self.enter::<libc::sigset_t>(len as _, want as _, flags.bits(), None) }
217    }
218
219    /// Submit all queued submission queue events to the kernel and wait for at least `want`
220    /// completion events to complete with additional options
221    ///
222    /// You can specify a set of signals to mask and a timeout for operation, see
223    /// [`SubmitArgs`](types::SubmitArgs) for more details
224    pub fn submit_with_args(
225        &self,
226        want: usize,
227        args: &types::SubmitArgs<'_, '_>,
228    ) -> io::Result<usize> {
229        let len = self.sq_len();
230        let mut flags = EnterFlags::EXT_ARG;
231
232        let sq_flags = self.load_sq_flags();
233        let sq_cq_overflow = sq_flags & sys::IORING_SQ_CQ_OVERFLOW != 0;
234        let sq_taskrun = sq_flags & sys::IORING_SQ_TASKRUN != 0;
235        let need_syscall = (sq_cq_overflow && self.params.is_feature_nodrop()) || sq_taskrun;
236
237        // Deferred task work is only run by an enter carrying GETEVENTS.
238        if want > 0 || self.params.is_setup_iopoll() || sq_cq_overflow || sq_taskrun {
239            flags.insert(EnterFlags::GETEVENTS);
240        }
241
242        if self.params.is_setup_sqpoll() {
243            // See discussion in [`SubmissionQueue::need_wakeup`].
244            atomic::fence(atomic::Ordering::SeqCst);
245            if self.sq_need_wakeup() {
246                flags.insert(EnterFlags::SQ_WAKEUP);
247            } else if want == 0 && !need_syscall {
248                // The kernel thread is polling and hasn't fallen asleep, so we don't need to tell
249                // it to process events or wake it up
250                return Ok(len);
251            }
252        }
253
254        unsafe { self.enter(len as _, want as _, flags.bits(), Some(args)) }
255    }
256
257    /// Wait for the submission queue to have free entries.
258    pub fn squeue_wait(&self) -> io::Result<usize> {
259        unsafe { self.enter::<libc::sigset_t>(0, 0, EnterFlags::SQ_WAIT.bits(), None) }
260    }
261
262    /// Register in-memory fixed buffers for I/O with the kernel. You can use these buffers with the
263    /// [`ReadFixed`](crate::opcode::ReadFixed) and [`WriteFixed`](crate::opcode::WriteFixed)
264    /// operations.
265    ///
266    /// # Safety
267    ///
268    /// Developers must ensure that the `iov_base` and `iov_len` values are valid and will
269    /// be valid until buffers are unregistered or the ring destroyed, otherwise undefined
270    /// behaviour may occur.
271    pub unsafe fn register_buffers(&self, bufs: &[libc::iovec]) -> io::Result<()> {
272        self.execute_register(
273            sys::IORING_REGISTER_BUFFERS,
274            bufs.as_ptr().cast(),
275            bufs.len() as _,
276        )
277        .map(drop)
278    }
279
280    /// Update a range of fixed buffers starting at `offset`.
281    ///
282    /// This is required to use buffers registered using
283    /// [`register_buffers_sparse`](Self::register_buffers_sparse),
284    /// although it can be also be used with [`register_buffers`](Self::register_buffers).
285    ///
286    /// See [`register_buffers2`](Self::register_buffers2)
287    /// for more information about resource tagging.
288    ///
289    /// Available since Linux 5.13.
290    ///
291    /// # Safety
292    ///
293    /// Developers must ensure that the `iov_base` and `iov_len` values are valid and will
294    /// be valid until buffers are unregistered or the ring destroyed, otherwise undefined
295    /// behaviour may occur.
296    pub unsafe fn register_buffers_update(
297        &self,
298        offset: u32,
299        bufs: &[libc::iovec],
300        tags: Option<&[u64]>,
301    ) -> io::Result<()> {
302        let nr = tags
303            .as_ref()
304            .map_or(bufs.len(), |tags| bufs.len().min(tags.len()));
305
306        let rr = sys::io_uring_rsrc_update2 {
307            nr: nr as _,
308            data: bufs.as_ptr() as _,
309            tags: tags.map(|tags| tags.as_ptr() as _).unwrap_or(0),
310            offset,
311            ..Default::default()
312        };
313
314        self.execute_register(
315            sys::IORING_REGISTER_BUFFERS_UPDATE,
316            cast_ptr::<sys::io_uring_rsrc_update2>(&rr).cast(),
317            std::mem::size_of::<sys::io_uring_rsrc_update2>() as _,
318        )
319        .map(drop)
320    }
321
322    /// Variant of [`register_buffers`](Self::register_buffers)
323    /// with resource tagging.
324    ///
325    /// `tags` should be the same length as `bufs` and contain the
326    /// tag value corresponding to the buffer at the same index.
327    ///
328    /// If a tag is zero, then tagging for this particular resource
329    /// (a buffer in this case) is disabled. Otherwise, after the
330    /// resource had been unregistered and it's not used anymore,
331    /// a CQE will be posted with `user_data` set to the specified
332    /// tag and all other fields zeroed.
333    ///
334    /// Available since Linux 5.13.
335    ///
336    /// # Safety
337    ///
338    /// Developers must ensure that the `iov_base` and `iov_len` values are valid and will
339    /// be valid until buffers are unregistered or the ring destroyed, otherwise undefined
340    /// behaviour may occur.
341    pub unsafe fn register_buffers2(&self, bufs: &[libc::iovec], tags: &[u64]) -> io::Result<()> {
342        let rr = sys::io_uring_rsrc_register {
343            nr: bufs.len().min(tags.len()) as _,
344            data: bufs.as_ptr() as _,
345            tags: tags.as_ptr() as _,
346            ..Default::default()
347        };
348        self.execute_register(
349            sys::IORING_REGISTER_BUFFERS2,
350            cast_ptr::<sys::io_uring_rsrc_register>(&rr).cast(),
351            std::mem::size_of::<sys::io_uring_rsrc_register>() as _,
352        )
353        .map(drop)
354    }
355
356    /// Registers an empty table of nr fixed buffers buffers.
357    ///
358    /// These must be updated before use, using eg.
359    /// [`register_buffers_update`](Self::register_buffers_update).
360    ///
361    /// See [`register_buffers`](Self::register_buffers)
362    /// for more information about fixed buffers.
363    ///
364    /// Available since Linux 5.13.
365    pub fn register_buffers_sparse(&self, nr: u32) -> io::Result<()> {
366        let rr = sys::io_uring_rsrc_register {
367            nr,
368            flags: sys::IORING_RSRC_REGISTER_SPARSE,
369            ..Default::default()
370        };
371        self.execute_register(
372            sys::IORING_REGISTER_BUFFERS2,
373            cast_ptr::<sys::io_uring_rsrc_register>(&rr).cast(),
374            std::mem::size_of::<sys::io_uring_rsrc_register>() as _,
375        )
376        .map(drop)
377    }
378
379    /// Clone the entire registered buffer table from another ring into this one.
380    ///
381    /// `src_fd` is the raw file descriptor of the source `io_uring`. The source's
382    /// buffers are shared with this ring rather than copied, so a single physical
383    /// registration can back many rings without re-pinning the pages in the kernel.
384    ///
385    /// This ring's buffer table must be empty. To clone into a non-empty table or
386    /// to copy a sub-range, use
387    /// [`register_buffers_clone_offset`](Self::register_buffers_clone_offset).
388    ///
389    /// Available since Linux 6.12.
390    pub fn register_buffers_clone(&self, src_fd: RawFd) -> io::Result<()> {
391        self.register_buffers_clone_offset(src_fd, 0, 0, 0, CloneBuffersFlags::empty())
392    }
393
394    /// Clone a range of the registered buffer table from another ring into this one.
395    ///
396    /// `src_fd` is the raw file descriptor of the source `io_uring`. `nr` buffers
397    /// starting at `src_off` in the source table are installed starting at `dst_off`
398    /// in this ring's table. A `nr` of `0` clones the source's entire table.
399    ///
400    /// See [`CloneBuffersFlags`] for replacing an existing destination range or
401    /// treating `src_fd` as a registered ring descriptor.
402    ///
403    /// Available since Linux 6.12.
404    pub fn register_buffers_clone_offset(
405        &self,
406        src_fd: RawFd,
407        src_off: u32,
408        dst_off: u32,
409        nr: u32,
410        flags: CloneBuffersFlags,
411    ) -> io::Result<()> {
412        let arg = sys::io_uring_clone_buffers {
413            src_fd: src_fd as _,
414            flags: flags.bits(),
415            src_off,
416            dst_off,
417            nr,
418            ..Default::default()
419        };
420        execute(
421            RegisterRing::RawFd(self.fd.as_raw_fd()),
422            sys::IORING_REGISTER_CLONE_BUFFERS,
423            cast_ptr::<sys::io_uring_clone_buffers>(&arg).cast(),
424            // This opcode takes a single struct; the kernel requires nr_args == 1.
425            1,
426        )
427        .map(drop)
428    }
429
430    /// Registers an empty file table of nr_files number of file descriptors. The sparse variant is
431    /// available in kernels 5.19 and later.
432    ///
433    /// Registering a file table is a prerequisite for using any request that
434    /// uses direct descriptors.
435    pub fn register_files_sparse(&self, nr: u32) -> io::Result<()> {
436        let rr = sys::io_uring_rsrc_register {
437            nr,
438            flags: sys::IORING_RSRC_REGISTER_SPARSE,
439            resv2: 0,
440            data: 0,
441            tags: 0,
442        };
443        self.execute_register(
444            sys::IORING_REGISTER_FILES2,
445            cast_ptr::<sys::io_uring_rsrc_register>(&rr).cast(),
446            mem::size_of::<sys::io_uring_rsrc_register>() as _,
447        )
448        .map(drop)
449    }
450
451    /// Register files for I/O. You can use the registered files with
452    /// [`Fixed`](crate::types::Fixed).
453    ///
454    /// Each fd may be -1, in which case it is considered "sparse", and can be filled in later with
455    /// [`register_files_update`](Self::register_files_update).
456    ///
457    /// Note that this will wait for the ring to idle; it will only return once all active requests
458    /// are complete. Use [`register_files_update`](Self::register_files_update) to avoid this.
459    pub fn register_files(&self, fds: &[RawFd]) -> io::Result<()> {
460        self.execute_register(
461            sys::IORING_REGISTER_FILES,
462            fds.as_ptr().cast(),
463            fds.len() as _,
464        )
465        .map(drop)
466    }
467
468    /// This operation replaces existing files in the registered file set with new ones,
469    /// either turning a sparse entry (one where fd is equal to -1) into a real one, removing an existing entry (new one is set to -1),
470    /// or replacing an existing entry with a new existing entry. The `offset` parameter specifies
471    /// the offset into the list of registered files at which to start updating files.
472    ///
473    /// You can also perform this asynchronously with the
474    /// [`FilesUpdate`](crate::opcode::FilesUpdate) opcode.
475    pub fn register_files_update(&self, offset: u32, fds: &[RawFd]) -> io::Result<usize> {
476        let fu = sys::io_uring_files_update {
477            offset,
478            resv: 0,
479            fds: fds.as_ptr() as _,
480        };
481        let ret = self.execute_register(
482            sys::IORING_REGISTER_FILES_UPDATE,
483            cast_ptr::<sys::io_uring_files_update>(&fu).cast(),
484            fds.len() as _,
485        )?;
486        Ok(ret as _)
487    }
488
489    /// Register an eventfd created by [`eventfd`](libc::eventfd) with the io_uring instance.
490    pub fn register_eventfd(&self, eventfd: RawFd) -> io::Result<()> {
491        self.execute_register(
492            sys::IORING_REGISTER_EVENTFD,
493            cast_ptr::<RawFd>(&eventfd).cast(),
494            1,
495        )
496        .map(drop)
497    }
498
499    /// This works just like [`register_eventfd`](Self::register_eventfd), except notifications are
500    /// only posted for events that complete in an async manner, so requests that complete
501    /// immediately will not cause a notification.
502    pub fn register_eventfd_async(&self, eventfd: RawFd) -> io::Result<()> {
503        self.execute_register(
504            sys::IORING_REGISTER_EVENTFD_ASYNC,
505            cast_ptr::<RawFd>(&eventfd).cast(),
506            1,
507        )
508        .map(drop)
509    }
510
511    /// Fill in the given [`Probe`] with information about the opcodes supported by io_uring on the
512    /// running kernel.
513    ///
514    /// # Examples
515    ///
516    // This is marked no_run as it is only available from Linux 5.6+, however the latest Ubuntu (on
517    // which CI runs) only has Linux 5.4.
518    /// ```no_run
519    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
520    /// let io_uring = io_uring::IoUring::new(1)?;
521    /// let mut probe = io_uring::Probe::new();
522    /// io_uring.submitter().register_probe(&mut probe)?;
523    ///
524    /// if probe.is_supported(io_uring::opcode::Read::CODE) {
525    ///     println!("Reading is supported!");
526    /// }
527    /// # Ok(())
528    /// # }
529    /// ```
530    pub fn register_probe(&self, probe: &mut Probe) -> io::Result<()> {
531        self.execute_register(
532            sys::IORING_REGISTER_PROBE,
533            probe.as_mut_ptr() as *const _,
534            Probe::COUNT as _,
535        )
536        .map(drop)
537    }
538
539    /// Register credentials of the running application with io_uring, and get an id associated with
540    /// these credentials. This ID can then be [passed](crate::squeue::Entry::personality) into
541    /// submission queue entries to issue the request with this process' credentials.
542    ///
543    /// By default, if [`Parameters::is_feature_cur_personality`] is set then requests will use the
544    /// credentials of the task that called [`Submitter::enter`], otherwise they will use the
545    /// credentials of the task that originally registered the io_uring.
546    ///
547    /// [`Parameters::is_feature_cur_personality`]: crate::Parameters::is_feature_cur_personality
548    pub fn register_personality(&self) -> io::Result<u16> {
549        let id = self.execute_register(sys::IORING_REGISTER_PERSONALITY, ptr::null(), 0)?;
550        Ok(id as u16)
551    }
552
553    /// Unregister all previously registered buffers.
554    ///
555    /// You do not need to explicitly call this before dropping the [`IoUring`](crate::IoUring), as
556    /// it will be cleaned up by the kernel automatically.
557    ///
558    /// Available since Linux 5.1.
559    pub fn unregister_buffers(&self) -> io::Result<()> {
560        self.execute_register(sys::IORING_UNREGISTER_BUFFERS, ptr::null(), 0)
561            .map(drop)
562    }
563
564    /// Unregister all previously registered files.
565    ///
566    /// You do not need to explicitly call this before dropping the [`IoUring`](crate::IoUring), as
567    /// it will be cleaned up by the kernel automatically.
568    pub fn unregister_files(&self) -> io::Result<()> {
569        self.execute_register(sys::IORING_UNREGISTER_FILES, ptr::null(), 0)
570            .map(drop)
571    }
572
573    /// Unregister an eventfd file descriptor to stop notifications.
574    pub fn unregister_eventfd(&self) -> io::Result<()> {
575        self.execute_register(sys::IORING_UNREGISTER_EVENTFD, ptr::null(), 0)
576            .map(drop)
577    }
578
579    /// Unregister a previously registered personality.
580    pub fn unregister_personality(&self, personality: u16) -> io::Result<()> {
581        self.execute_register(
582            sys::IORING_UNREGISTER_PERSONALITY,
583            ptr::null(),
584            personality as _,
585        )
586        .map(drop)
587    }
588
589    /// Permanently install a feature allowlist. Once this has been called, attempting to perform
590    /// an operation not on the allowlist will fail with `-EACCES`.
591    ///
592    /// This can only be called once, to prevent untrusted code from removing restrictions.
593    pub fn register_restrictions(&self, res: &mut [Restriction]) -> io::Result<()> {
594        self.execute_register(
595            sys::IORING_REGISTER_RESTRICTIONS,
596            res.as_mut_ptr().cast(),
597            res.len() as _,
598        )
599        .map(drop)
600    }
601
602    /// Enable the rings of the io_uring instance if they have been disabled with
603    /// [`setup_r_disabled`](crate::Builder::setup_r_disabled).
604    pub fn register_enable_rings(&self) -> io::Result<()> {
605        self.execute_register(sys::IORING_REGISTER_ENABLE_RINGS, ptr::null(), 0)
606            .map(drop)
607    }
608
609    /// Tell io_uring on what CPUs the async workers can run. By default, async workers
610    /// created by io_uring will inherit the CPU mask of its parent. This is usually
611    /// all the CPUs in the system, unless the parent is being run with a limited set.
612    pub fn register_iowq_aff(&self, cpu_set: &libc::cpu_set_t) -> io::Result<()> {
613        self.execute_register(
614            sys::IORING_REGISTER_IOWQ_AFF,
615            cpu_set as *const _ as *const libc::c_void,
616            mem::size_of::<libc::cpu_set_t>() as u32,
617        )
618        .map(drop)
619    }
620
621    /// Undoes a CPU mask previously set with register_iowq_aff
622    pub fn unregister_iowq_aff(&self) -> io::Result<()> {
623        self.execute_register(sys::IORING_UNREGISTER_IOWQ_AFF, ptr::null(), 0)
624            .map(drop)
625    }
626
627    /// Get and/or set the limit for number of io_uring worker threads per NUMA
628    /// node. `max[0]` holds the limit for bounded workers, which process I/O
629    /// operations expected to be bound in time, that is I/O on regular files or
630    /// block devices. While `max[1]` holds the limit for unbounded workers,
631    /// which carry out I/O operations that can never complete, for instance I/O
632    /// on sockets. Passing `0` does not change the current limit. Returns
633    /// previous limits on success.
634    pub fn register_iowq_max_workers(&self, max: &mut [u32; 2]) -> io::Result<()> {
635        self.execute_register(
636            sys::IORING_REGISTER_IOWQ_MAX_WORKERS,
637            max.as_mut_ptr().cast(),
638            max.len() as _,
639        )
640        .map(drop)
641    }
642
643    /// Register the io_uring instance's own file descriptor with the kernel, so that subsequent
644    /// [`enter`](Self::enter) calls (including [`submit`](Self::submit) and
645    /// [`submit_and_wait`](Self::submit_and_wait)) automatically pass
646    /// [`EnterFlags::REGISTERED_RING`] together with the registered index instead of the raw file
647    /// descriptor. This avoids the per-call file descriptor lookup overhead in the kernel.
648    ///
649    /// The registration is remembered by this [`Submitter`] and used transparently; call
650    /// [`unregister_ring_fd`](Self::unregister_ring_fd) on the same [`Submitter`] to undo it.
651    /// Calling this while a ring fd is already registered on this [`Submitter`] returns an `EEXIST`
652    /// error.
653    ///
654    /// On kernels with `IORING_FEAT_REG_REG_RING`, registration methods on this [`Submitter`] also
655    /// use the registered ring fd internally.
656    ///
657    /// Available since Linux 5.18.
658    pub fn register_ring_fd(&mut self) -> io::Result<()> {
659        if self.enter_ring_fd >= 0 {
660            return Err(io::Error::from_raw_os_error(libc::EEXIST));
661        }
662        let raw_fd = self.fd.as_raw_fd();
663        let mut up = sys::io_uring_rsrc_update {
664            offset: u32::MAX,
665            resv: 0,
666            data: raw_fd as _,
667        };
668        self.execute_register(
669            sys::IORING_REGISTER_RING_FDS,
670            (&mut up as *mut sys::io_uring_rsrc_update).cast(),
671            1,
672        )?;
673        self.enter_ring_fd = up.offset as i32;
674        Ok(())
675    }
676
677    /// Unregister a ring file descriptor previously registered with
678    /// [`register_ring_fd`](Self::register_ring_fd). Subsequent [`enter`](Self::enter) calls revert
679    /// to using the raw file descriptor, as do subsequent registration calls. Returns an `EINVAL`
680    /// error if no ring fd is registered.
681    ///
682    /// Available since Linux 5.18.
683    pub fn unregister_ring_fd(&mut self) -> io::Result<()> {
684        let offset = self.enter_ring_fd;
685        if offset < 0 {
686            return Err(io::Error::from_raw_os_error(libc::EINVAL));
687        }
688        let up = sys::io_uring_rsrc_update {
689            offset: offset as u32,
690            resv: 0,
691            data: 0,
692        };
693        self.execute_register(
694            sys::IORING_UNREGISTER_RING_FDS,
695            cast_ptr::<sys::io_uring_rsrc_update>(&up).cast(),
696            1,
697        )?;
698        self.enter_ring_fd = -1;
699        Ok(())
700    }
701
702    /// Register NAPI busy-poll settings on this ring.
703    ///
704    /// The kernel writes the previous settings back into `napi` before applying the new
705    /// ones; read them back with [`Napi::busy_poll_timeout`] and
706    /// [`Napi::prefer_busy_poll`].
707    ///
708    /// Available since Linux 6.9.
709    pub fn register_napi(&self, napi: &mut Napi) -> io::Result<()> {
710        self.execute_register(sys::IORING_REGISTER_NAPI, napi.as_mut_ptr().cast(), 1)
711            .map(drop)
712    }
713
714    /// Unregister NAPI busy-poll from this ring.
715    ///
716    /// The kernel writes the current settings back into `napi` before disabling them;
717    /// read them back with [`Napi::busy_poll_timeout`] and [`Napi::prefer_busy_poll`]. A
718    /// valid buffer is required, as the kernel rejects a null argument with `EINVAL`.
719    ///
720    /// Available since Linux 6.9.
721    pub fn unregister_napi(&self, napi: &mut Napi) -> io::Result<()> {
722        self.execute_register(sys::IORING_UNREGISTER_NAPI, napi.as_mut_ptr().cast(), 1)
723            .map(drop)
724    }
725
726    /// Add a NAPI id to this ring's statically tracked busy-poll set.
727    ///
728    /// The ring must already be registered with [`NapiTracking::Static`]; otherwise the
729    /// kernel returns an error. `napi_id` identifies a NIC receive-queue NAPI instance,
730    /// typically obtained from a socket via the `SO_INCOMING_NAPI_ID` socket option.
731    ///
732    /// [`NapiTracking::Static`]: crate::types::NapiTracking::Static
733    ///
734    /// Available since Linux 6.13.
735    pub fn register_napi_add_id(&self, napi_id: u32) -> io::Result<()> {
736        self.register_napi_static_op(sys::IO_URING_NAPI_STATIC_ADD_ID as _, napi_id)
737    }
738
739    /// Remove a NAPI id from this ring's statically tracked busy-poll set.
740    ///
741    /// The ring must already be registered with [`NapiTracking::Static`]; otherwise the
742    /// kernel returns an error. See [`register_napi_add_id`](Self::register_napi_add_id).
743    ///
744    /// [`NapiTracking::Static`]: crate::types::NapiTracking::Static
745    ///
746    /// Available since Linux 6.13.
747    pub fn register_napi_del_id(&self, napi_id: u32) -> io::Result<()> {
748        self.register_napi_static_op(sys::IO_URING_NAPI_STATIC_DEL_ID as _, napi_id)
749    }
750
751    fn register_napi_static_op(&self, opcode: u8, napi_id: u32) -> io::Result<()> {
752        // Both ops are issued through IORING_REGISTER_NAPI, distinguished by `opcode`,
753        // with the NAPI id carried in `op_param`. The kernel writes the current settings
754        // back into the struct, so pass a mutable pointer even though we discard them.
755        let mut arg = sys::io_uring_napi {
756            opcode,
757            op_param: napi_id,
758            ..Default::default()
759        };
760        self.execute_register(
761            sys::IORING_REGISTER_NAPI,
762            (&mut arg as *mut sys::io_uring_napi).cast(),
763            1,
764        )
765        .map(drop)
766    }
767
768    /// Register buffer ring for provided buffers.
769    ///
770    /// Details can be found in the io_uring_register_buf_ring.3 man page.
771    ///
772    /// If the register command is not supported, or the ring_entries value exceeds
773    /// 32768, the InvalidInput error is returned.
774    ///
775    /// Available since 5.19.
776    ///
777    /// # Safety
778    ///
779    /// Developers must ensure that the `ring_addr` and its length represented by `ring_entries`
780    /// are valid and will be valid until the bgid is unregistered or the ring destroyed,
781    /// otherwise undefined behaviour may occur.
782    #[deprecated(note = "please use `register_buf_ring_with_flags` instead")]
783    pub unsafe fn register_buf_ring(
784        &self,
785        ring_addr: u64,
786        ring_entries: u16,
787        bgid: u16,
788    ) -> io::Result<()> {
789        self.register_buf_ring_with_flags(ring_addr, ring_entries, bgid, 0)
790    }
791
792    /// Register buffer ring for provided buffers.
793    ///
794    /// Details can be found in the io_uring_register_buf_ring.3 man page.
795    ///
796    /// If the register command is not supported, or the ring_entries value exceeds
797    /// 32768, the InvalidInput error is returned.
798    ///
799    /// Available since 5.19.
800    ///
801    /// # Safety
802    ///
803    /// Developers must ensure that the `ring_addr` and its length represented by `ring_entries`
804    /// are valid and will be valid until the bgid is unregistered or the ring destroyed,
805    /// otherwise undefined behaviour may occur.
806    pub unsafe fn register_buf_ring_with_flags(
807        &self,
808        ring_addr: u64,
809        ring_entries: u16,
810        bgid: u16,
811        flags: u16,
812    ) -> io::Result<()> {
813        // The interface type for ring_entries is u32 but the same interface only allows a u16 for
814        // the tail to be specified, so to try and avoid further confusion, we limit the
815        // ring_entries to u16 here too. The value is actually limited to 2^15 (32768) but we can
816        // let the kernel enforce that.
817        let arg = sys::io_uring_buf_reg {
818            ring_addr,
819            ring_entries: ring_entries as _,
820            bgid,
821            flags,
822            ..Default::default()
823        };
824        self.execute_register(
825            sys::IORING_REGISTER_PBUF_RING,
826            cast_ptr::<sys::io_uring_buf_reg>(&arg).cast(),
827            1,
828        )
829        .map(drop)
830    }
831
832    /// Unregister a previously registered buffer ring.
833    ///
834    /// Available since 5.19.
835    pub fn unregister_buf_ring(&self, bgid: u16) -> io::Result<()> {
836        let arg = sys::io_uring_buf_reg {
837            ring_addr: 0,
838            ring_entries: 0,
839            bgid,
840            ..Default::default()
841        };
842        self.execute_register(
843            sys::IORING_UNREGISTER_PBUF_RING,
844            cast_ptr::<sys::io_uring_buf_reg>(&arg).cast(),
845            1,
846        )
847        .map(drop)
848    }
849
850    /// Performs a synchronous cancellation request, similar to [AsyncCancel](crate::opcode::AsyncCancel),
851    /// except that it completes synchronously.
852    ///
853    /// Cancellation can target a specific request, or all requests matching some criteria. The
854    /// [`CancelBuilder`] builder supports describing the match criteria for cancellation.
855    ///
856    /// An optional `timeout` can be provided to specify how long to wait for matched requests to be
857    /// canceled. If no timeout is provided, the default is to wait indefinitely.
858    ///
859    /// ### Errors
860    ///
861    /// If no requests are matched, returns:
862    ///
863    /// [io::ErrorKind::NotFound]: `No such file or directory (os error 2)`
864    ///
865    /// If a timeout is supplied, and the timeout elapses prior to all requests being canceled, returns:
866    ///
867    /// [io::ErrorKind::Uncategorized]: `Timer expired (os error 62)`
868    ///
869    /// ### Notes
870    ///
871    /// Only requests which have been submitted to the ring will be considered for cancellation. Requests
872    /// which have been written to the SQ, but not submitted, will not be canceled.
873    ///
874    /// Available since 6.0.
875    pub fn register_sync_cancel(
876        &self,
877        timeout: Option<Timespec>,
878        builder: CancelBuilder,
879    ) -> io::Result<()> {
880        let timespec = timeout.map(|ts| ts.0).unwrap_or(sys::__kernel_timespec {
881            tv_sec: -1,
882            tv_nsec: -1,
883        });
884        let user_data = builder.user_data.unwrap_or(0);
885        let flags = builder.flags.bits();
886        let fd = builder.to_fd();
887
888        let arg = sys::io_uring_sync_cancel_reg {
889            addr: user_data,
890            fd,
891            flags,
892            timeout: timespec,
893            ..Default::default()
894        };
895
896        self.execute_register(
897            sys::IORING_REGISTER_SYNC_CANCEL,
898            cast_ptr::<sys::io_uring_sync_cancel_reg>(&arg).cast(),
899            1,
900        )
901        .map(drop)
902    }
903
904    /// Register a netdev hw rx queue for zerocopy.
905    ///
906    /// Available since 6.15.
907    pub fn register_ifq(&self, reg: &sys::io_uring_zcrx_ifq_reg) -> io::Result<()> {
908        self.execute_register(
909            sys::IORING_REGISTER_ZCRX_IFQ,
910            cast_ptr::<sys::io_uring_zcrx_ifq_reg>(reg) as _,
911            1,
912        )
913        .map(drop)
914    }
915}