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}