linux_io_uring/
opcode.rs

1//! Operation code
2
3#![allow(clippy::new_without_default)]
4
5use linux_io_uring_sys as sys;
6use crate::squeue::Entry;
7use crate::util::sqe_zeroed;
8
9pub mod types {
10    use std::os::unix::io::RawFd;
11    use bitflags::bitflags;
12    use linux_io_uring_sys as sys;
13
14    pub use sys::__kernel_timespec as Timespec;
15    pub use sys::__kernel_rwf_t as RwFlags;
16
17    #[derive(Debug, Clone, Copy)]
18    pub enum Target {
19        Fd(RawFd),
20
21        /// The index of registered fd.
22        Fixed(u32)
23    }
24
25    bitflags!{
26        pub struct TimeoutFlags: u32 {
27            #[cfg(feature = "unstable")]
28            const ABS = sys::IORING_TIMEOUT_ABS;
29        }
30    }
31
32    bitflags!{
33        pub struct FsyncFlags: u32 {
34            const DATASYNC = sys::IORING_FSYNC_DATASYNC;
35        }
36    }
37}
38
39
40macro_rules! assign_fd {
41    ( $sqe:ident . fd = $opfd:expr ) => {
42        match $opfd {
43            types::Target::Fd(fd) => $sqe.fd = fd,
44            types::Target::Fixed(i) => {
45                $sqe.fd = i as _;
46                $sqe.flags |= crate::squeue::Flags::FIXED_FILE.bits();
47            }
48        }
49    }
50}
51
52macro_rules! opcode {
53    (
54        $( #[$outer:meta] )*
55        pub struct $name:ident {
56            $( #[$new_meta:meta] )*
57            $( $field:ident : $tname:ty ),* $(,)?
58            ;;
59            $(
60                $( #[$opt_meta:meta] )*
61                $opt_field:ident : $opt_tname:ty = $default:expr
62            ),* $(,)?
63        }
64
65        pub fn build($self:ident) -> Entry $build_block:block
66    ) => {
67        $( #[$outer] )*
68        pub struct $name {
69            $( $field : $tname, )*
70            $( $opt_field : $opt_tname, )*
71        }
72
73        impl $name {
74            $( #[$new_meta] )*
75            pub const fn new( $( $field : $tname ),* ) -> Self {
76                $name {
77                    $( $field , )*
78                    $( $opt_field: $default, )*
79                }
80            }
81
82            $(
83                $( #[$opt_meta] )*
84                pub const fn $opt_field(mut self, $opt_field: $opt_tname) -> Self {
85                    self.$opt_field = $opt_field;
86                    self
87                }
88            )*
89
90            pub fn build($self) -> Entry $build_block
91        }
92    }
93}
94
95opcode!(
96    /// Do not perform any I/O.
97    ///
98    /// This is useful for testing the performance of the io_uring implementation itself.
99    #[derive(Debug)]
100    pub struct Nop { ;; }
101
102    pub fn build(self) -> Entry {
103        let Nop {} = self;
104
105        let mut sqe = sqe_zeroed();
106        sqe.opcode = sys::IORING_OP_NOP as _;
107        sqe.fd = -1;
108        Entry(sqe)
109    }
110);
111
112opcode!(
113    /// Vectored read operations, similar to `preadv2 (2)`.
114    ///
115    /// The return values match those documented in the `preadv2 (2)` man pages.
116    #[derive(Debug)]
117    pub struct Readv {
118        fd: types::Target,
119        iovec: *mut libc::iovec,
120        len: u32,
121        ;;
122        ioprio: u16 = 0,
123        offset: libc::off_t = 0,
124        /// specified for read operations, contains a bitwise OR of per-I/O flags,
125        /// as described in the `preadv2 (2)` man page.
126        rw_flags: types::RwFlags = 0
127    }
128
129    pub fn build(self) -> Entry {
130        let Readv {
131            fd,
132            iovec, len, offset,
133            ioprio, rw_flags
134        } = self;
135
136        let mut sqe = sqe_zeroed();
137        sqe.opcode = sys::IORING_OP_READV as _;
138        assign_fd!(sqe.fd = fd);
139        sqe.ioprio = ioprio;
140        sqe.addr = iovec as _;
141        sqe.len = len;
142        sqe.__bindgen_anon_1.off = offset as _;
143        sqe.__bindgen_anon_2.rw_flags = rw_flags;
144        Entry(sqe)
145    }
146);
147
148opcode!(
149    /// Vectored write operations, similar to `pwritev2 (2)`.
150    ///
151    /// The return values match those documented in the `pwritev2 (2)` man pages.
152    #[derive(Debug)]
153    pub struct Writev {
154        fd: types::Target,
155        iovec: *const libc::iovec,
156        len: u32,
157        ;;
158        ioprio: u16 = 0,
159        offset: libc::off_t = 0,
160        /// specified for write operations, contains a bitwise OR of per-I/O flags,
161        /// as described in the `preadv2 (2)` man page.
162        rw_flags: types::RwFlags = 0
163    }
164
165    pub fn build(self) -> Entry {
166        let Writev {
167            fd,
168            iovec, len, offset,
169            ioprio, rw_flags
170        } = self;
171
172        let mut sqe = sqe_zeroed();
173        sqe.opcode = sys::IORING_OP_WRITEV as _;
174        assign_fd!(sqe.fd = fd);
175        sqe.ioprio = ioprio;
176        sqe.addr = iovec as _;
177        sqe.len = len;
178        sqe.__bindgen_anon_1.off = offset as _;
179        sqe.__bindgen_anon_2.rw_flags = rw_flags;
180        Entry(sqe)
181    }
182);
183
184opcode!(
185    /// File sync. See also `fsync (2)`.
186    ///
187    /// Note that, while I/O is initiated in the order in which it appears in the submission queue, completions are unordered.
188    /// For example, an application which places a write I/O followed by an fsync in the submission queue cannot expect the fsync to apply to the write. The two operations execute in parallel, so the fsync may complete before the write is issued to the storage. The same is also true for previously issued writes that have not completed prior to the fsync.
189    #[derive(Debug)]
190    pub struct Fsync {
191        fd: types::Target,
192        ;;
193        /// The `flags` bit mask may contain either 0, for a normal file integrity sync,
194        /// or [types::FsyncFlags::DATASYNC] to provide data sync only semantics.
195        /// See the descriptions of `O_SYNC` and `O_DSYNC` in the `open (2)` manual page for more information.
196        flags: types::FsyncFlags = types::FsyncFlags::empty()
197    }
198
199    pub fn build(self) -> Entry {
200        let Fsync { fd, flags } = self;
201
202        let mut sqe = sqe_zeroed();
203        sqe.opcode = sys::IORING_OP_FSYNC as _;
204        assign_fd!(sqe.fd = fd);
205        sqe.__bindgen_anon_2.fsync_flags = flags.bits();
206        Entry(sqe)
207    }
208);
209
210opcode!(
211    /// Read from pre-mapped buffers.
212    ///
213    /// The return values match those documented in the `preadv2 (2)` man pages.
214    #[derive(Debug)]
215    pub struct ReadFixed {
216        /// The `buf_index` is an index into an array of fixed buffers,
217        /// and is only valid if fixed buffers were registered.
218        fd: types::Target,
219        buf: *mut u8,
220        len: u32,
221        buf_index: u16,
222        ;;
223        ioprio: u16 = 0,
224        offset: libc::off_t = 0,
225        /// specified for read operations, contains a bitwise OR of per-I/O flags,
226        /// as described in the `preadv2 (2)` man page.
227        rw_flags: types::RwFlags = 0
228    }
229
230    pub fn build(self) -> Entry {
231        let ReadFixed {
232            fd,
233            buf, len, offset,
234            buf_index,
235            ioprio, rw_flags
236        } = self;
237
238        let mut sqe = sqe_zeroed();
239        sqe.opcode = sys::IORING_OP_READ_FIXED as _;
240        assign_fd!(sqe.fd = fd);
241        sqe.ioprio = ioprio;
242        sqe.addr = buf as _;
243        sqe.len = len;
244        sqe.__bindgen_anon_1.off = offset as _;
245        sqe.__bindgen_anon_2.rw_flags = rw_flags;
246        sqe.__bindgen_anon_3.buf_index = buf_index;
247        Entry(sqe)
248    }
249);
250
251opcode!(
252    /// Write to pre-mapped buffers.
253    ///
254    /// The return values match those documented in the `pwritev2 (2)` man pages.
255    #[derive(Debug)]
256    pub struct WriteFixed {
257        /// The `buf_index` is an index into an array of fixed buffers,
258        /// and is only valid if fixed buffers were registered.
259        fd: types::Target,
260        buf: *const u8,
261        len: u32,
262        buf_index: u16,
263        ;;
264        ioprio: u16 = 0,
265        offset: libc::off_t = 0,
266        /// specified for write operations, contains a bitwise OR of per-I/O flags,
267        /// as described in the `preadv2 (2)` man page.
268        rw_flags: types::RwFlags = 0
269    }
270
271    pub fn build(self) -> Entry {
272        let WriteFixed {
273            fd,
274            buf, len, offset,
275            buf_index,
276            ioprio, rw_flags
277        } = self;
278
279        let mut sqe = sqe_zeroed();
280        sqe.opcode = sys::IORING_OP_WRITE_FIXED as _;
281        assign_fd!(sqe.fd = fd);
282        sqe.ioprio = ioprio;
283        sqe.addr = buf as _;
284        sqe.len = len;
285        sqe.__bindgen_anon_1.off = offset as _;
286        sqe.__bindgen_anon_2.rw_flags = rw_flags;
287        sqe.__bindgen_anon_3.buf_index = buf_index;
288        Entry(sqe)
289    }
290);
291
292opcode!(
293    /// Poll the specified fd.
294    ///
295    /// Unlike poll or epoll without `EPOLLONESHOT`, this interface always works in one shot mode.
296    /// That is, once the poll operation is completed, it will have to be resubmitted.
297    #[derive(Debug)]
298    pub struct PollAdd {
299        /// The bits that may be set in `flags` are defined in `<poll.h>`,
300        /// and documented in `poll (2)`.
301        fd: types::Target,
302        flags: libc::c_short,
303        ;;
304    }
305
306    pub fn build(self) -> Entry {
307        let PollAdd { fd, flags } = self;
308
309        let mut sqe = sqe_zeroed();
310        sqe.opcode = sys::IORING_OP_POLL_ADD as _;
311        assign_fd!(sqe.fd = fd);
312        sqe.__bindgen_anon_2.poll_events = flags as _;
313        Entry(sqe)
314    }
315);
316
317opcode!(
318    /// Remove an existing poll request.
319    ///
320    /// If found, the `result` method of the `cqueue::Entry` will return 0.
321    /// If not found, `result` will return `-libc::ENOENT`.
322    #[derive(Debug)]
323    pub struct PollRemove {
324        user_data: u64
325        ;;
326    }
327
328    pub fn build(self) -> Entry {
329        let PollRemove { user_data } = self;
330
331        let mut sqe = sqe_zeroed();
332        sqe.opcode = sys::IORING_OP_POLL_REMOVE as _;
333        sqe.fd = -1;
334        sqe.addr = user_data as _;
335        Entry(sqe)
336    }
337);
338
339opcode!(
340    /// Issue the equivalent of a `sync_file_range (2)` on the file descriptor.
341    ///
342    /// See also `sync_file_range (2)`. for the general description of the related system call.
343    #[derive(Debug)]
344    pub struct SyncFileRange {
345        fd: types::Target,
346        len: u32,
347        ;;
348        /// the offset method holds the offset in bytes
349        offset: libc::off_t = 0,
350        /// the flags method holds the flags for the command
351        flags: u32 = 0
352    }
353
354    pub fn build(self) -> Entry {
355        let SyncFileRange {
356            fd,
357            len, offset,
358            flags
359        } = self;
360
361        let mut sqe = sqe_zeroed();
362        sqe.opcode = sys::IORING_OP_SYNC_FILE_RANGE as _;
363        assign_fd!(sqe.fd = fd);
364        sqe.len = len;
365        sqe.__bindgen_anon_1.off = offset as _;
366        sqe.__bindgen_anon_2.sync_range_flags = flags;
367        Entry(sqe)
368    }
369);
370
371opcode!(
372    /// Issue the equivalent of a `sendmsg (2)` system call.
373    ///
374    /// fd must be set to the socket file descriptor, addr must contains a pointer to the msghdr structure,
375    /// and flags holds the flags associated with the system call.
376    /// See also `sendmsg (2)`. for the general description of the related system call.
377    #[derive(Debug)]
378    pub struct SendMsg {
379        fd: types::Target,
380        msg: *const libc::msghdr,
381        ;;
382        ioprio: u16 = 0,
383        flags: u32 = 0
384    }
385
386    pub fn build(self) -> Entry {
387        let SendMsg { fd, msg, ioprio, flags } = self;
388
389        let mut sqe = sqe_zeroed();
390        sqe.opcode = sys::IORING_OP_SENDMSG as _;
391        assign_fd!(sqe.fd = fd);
392        sqe.ioprio = ioprio;
393        sqe.addr = msg as _;
394        sqe.len = 1;
395        sqe.__bindgen_anon_2.msg_flags = flags;
396        Entry(sqe)
397    }
398);
399
400opcode!(
401    /// Works just like [SendMsg], except for instead.
402    ///
403    /// See the description of [SendMsg].
404    #[derive(Debug)]
405    pub struct RecvMsg {
406        fd: types::Target,
407        msg: *mut libc::msghdr,
408        ;;
409        ioprio: u16 = 0,
410        flags: u32 = 0
411    }
412
413    pub fn build(self) -> Entry {
414        let RecvMsg { fd, msg, ioprio, flags } = self;
415
416        let mut sqe = sqe_zeroed();
417        sqe.opcode = sys::IORING_OP_RECVMSG as _;
418        assign_fd!(sqe.fd = fd);
419        sqe.ioprio = ioprio;
420        sqe.addr = msg as _;
421        sqe.len = 1;
422        sqe.__bindgen_anon_2.msg_flags = flags;
423        Entry(sqe)
424    }
425);
426
427opcode!(
428    /// This command will register a timeout operation.
429    ///
430    /// A timeout will trigger a wakeup event on the completion ring for anyone waiting for events.
431    /// A timeout condition is met when either the specified timeout expires, or the specified number of events have completed.
432    /// Either condition will trigger the event.
433    /// The request will complete with `-ETIME` if the timeout got completed through expiration of the timer,
434    /// or 0 if the timeout got completed through requests completing on their own.
435    /// If the timeout was cancelled before it expired, the request will complete with `-ECANCELED`.
436    #[derive(Debug)]
437    pub struct Timeout {
438        timespec: *const types::Timespec,
439        ;;
440        /// `count` may contain a completion event count. If not set, this defaults to 1.
441        count: u32 = 0,
442
443        #[cfg_attr(not(feature = "unstable"), allow(intra_doc_link_resolution_failure))]
444        /// `flags` may contain [types::TimeoutFlags::ABS] for an absolutel timeout value, or 0 for a relative timeout.
445        flags: types::TimeoutFlags = types::TimeoutFlags::empty()
446    }
447
448    pub fn build(self) -> Entry {
449        let Timeout { timespec, count, flags } = self;
450
451        let mut sqe = sqe_zeroed();
452        sqe.opcode = sys::IORING_OP_TIMEOUT as _;
453        sqe.fd = -1;
454        sqe.addr = timespec as _;
455        sqe.len = 1;
456        sqe.__bindgen_anon_1.off = count as _;
457        sqe.__bindgen_anon_2.timeout_flags = flags.bits();
458        Entry(sqe)
459    }
460);
461
462#[cfg(feature = "unstable")]
463opcode!(
464    pub struct TimeoutRemove {
465        user_data: u64,
466        ;;
467        flags: types::TimeoutFlags = types::TimeoutFlags::empty()
468    }
469
470    pub fn build(self) -> Entry {
471        let TimeoutRemove { user_data, flags } = self;
472
473        let mut sqe = sqe_zeroed();
474        sqe.opcode = sys::IORING_OP_TIMEOUT_REMOVE as _;
475        sqe.fd = -1;
476        sqe.addr = user_data as _;
477        sqe.__bindgen_anon_2.timeout_flags = flags.bits();
478        Entry(sqe)
479    }
480);
481
482#[cfg(feature = "unstable")]
483opcode!(
484    pub struct Accept {
485        fd: types::Target,
486        addr: *const libc::sockaddr,
487        addrlen: libc::socklen_t
488        ;;
489        flags: u32 = 0
490    }
491
492    pub fn build(self) -> Entry {
493        let Accept { fd, addr, addrlen, flags } = self;
494
495        let mut sqe = sqe_zeroed();
496        sqe.opcode = sys::IORING_OP_ACCEPT as _;
497        assign_fd!(sqe.fd = fd);
498        sqe.addr = addr as _;
499        sqe.__bindgen_anon_1.off = addrlen as _;
500        sqe.__bindgen_anon_2.accept_flags = flags;
501        Entry(sqe)
502    }
503);
504
505#[cfg(feature = "unstable")]
506opcode!(
507    pub struct AsyncCancel {
508        user_data: u64,
509        ;;
510        flags: u32 = 0
511    }
512
513    pub fn build(self) -> Entry {
514        let AsyncCancel { user_data, flags } = self;
515
516        let mut sqe = sqe_zeroed();
517        sqe.opcode = sys::IORING_OP_ASYNC_CANCEL as _;
518        sqe.fd = -1;
519        sqe.addr = user_data as _;
520        sqe.__bindgen_anon_2.timeout_flags = flags;
521        Entry(sqe)
522    }
523);
524
525#[cfg(feature = "unstable")]
526opcode!(
527    pub struct LinkTimeout {
528        timespec: *const types::Timespec,
529        ;;
530        flags: types::TimeoutFlags = types::TimeoutFlags::empty()
531    }
532
533    pub fn build(self) -> Entry {
534        let LinkTimeout { timespec, flags } = self;
535
536        let mut sqe = sqe_zeroed();
537        sqe.opcode = sys::IORING_OP_TIMEOUT as _;
538        sqe.fd = -1;
539        sqe.addr = timespec as _;
540        sqe.len = 1;
541        sqe.__bindgen_anon_2.timeout_flags = flags.bits();
542        Entry(sqe)
543    }
544);
545
546#[cfg(feature = "unstable")]
547opcode!(
548    pub struct Connect {
549        fd: types::Target,
550        addr: *const libc::sockaddr,
551        addrlen: libc::socklen_t
552        ;;
553    }
554
555    pub fn build(self) -> Entry {
556        let Connect { fd, addr, addrlen } = self;
557
558        let mut sqe = sqe_zeroed();
559        sqe.opcode = sys::IORING_OP_CONNECT as _;
560        assign_fd!(sqe.fd = fd);
561        sqe.addr = addr as _;
562        sqe.__bindgen_anon_1.off = addrlen as _;
563        Entry(sqe)
564    }
565);