Skip to main content

io_uring/
types.rs

1//! Common Linux types not provided by libc.
2
3pub(crate) mod sealed {
4    use super::{Fd, Fixed};
5    use std::os::unix::io::RawFd;
6
7    #[derive(Debug)]
8    pub enum Target {
9        Fd(RawFd),
10        Fixed(u32),
11    }
12
13    pub trait UseFd: Sized {
14        fn into(self) -> RawFd;
15    }
16
17    pub trait UseFixed: Sized {
18        fn into(self) -> Target;
19    }
20
21    impl UseFd for Fd {
22        #[inline]
23        fn into(self) -> RawFd {
24            self.0
25        }
26    }
27
28    impl UseFixed for Fd {
29        #[inline]
30        fn into(self) -> Target {
31            Target::Fd(self.0)
32        }
33    }
34
35    impl UseFixed for Fixed {
36        #[inline]
37        fn into(self) -> Target {
38            Target::Fixed(self.0)
39        }
40    }
41}
42
43use crate::sys;
44use crate::util::{cast_ptr, unwrap_nonzero, unwrap_u32};
45use bitflags::bitflags;
46use std::convert::TryFrom;
47use std::marker::PhantomData;
48use std::num::NonZeroU32;
49use std::os::unix::io::RawFd;
50
51#[deprecated]
52pub type RwFlags = u32;
53pub use sys::{
54    io_uring_region_desc, io_uring_zcrx_area_reg, io_uring_zcrx_cqe, io_uring_zcrx_ifq_reg,
55    io_uring_zcrx_rqe, IORING_MEM_REGION_TYPE_USER, IORING_ZCRX_AREA_SHIFT, IOU_PBUF_RING_INC,
56    IOU_PBUF_RING_MMAP,
57};
58
59// From linux/io_uring.h
60//
61// NOTE: bindgen skips this due to the expression so we define it manually.
62pub const IORING_ZCRX_AREA_MASK: u64 = !((1u64 << IORING_ZCRX_AREA_SHIFT) - 1);
63
64/// Opaque types, you should use [`statx`](struct@libc::statx) instead.
65#[repr(C)]
66#[allow(non_camel_case_types)]
67pub struct statx {
68    _priv: (),
69}
70
71/// Opaque types, you should use [`epoll_event`](libc::epoll_event) instead.
72#[repr(C)]
73#[allow(non_camel_case_types)]
74pub struct epoll_event {
75    _priv: (),
76}
77
78/// A file descriptor that has not been registered with io_uring.
79#[derive(Debug, Clone, Copy)]
80#[repr(transparent)]
81pub struct Fd(pub RawFd);
82
83/// A file descriptor that has been registered with io_uring using
84/// [`Submitter::register_files`](crate::Submitter::register_files) or [`Submitter::register_files_sparse`](crate::Submitter::register_files_sparse).
85/// This can reduce overhead compared to using [`Fd`] in some cases.
86#[derive(Debug, Clone, Copy)]
87#[repr(transparent)]
88pub struct Fixed(pub u32);
89
90bitflags! {
91    /// Options for [`Timeout`](super::Timeout).
92    ///
93    /// The default behavior is to treat the timespec as a relative time interval. `flags` may
94    /// contain [`TimeoutFlags::ABS`] to indicate the timespec represents an absolute
95    /// time. When an absolute time is being specified, the kernel will use its monotonic clock
96    /// unless one of the following flags is set (they may not both be set):
97    /// [`TimeoutFlags::BOOTTIME`] or [`TimeoutFlags::REALTIME`].
98    ///
99    /// The default behavior when the timeout expires is to sever dependent links, as a failed
100    /// request normally would. To keep the links untouched include [`TimeoutFlags::ETIME_SUCCESS`].
101    /// CQE will still contain -libc::ETIME in the res field
102    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
103    pub struct TimeoutFlags: u32 {
104        const ABS = sys::IORING_TIMEOUT_ABS;
105
106        const BOOTTIME = sys::IORING_TIMEOUT_BOOTTIME;
107
108        const REALTIME = sys::IORING_TIMEOUT_REALTIME;
109
110        const LINK_TIMEOUT_UPDATE = sys::IORING_LINK_TIMEOUT_UPDATE;
111
112        const ETIME_SUCCESS = sys::IORING_TIMEOUT_ETIME_SUCCESS;
113
114        const MULTISHOT = sys::IORING_TIMEOUT_MULTISHOT;
115    }
116}
117
118bitflags! {
119    /// Options for [`Fsync`](super::Fsync).
120    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
121    pub struct FsyncFlags: u32 {
122        const DATASYNC = sys::IORING_FSYNC_DATASYNC;
123    }
124}
125
126bitflags! {
127    /// Options for [`AsyncCancel`](super::AsyncCancel) and
128    /// [`Submitter::register_sync_cancel`](super::Submitter::register_sync_cancel).
129    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
130    pub(crate) struct AsyncCancelFlags: u32 {
131        /// Cancel all requests that match the given criteria, rather
132        /// than just canceling the first one found.
133        ///
134        /// Available since 5.19.
135        const ALL = sys::IORING_ASYNC_CANCEL_ALL;
136
137        /// Match based on the file descriptor used in the original
138        /// request rather than the user_data.
139        ///
140        /// Available since 5.19.
141        const FD = sys::IORING_ASYNC_CANCEL_FD;
142
143        /// Match any request in the ring, regardless of user_data or
144        /// file descriptor.  Can be used to cancel any pending
145        /// request in the ring.
146        ///
147        /// Available since 5.19.
148        const ANY = sys::IORING_ASYNC_CANCEL_ANY;
149
150        /// Match based on the fixed file descriptor used in the original
151        /// request rather than the user_data.
152        ///
153        /// Available since 6.0
154        const FD_FIXED = sys::IORING_ASYNC_CANCEL_FD_FIXED;
155    }
156}
157
158bitflags! {
159    /// Options for
160    /// [`Submitter::register_buffers_clone`](super::Submitter::register_buffers_clone) and
161    /// [`Submitter::register_buffers_clone_offset`](super::Submitter::register_buffers_clone_offset).
162    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
163    pub struct CloneBuffersFlags: u32 {
164        /// Interpret `src_fd` as a registered ring descriptor rather than a
165        /// raw file descriptor.
166        const SRC_REGISTERED = sys::IORING_REGISTER_SRC_REGISTERED;
167
168        /// Allow cloning into a destination range that already has buffers
169        /// registered, replacing them. Without this flag the destination
170        /// range must be empty.
171        const DST_REPLACE = sys::IORING_REGISTER_DST_REPLACE;
172    }
173}
174
175/// Wrapper around `open_how` as used in [the `openat2(2)` system
176/// call](https://man7.org/linux/man-pages/man2/openat2.2.html).
177#[derive(Default, Debug, Clone, Copy)]
178#[repr(transparent)]
179pub struct OpenHow(sys::open_how);
180
181impl OpenHow {
182    pub const fn new() -> Self {
183        OpenHow(sys::open_how {
184            flags: 0,
185            mode: 0,
186            resolve: 0,
187        })
188    }
189
190    pub const fn flags(mut self, flags: u64) -> Self {
191        self.0.flags = flags;
192        self
193    }
194
195    pub const fn mode(mut self, mode: u64) -> Self {
196        self.0.mode = mode;
197        self
198    }
199
200    pub const fn resolve(mut self, resolve: u64) -> Self {
201        self.0.resolve = resolve;
202        self
203    }
204}
205
206#[derive(Default, Debug, Clone, Copy)]
207#[repr(transparent)]
208pub struct Timespec(pub(crate) sys::__kernel_timespec);
209
210impl Timespec {
211    #[inline]
212    pub const fn new() -> Self {
213        Timespec(sys::__kernel_timespec {
214            tv_sec: 0,
215            tv_nsec: 0,
216        })
217    }
218
219    #[inline]
220    pub const fn sec(mut self, sec: u64) -> Self {
221        self.0.tv_sec = sec as _;
222        self
223    }
224
225    #[inline]
226    pub const fn nsec(mut self, nsec: u32) -> Self {
227        self.0.tv_nsec = nsec as _;
228        self
229    }
230}
231
232impl From<std::time::Duration> for Timespec {
233    fn from(value: std::time::Duration) -> Self {
234        Timespec::new()
235            .sec(value.as_secs())
236            .nsec(value.subsec_nanos())
237    }
238}
239
240/// Submit arguments
241///
242/// Note that arguments that exceed their lifetime will fail to compile.
243///
244/// ```compile_fail
245/// use io_uring::types::{ SubmitArgs, Timespec };
246///
247/// let sigmask: libc::sigset_t = unsafe { std::mem::zeroed() };
248///
249/// let mut args = SubmitArgs::new();
250///
251/// {
252///     let ts = Timespec::new();
253///     args = args.timespec(&ts);
254///     args = args.sigmask(&sigmask);
255/// }
256///
257/// drop(args);
258/// ```
259#[repr(transparent)]
260#[derive(Default, Debug, Clone, Copy)]
261pub struct SubmitArgs<'prev: 'now, 'now> {
262    pub(crate) args: sys::io_uring_getevents_arg,
263    prev: PhantomData<&'prev ()>,
264    now: PhantomData<&'now ()>,
265}
266
267impl<'prev, 'now> SubmitArgs<'prev, 'now> {
268    #[inline]
269    pub const fn new() -> SubmitArgs<'static, 'static> {
270        let args = sys::io_uring_getevents_arg {
271            sigmask: 0,
272            sigmask_sz: 0,
273            min_wait_usec: 0,
274            ts: 0,
275        };
276
277        SubmitArgs {
278            args,
279            prev: PhantomData,
280            now: PhantomData,
281        }
282    }
283
284    #[inline]
285    /// Signals to mask during waiting for the result
286    ///
287    /// Masked signals will be restored after submit operation returns
288    pub fn sigmask<'new>(mut self, sigmask: &'new libc::sigset_t) -> SubmitArgs<'now, 'new> {
289        self.args.sigmask = cast_ptr(sigmask) as _;
290        self.args.sigmask_sz = std::mem::size_of::<libc::sigset_t>() as _;
291
292        SubmitArgs {
293            args: self.args,
294            prev: self.now,
295            now: PhantomData,
296        }
297    }
298
299    /// Sets a timeout in microseconds to start waiting for a minimum of a single completion.
300    ///
301    /// Once the timeout expires, the kernel will return when a single completion has been received
302    /// instead of waiting for the minimum amount of completions specified by the `want` parameter
303    /// in the call to [`Submitter::submit_and_wait`](crate::Submitter::submit_and_wait) or
304    /// [`Submitter::submit_with_args`](crate::Submitter::submit_with_args).
305    ///
306    /// Available since 6.12. Use the
307    /// [`Parameters::is_feature_min_timeout`](crate::Parameters::is_feature_min_timeout) method to
308    /// check for availability.
309    #[inline]
310    pub fn min_wait_usec(mut self, min_wait_usec: u32) -> Self {
311        self.args.min_wait_usec = min_wait_usec;
312        self
313    }
314
315    #[inline]
316    /// Timeout for submit operation
317    pub fn timespec<'new>(mut self, timespec: &'new Timespec) -> SubmitArgs<'now, 'new> {
318        self.args.ts = cast_ptr(timespec) as _;
319
320        SubmitArgs {
321            args: self.args,
322            prev: self.now,
323            now: PhantomData,
324        }
325    }
326}
327
328#[repr(transparent)]
329pub struct BufRingEntry(sys::io_uring_buf);
330
331/// An entry in a buf_ring that allows setting the address, length and buffer id.
332#[allow(clippy::len_without_is_empty)]
333impl BufRingEntry {
334    /// Sets the entry addr.
335    pub fn set_addr(&mut self, addr: u64) {
336        self.0.addr = addr;
337    }
338
339    /// Returns the entry addr.
340    pub fn addr(&self) -> u64 {
341        self.0.addr
342    }
343
344    /// Sets the entry len.
345    pub fn set_len(&mut self, len: u32) {
346        self.0.len = len;
347    }
348
349    /// Returns the entry len.
350    pub fn len(&self) -> u32 {
351        self.0.len
352    }
353
354    /// Sets the entry bid.
355    pub fn set_bid(&mut self, bid: u16) {
356        self.0.bid = bid;
357    }
358
359    /// Returns the entry bid.
360    pub fn bid(&self) -> u16 {
361        self.0.bid
362    }
363
364    /// The offset to the ring's tail field given the ring's base address.
365    ///
366    /// The caller should ensure the ring's base address is aligned with the system's page size,
367    /// per the uring interface requirements.
368    ///
369    /// # Safety
370    ///
371    /// The ptr will be dereferenced in order to determine the address of the resv field,
372    /// so the caller is responsible for passing in a valid pointer. And not just
373    /// a valid pointer type, but also the argument must be the address to the first entry
374    /// of the buf_ring for the resv field to even be considered the tail field of the ring.
375    /// The entry must also be properly initialized.
376    pub unsafe fn tail(ring_base: *const BufRingEntry) -> *const u16 {
377        std::ptr::addr_of!((*ring_base).0.resv)
378    }
379}
380
381/// A destination slot for sending fixed resources
382/// (e.g. [`opcode::MsgRingSendFd`](crate::opcode::MsgRingSendFd)).
383#[derive(Debug, Clone, Copy)]
384pub struct DestinationSlot {
385    /// Fixed slot as indexed by the kernel (target+1).
386    dest: NonZeroU32,
387}
388
389impl DestinationSlot {
390    // SAFETY: kernel constant, `IORING_FILE_INDEX_ALLOC` is always > 0.
391    const AUTO_ALLOC: NonZeroU32 =
392        unwrap_nonzero(NonZeroU32::new(sys::IORING_FILE_INDEX_ALLOC as u32));
393
394    /// Use an automatically allocated target slot.
395    pub const fn auto_target() -> Self {
396        Self {
397            dest: DestinationSlot::AUTO_ALLOC,
398        }
399    }
400
401    /// Try to use a given target slot.
402    ///
403    /// Valid slots are in the range from `0` to `u32::MAX - 2` inclusive.
404    pub fn try_from_slot_target(target: u32) -> Result<Self, u32> {
405        // SAFETY: kernel constant, `IORING_FILE_INDEX_ALLOC` is always >= 2.
406        const MAX_INDEX: u32 = unwrap_u32(DestinationSlot::AUTO_ALLOC.get().checked_sub(2));
407
408        if target > MAX_INDEX {
409            return Err(target);
410        }
411
412        let kernel_index = target.saturating_add(1);
413        // SAFETY: by construction, always clamped between 1 and IORING_FILE_INDEX_ALLOC-1.
414        debug_assert!(0 < kernel_index && kernel_index < DestinationSlot::AUTO_ALLOC.get());
415        let dest = NonZeroU32::new(kernel_index).unwrap();
416
417        Ok(Self { dest })
418    }
419
420    pub(crate) fn kernel_index_arg(&self) -> u32 {
421        self.dest.get()
422    }
423}
424
425/// Helper structure for parsing the result of a multishot [`opcode::RecvMsg`](crate::opcode::RecvMsg).
426#[derive(Debug)]
427pub struct RecvMsgOut<'buf> {
428    header: sys::io_uring_recvmsg_out,
429    /// The fixed length of the name field, in bytes.
430    ///
431    /// If the incoming name data is larger than this, it gets truncated to this.
432    /// If it is smaller, it gets 0-padded to fill the whole field. In either case,
433    /// this fixed amount of space is reserved in the result buffer.
434    msghdr_name_len: usize,
435
436    name_data: &'buf [u8],
437    control_data: &'buf [u8],
438    payload_data: &'buf [u8],
439}
440
441impl<'buf> RecvMsgOut<'buf> {
442    const DATA_START: usize = std::mem::size_of::<sys::io_uring_recvmsg_out>();
443
444    /// Parse the data buffered upon completion of a `RecvMsg` multishot operation.
445    ///
446    /// `buffer` is the whole buffer previously provided to the ring, while `msghdr`
447    /// is the same content provided as input to the corresponding SQE
448    /// (only `msg_namelen` and `msg_controllen` fields are relevant).
449    #[allow(clippy::result_unit_err)]
450    #[allow(clippy::useless_conversion)]
451    pub fn parse(buffer: &'buf [u8], msghdr: &libc::msghdr) -> Result<Self, ()> {
452        let msghdr_name_len = usize::try_from(msghdr.msg_namelen).unwrap();
453        let msghdr_control_len = usize::try_from(msghdr.msg_controllen).unwrap();
454
455        if Self::DATA_START
456            .checked_add(msghdr_name_len)
457            .and_then(|acc| acc.checked_add(msghdr_control_len))
458            .map(|header_len| buffer.len() < header_len)
459            .unwrap_or(true)
460        {
461            return Err(());
462        }
463        // SAFETY: buffer (minimum) length is checked here above.
464        let header = unsafe {
465            buffer
466                .as_ptr()
467                .cast::<sys::io_uring_recvmsg_out>()
468                .read_unaligned()
469        };
470
471        // min is used because the header may indicate the true size of the data
472        // while what we received was truncated.
473        let (name_data, control_start) = {
474            let name_start = Self::DATA_START;
475            let name_data_end =
476                name_start + usize::min(usize::try_from(header.namelen).unwrap(), msghdr_name_len);
477            let name_field_end = name_start + msghdr_name_len;
478            (&buffer[name_start..name_data_end], name_field_end)
479        };
480        let (control_data, payload_start) = {
481            let control_data_end = control_start
482                + usize::min(
483                    usize::try_from(header.controllen).unwrap(),
484                    msghdr_control_len,
485                );
486            let control_field_end = control_start + msghdr_control_len;
487            (&buffer[control_start..control_data_end], control_field_end)
488        };
489        let payload_data = {
490            let payload_data_end = payload_start
491                + usize::min(
492                    usize::try_from(header.payloadlen).unwrap(),
493                    buffer.len() - payload_start,
494                );
495            &buffer[payload_start..payload_data_end]
496        };
497
498        Ok(Self {
499            header,
500            msghdr_name_len,
501            name_data,
502            control_data,
503            payload_data,
504        })
505    }
506
507    /// Return the length of the incoming `name` data.
508    ///
509    /// This may be larger than the size of the content returned by
510    /// `name_data()`, if the kernel could not fit all the incoming
511    /// data in the provided buffer size. In that case, name data in
512    /// the result buffer gets truncated.
513    pub fn incoming_name_len(&self) -> u32 {
514        self.header.namelen
515    }
516
517    /// Return whether the incoming name data was larger than the provided limit/buffer.
518    ///
519    /// When `true`, data returned by `name_data()` is truncated and
520    /// incomplete.
521    pub fn is_name_data_truncated(&self) -> bool {
522        self.header.namelen as usize > self.msghdr_name_len
523    }
524
525    /// Message control data, with the same semantics as `msghdr.msg_control`.
526    pub fn name_data(&self) -> &[u8] {
527        self.name_data
528    }
529
530    /// Return the length of the incoming `control` data.
531    ///
532    /// This may be larger than the size of the content returned by
533    /// `control_data()`, if the kernel could not fit all the incoming
534    /// data in the provided buffer size. In that case, control data in
535    /// the result buffer gets truncated.
536    pub fn incoming_control_len(&self) -> u32 {
537        self.header.controllen
538    }
539
540    /// Return whether the incoming control data was larger than the provided limit/buffer.
541    ///
542    /// When `true`, data returned by `control_data()` is truncated and
543    /// incomplete.
544    pub fn is_control_data_truncated(&self) -> bool {
545        (self.header.flags & u32::try_from(libc::MSG_CTRUNC).unwrap()) != 0
546    }
547
548    /// Message control data, with the same semantics as `msghdr.msg_control`.
549    pub fn control_data(&self) -> &[u8] {
550        self.control_data
551    }
552
553    /// Return whether the incoming payload was larger than the provided limit/buffer.
554    ///
555    /// When `true`, data returned by `payload_data()` is truncated and
556    /// incomplete.
557    pub fn is_payload_truncated(&self) -> bool {
558        (self.header.flags & u32::try_from(libc::MSG_TRUNC).unwrap()) != 0
559    }
560
561    /// Message payload, as buffered by the kernel.
562    pub fn payload_data(&self) -> &[u8] {
563        self.payload_data
564    }
565
566    /// Return the length of the incoming `payload` data.
567    ///
568    /// This may be larger than the size of the content returned by
569    /// `payload_data()`, if the kernel could not fit all the incoming
570    /// data in the provided buffer size. In that case, payload data in
571    /// the result buffer gets truncated.
572    pub fn incoming_payload_len(&self) -> u32 {
573        self.header.payloadlen
574    }
575
576    /// Message flags, with the same semantics as `msghdr.msg_flags`.
577    pub fn flags(&self) -> u32 {
578        self.header.flags
579    }
580}
581
582/// [CancelBuilder] constructs match criteria for request cancellation.
583///
584/// The [CancelBuilder] can be used to selectively cancel one or more requests
585/// by user_data, fd, fixed fd, or unconditionally.
586///
587/// ### Examples
588///
589/// ```
590/// use io_uring::types::{CancelBuilder, Fd, Fixed};
591///
592/// // Match all in-flight requests.
593/// CancelBuilder::any();
594///
595/// // Match a single request with user_data = 42.
596/// CancelBuilder::user_data(42);
597///
598/// // Match a single request with fd = 42.
599/// CancelBuilder::fd(Fd(42));
600///
601/// // Match a single request with fixed fd = 42.
602/// CancelBuilder::fd(Fixed(42));
603///
604/// // Match all in-flight requests with user_data = 42.
605/// CancelBuilder::user_data(42).all();
606/// ```
607#[derive(Debug)]
608pub struct CancelBuilder {
609    pub(crate) flags: AsyncCancelFlags,
610    pub(crate) user_data: Option<u64>,
611    pub(crate) fd: Option<sealed::Target>,
612}
613
614impl CancelBuilder {
615    /// Create a new [CancelBuilder] which will match any in-flight request.
616    ///
617    /// This will cancel every in-flight request in the ring.
618    ///
619    /// Async cancellation matching any requests is only available since 5.19.
620    pub const fn any() -> Self {
621        Self {
622            flags: AsyncCancelFlags::ANY,
623            user_data: None,
624            fd: None,
625        }
626    }
627
628    /// Create a new [CancelBuilder] which will match in-flight requests
629    /// with the given `user_data` value.
630    ///
631    /// The first request with the given `user_data` value will be canceled.
632    /// [CancelBuilder::all](#method.all) can be called to instead match every
633    /// request with the provided `user_data` value.
634    pub const fn user_data(user_data: u64) -> Self {
635        Self {
636            flags: AsyncCancelFlags::empty(),
637            user_data: Some(user_data),
638            fd: None,
639        }
640    }
641
642    /// Create a new [CancelBuilder] which will match in-flight requests with
643    /// the given `fd` value.
644    ///
645    /// The first request with the given `fd` value will be canceled. [CancelBuilder::all](#method.all)
646    /// can be called to instead match every request with the provided `fd` value.
647    ///
648    /// FD async cancellation is only available since 5.19.
649    pub fn fd(fd: impl sealed::UseFixed) -> Self {
650        let mut flags = AsyncCancelFlags::FD;
651        let target = fd.into();
652        if matches!(target, sealed::Target::Fixed(_)) {
653            flags.insert(AsyncCancelFlags::FD_FIXED);
654        }
655        Self {
656            flags,
657            user_data: None,
658            fd: Some(target),
659        }
660    }
661
662    /// Modify the [CancelBuilder] match criteria to match all in-flight requests
663    /// rather than just the first one.
664    ///
665    /// This has no effect when combined with [CancelBuilder::any](#method.any).
666    ///
667    /// Async cancellation matching all requests is only available since 5.19.
668    pub fn all(mut self) -> Self {
669        self.flags.insert(AsyncCancelFlags::ALL);
670        self
671    }
672
673    pub(crate) fn to_fd(&self) -> i32 {
674        self.fd
675            .as_ref()
676            .map(|target| match *target {
677                sealed::Target::Fd(fd) => fd,
678                sealed::Target::Fixed(idx) => idx as i32,
679            })
680            .unwrap_or(-1)
681    }
682}
683
684/// Wrapper around `futex_waitv` as used in [`futex_waitv` system
685/// call](https://www.kernel.org/doc/html/latest/userspace-api/futex2.html).
686#[derive(Default, Debug, Clone, Copy)]
687#[repr(transparent)]
688pub struct FutexWaitV(sys::futex_waitv);
689
690impl FutexWaitV {
691    pub const fn new() -> Self {
692        Self(sys::futex_waitv {
693            val: 0,
694            uaddr: 0,
695            flags: 0,
696            __reserved: 0,
697        })
698    }
699
700    pub const fn val(mut self, val: u64) -> Self {
701        self.0.val = val;
702        self
703    }
704
705    pub const fn uaddr(mut self, uaddr: u64) -> Self {
706        self.0.uaddr = uaddr;
707        self
708    }
709
710    pub const fn flags(mut self, flags: u32) -> Self {
711        self.0.flags = flags;
712        self
713    }
714}
715
716/// Strategy the kernel uses to track which NAPI instances to busy-poll, used by
717/// [`Napi::tracking`].
718#[derive(Debug, Clone, Copy, PartialEq, Eq)]
719pub enum NapiTracking {
720    /// The kernel discovers NAPI ids automatically from the sockets used on the ring.
721    Dynamic,
722    /// The application manages the polled NAPI id set explicitly.
723    Static,
724    /// NAPI ids are not tracked.
725    Inactive,
726}
727
728impl NapiTracking {
729    const fn op_param(self) -> u32 {
730        match self {
731            NapiTracking::Dynamic => sys::IO_URING_NAPI_TRACKING_DYNAMIC,
732            NapiTracking::Static => sys::IO_URING_NAPI_TRACKING_STATIC,
733            NapiTracking::Inactive => sys::IO_URING_NAPI_TRACKING_INACTIVE,
734        }
735    }
736}
737
738/// NAPI busy-poll configuration for
739/// [`Submitter::register_napi`](super::Submitter::register_napi) and
740/// [`Submitter::unregister_napi`](super::Submitter::unregister_napi).
741///
742/// On (un)register the kernel writes the *previous* settings back into the value, which
743/// can be read with [`busy_poll_timeout`](Self::busy_poll_timeout) and
744/// [`prefer_busy_poll`](Self::prefer_busy_poll).
745///
746/// Available since Linux 6.9.
747#[derive(Debug, Clone, Copy)]
748#[repr(transparent)]
749pub struct Napi(sys::io_uring_napi);
750
751impl Napi {
752    pub const fn new() -> Self {
753        Napi(sys::io_uring_napi {
754            busy_poll_to: 0,
755            prefer_busy_poll: 0,
756            opcode: sys::IO_URING_NAPI_REGISTER_OP as _,
757            pad: [0; 2],
758            op_param: sys::IO_URING_NAPI_TRACKING_DYNAMIC,
759            resv: 0,
760        })
761    }
762
763    /// Set the busy-poll timeout, in microseconds.
764    pub const fn set_busy_poll_timeout(mut self, micros: u32) -> Self {
765        self.0.busy_poll_to = micros;
766        self
767    }
768
769    /// Set whether the kernel should prefer busy-polling over interrupts.
770    pub const fn set_prefer_busy_poll(mut self, enabled: bool) -> Self {
771        self.0.prefer_busy_poll = enabled as _;
772        self
773    }
774
775    /// Set the tracking strategy.
776    pub const fn set_tracking(mut self, strategy: NapiTracking) -> Self {
777        self.0.op_param = strategy.op_param();
778        self
779    }
780
781    /// The busy-poll timeout, in microseconds.
782    pub const fn busy_poll_timeout(&self) -> u32 {
783        self.0.busy_poll_to
784    }
785
786    /// Whether busy-polling is preferred over interrupts.
787    pub const fn prefer_busy_poll(&self) -> bool {
788        self.0.prefer_busy_poll != 0
789    }
790
791    pub(crate) fn as_mut_ptr(&mut self) -> *mut sys::io_uring_napi {
792        &mut self.0
793    }
794}
795
796impl Default for Napi {
797    fn default() -> Self {
798        Self::new()
799    }
800}
801
802#[cfg(test)]
803mod tests {
804    use std::time::Duration;
805
806    use crate::types::sealed::Target;
807
808    use super::*;
809
810    #[test]
811    fn timespec_from_duration_converts_correctly() {
812        let duration = Duration::new(2, 500);
813        let timespec = Timespec::from(duration);
814
815        assert_eq!(timespec.0.tv_sec as u64, duration.as_secs());
816        assert_eq!(timespec.0.tv_nsec as u32, duration.subsec_nanos());
817    }
818
819    #[test]
820    fn test_cancel_builder_flags() {
821        let cb = CancelBuilder::any();
822        assert_eq!(cb.flags, AsyncCancelFlags::ANY);
823
824        let mut cb = CancelBuilder::user_data(42);
825        assert_eq!(cb.flags, AsyncCancelFlags::empty());
826        assert_eq!(cb.user_data, Some(42));
827        assert!(cb.fd.is_none());
828        cb = cb.all();
829        assert_eq!(cb.flags, AsyncCancelFlags::ALL);
830
831        let mut cb = CancelBuilder::fd(Fd(42));
832        assert_eq!(cb.flags, AsyncCancelFlags::FD);
833        assert!(matches!(cb.fd, Some(Target::Fd(42))));
834        assert!(cb.user_data.is_none());
835        cb = cb.all();
836        assert_eq!(cb.flags, AsyncCancelFlags::FD | AsyncCancelFlags::ALL);
837
838        let mut cb = CancelBuilder::fd(Fixed(42));
839        assert_eq!(cb.flags, AsyncCancelFlags::FD | AsyncCancelFlags::FD_FIXED);
840        assert!(matches!(cb.fd, Some(Target::Fixed(42))));
841        assert!(cb.user_data.is_none());
842        cb = cb.all();
843        assert_eq!(
844            cb.flags,
845            AsyncCancelFlags::FD | AsyncCancelFlags::FD_FIXED | AsyncCancelFlags::ALL
846        );
847    }
848}