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