lio_uring/operation.rs
1//! io_uring operations
2//!
3//! This module defines all supported io_uring operations. Each operation implements
4//! the `UringOperation` trait which allows it to be submitted to an io_uring instance.
5//!
6//! # Safety
7//!
8//! All operations contain raw pointers and file descriptors. When submitting operations,
9//! you must ensure that:
10//! - All pointers remain valid until the operation completes
11//! - Buffers are not accessed mutably while operations are in flight
12//! - File descriptors remain valid until operations complete
13//!
14//! # Examples
15//!
16//! ```rust
17//! use lio_uring::{LioUring, operation::Write};
18//! use std::os::fd::AsRawFd;
19//!
20//! # fn main() -> std::io::Result<()> {
21//! let mut ring = LioUring::new(32)?;
22//!
23//! let data = b"Hello, io_uring!";
24//! let file = std::fs::File::create("/tmp/test")?;
25//!
26//! let op = Write::new(file.as_raw_fd(), data.as_ptr(), data.len() as u32);
27//!
28//! unsafe { ring.push(op.build(), 1) }?;
29//! ring.submit()?;
30//!
31//! let completion = ring.wait()?;
32//! assert_eq!(completion.result(), data.len() as i32);
33//! # Ok(())
34//! # }
35//! ```
36
37use core::mem;
38use std::os::fd::RawFd;
39
40use crate::{bindings, Entry, SqeFlags};
41
42macro_rules! opcode {
43 (@type $name:ty ) => {
44 $name
45 };
46 (
47 $( #[$outer:meta] )*
48 pub struct $name:ident {
49 $( #[$new_meta:meta] )*
50
51 $( $field:ident : { $( $tnt:tt )+ } ),*
52
53 $(,)?
54
55 ;;
56
57 $(
58 $( #[$opt_meta:meta] )*
59 $opt_field:ident : $opt_tname:ty = $default:expr
60 ),*
61
62 $(,)?
63 }
64
65 pub const CODE = $opcode:expr;
66
67 $( #[$build_meta:meta] )*
68 pub fn build($self:ident) -> $entry:ty $build_block:block
69 ) => {
70 $( #[$outer] )*
71 pub struct $name {
72 $( $field : opcode!(@type $( $tnt )*), )*
73 $( $opt_field : $opt_tname, )*
74 }
75
76 impl $name {
77 $( #[$new_meta] )*
78 #[inline]
79 pub fn new($( $field : $( $tnt )* ),*) -> Self {
80 $name {
81 $( $field: $field.into(), )*
82 $( $opt_field: $default, )*
83 }
84 }
85
86 /// The opcode of the operation. This can be passed to
87 /// [`Probe::is_supported`](crate::Probe::is_supported) to check if this operation is
88 /// supported with the current kernel.
89 pub const CODE: u8 = $opcode as _;
90
91 $(
92 $( #[$opt_meta] )*
93 #[inline]
94 pub const fn $opt_field(mut self, $opt_field: $opt_tname) -> Self {
95 self.$opt_field = $opt_field;
96 self
97 }
98 )*
99
100 $( #[$build_meta] )*
101 #[inline]
102 pub fn build($self) -> $entry $build_block
103 }
104 }
105}
106
107/// inline zeroed to improve codegen
108#[inline(always)]
109fn sqe_zeroed() -> bindings::io_uring_sqe {
110 unsafe { mem::zeroed() }
111}
112
113opcode! {
114 /// Do not perform any I/O.
115 ///
116 /// This is useful for testing the performance of the io_uring implementation itself.
117 #[derive(Debug)]
118 pub struct Nop { ;; }
119
120 pub const CODE = bindings::io_uring_op_IORING_OP_NOP;
121
122 pub fn build(self) -> Entry {
123 let Nop {} = self;
124
125 let mut sqe = sqe_zeroed();
126 sqe.opcode = Self::CODE;
127 sqe.fd = -1;
128 Entry(sqe)
129 }
130}
131
132opcode! {
133 /// Vectored read, equivalent to `preadv2(2)`.
134 #[derive(Debug)]
135 pub struct Readv {
136 fd: { RawFd },
137 iovec: { *const libc::iovec },
138 len: { u32 },
139 ;;
140 ioprio: u16 = 0,
141 offset: u64 = 0,
142 /// specified for read operations, contains a bitwise OR of per-I/O flags,
143 /// as described in the `preadv2(2)` man page.
144 rw_flags: i32 = 0,
145 buf_group: u16 = 0
146 }
147
148 pub const CODE = bindings::io_uring_op_IORING_OP_READV;
149
150 pub fn build(self) -> Entry {
151 let Readv {
152 fd,
153 iovec, len, offset,
154 ioprio, rw_flags,
155 buf_group
156 } = self;
157
158 let mut sqe = sqe_zeroed();
159 sqe.opcode = Self::CODE;
160 sqe.fd = fd;
161 sqe.ioprio = ioprio;
162 sqe.__bindgen_anon_2.addr = iovec as _;
163 sqe.len = len;
164 sqe.__bindgen_anon_1.off = offset;
165 sqe.__bindgen_anon_3.rw_flags = rw_flags as _;
166 sqe.__bindgen_anon_4.buf_group = buf_group;
167 Entry(sqe)
168 }
169}
170
171opcode! {
172 /// Vectored write, equivalent to `pwritev2(2)`.
173 #[derive(Debug)]
174 pub struct Writev {
175 fd: { RawFd },
176 iovec: { *const libc::iovec },
177 len: { u32 },
178 ;;
179 ioprio: u16 = 0,
180 offset: u64 = 0,
181 /// specified for write operations, contains a bitwise OR of per-I/O flags,
182 /// as described in the `preadv2(2)` man page.
183 rw_flags: i32 = 0
184 }
185
186 pub const CODE = bindings::io_uring_op_IORING_OP_WRITEV;
187
188 pub fn build(self) -> Entry {
189 let Writev {
190 fd,
191 iovec, len, offset,
192 ioprio, rw_flags
193 } = self;
194
195 let mut sqe = sqe_zeroed();
196 sqe.opcode = Self::CODE;
197 sqe.fd = fd;
198 sqe.ioprio = ioprio;
199 sqe.__bindgen_anon_2.addr = iovec as _;
200 sqe.len = len;
201 sqe.__bindgen_anon_1.off = offset;
202 sqe.__bindgen_anon_3.rw_flags = rw_flags as _;
203 Entry(sqe)
204 }
205}
206
207/// Options for [`Fsync`](super::Fsync).
208#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
209pub struct FsyncFlags(u32);
210
211impl FsyncFlags {
212 const DATASYNC: Self = Self(bindings::IORING_FSYNC_DATASYNC);
213}
214
215impl FsyncFlags {
216 pub fn empty() -> Self {
217 Self(0)
218 }
219 fn bits(&self) -> u32 {
220 self.0
221 }
222}
223
224opcode! {
225 /// File sync, equivalent to `fsync(2)`.
226 ///
227 /// Note that, while I/O is initiated in the order in which it appears in the submission queue,
228 /// completions are unordered. For example, an application which places a write I/O followed by
229 /// an fsync in the submission queue cannot expect the fsync to apply to the write. The two
230 /// operations execute in parallel, so the fsync may complete before the write is issued to the
231 /// storage. The same is also true for previously issued writes that have not completed prior to
232 /// the fsync.
233 #[derive(Debug)]
234 pub struct Fsync {
235 fd: { RawFd },
236 ;;
237 /// The `flags` bit mask may contain either 0, for a normal file integrity sync,
238 /// or [types::FsyncFlags::DATASYNC] to provide data sync only semantics.
239 /// See the descriptions of `O_SYNC` and `O_DSYNC` in the `open(2)` manual page for more information.
240 flags: FsyncFlags = FsyncFlags::empty()
241 }
242
243 pub const CODE = bindings::io_uring_op_IORING_OP_FSYNC;
244
245 pub fn build(self) -> Entry {
246 let Fsync { fd, flags } = self;
247
248 let mut sqe = sqe_zeroed();
249 sqe.opcode = Self::CODE;
250 sqe.fd = fd;
251 sqe.__bindgen_anon_3.fsync_flags = flags.bits();
252 Entry(sqe)
253 }
254}
255
256opcode! {
257 /// Read from a file into a fixed buffer that has been previously registered with
258 /// [`Submitter::register_buffers`](crate::Submitter::register_buffers).
259 ///
260 /// The return values match those documented in the `preadv2(2)` man pages.
261 #[derive(Debug)]
262 pub struct ReadFixed {
263 fd: { RawFd },
264 buf: { *mut u8 },
265 len: { u32 },
266 buf_index: { u16 },
267 ;;
268 ioprio: u16 = 0,
269 /// The offset of the file to read from.
270 offset: u64 = 0,
271 /// Specified for read operations, contains a bitwise OR of per-I/O flags, as described in
272 /// the `preadv2(2)` man page.
273 rw_flags: i32 = 0
274 }
275
276 pub const CODE = bindings::io_uring_op_IORING_OP_READ_FIXED;
277
278 pub fn build(self) -> Entry {
279 let ReadFixed {
280 fd,
281 buf, len, offset,
282 buf_index,
283 ioprio, rw_flags
284 } = self;
285
286 let mut sqe = sqe_zeroed();
287 sqe.opcode = Self::CODE;
288 sqe.fd = fd;
289 sqe.ioprio = ioprio;
290 sqe.__bindgen_anon_2.addr = buf as _;
291 sqe.len = len;
292 sqe.__bindgen_anon_1.off = offset;
293 sqe.__bindgen_anon_3.rw_flags = rw_flags as _;
294 sqe.__bindgen_anon_4.buf_index = buf_index;
295 Entry(sqe)
296 }
297}
298
299opcode! {
300 /// Write to a file from a fixed buffer that have been previously registered with
301 /// [`Submitter::register_buffers`](crate::Submitter::register_buffers).
302 ///
303 /// The return values match those documented in the `pwritev2(2)` man pages.
304 #[derive(Debug)]
305 pub struct WriteFixed {
306 fd: { RawFd },
307 buf: { *const u8 },
308 len: { u32 },
309 buf_index: { u16 },
310 ;;
311 ioprio: u16 = 0,
312 /// The offset of the file to write to.
313 offset: u64 = 0,
314 /// Specified for write operations, contains a bitwise OR of per-I/O flags, as described in
315 /// the `pwritev2(2)` man page.
316 rw_flags: i32 = 0
317 }
318
319 pub const CODE = bindings::io_uring_op_IORING_OP_WRITE_FIXED;
320
321 pub fn build(self) -> Entry {
322 let WriteFixed {
323 fd,
324 buf, len, offset,
325 buf_index,
326 ioprio, rw_flags
327 } = self;
328
329 let mut sqe = sqe_zeroed();
330 sqe.opcode = Self::CODE;
331 sqe.fd = fd;
332 sqe.ioprio = ioprio;
333 sqe.__bindgen_anon_2.addr = buf as _;
334 sqe.len = len;
335 sqe.__bindgen_anon_1.off = offset;
336 sqe.__bindgen_anon_3.rw_flags = rw_flags as _;
337 sqe.__bindgen_anon_4.buf_index = buf_index;
338 Entry(sqe)
339 }
340}
341
342opcode! {
343 /// Poll the specified fd.
344 ///
345 /// Unlike poll or epoll without `EPOLLONESHOT`, this interface defaults to work in one shot mode.
346 /// That is, once the poll operation is completed, it will have to be resubmitted.
347 ///
348 /// If multi is set, the poll will work in multi shot mode instead. That means it will
349 /// repeatedly trigger when the requested event becomes true, and hence multiple CQEs can be
350 /// generated from this single submission. The CQE flags field will have IORING_CQE_F_MORE set
351 /// on completion if the application should expect further CQE entries from the original
352 /// request. If this flag isn't set on completion, then the poll request has been terminated
353 /// and no further events will be generated. This mode is available since 5.13.
354 #[derive(Debug)]
355 pub struct PollAdd {
356 /// The bits that may be set in `flags` are defined in `<poll.h>`,
357 /// and documented in `poll(2)`.
358 fd: { RawFd },
359 flags: { u32 },
360 ;;
361 multi: bool = false
362 }
363
364 pub const CODE = bindings::io_uring_op_IORING_OP_POLL_ADD;
365
366 pub fn build(self) -> Entry {
367 let PollAdd { fd, flags, multi } = self;
368
369 let mut sqe = sqe_zeroed();
370 sqe.opcode = Self::CODE;
371 sqe.fd = fd;
372 if multi {
373 sqe.len = bindings::IORING_POLL_ADD_MULTI;
374 }
375
376 #[cfg(target_endian = "little")] {
377 sqe.__bindgen_anon_3.poll32_events = flags;
378 }
379
380 #[cfg(target_endian = "big")] {
381 let x = flags << 16;
382 let y = flags >> 16;
383 let flags = x | y;
384 sqe.__bindgen_anon_3.poll32_events = flags;
385 }
386
387 Entry(sqe)
388 }
389}
390
391opcode! {
392 /// Remove an existing [poll](PollAdd) request.
393 ///
394 /// If found, the `result` method of the `cqueue::Entry` will return 0.
395 /// If not found, `result` will return `-libc::ENOENT`.
396 #[derive(Debug)]
397 pub struct PollRemove {
398 user_data: { u64 }
399 ;;
400 }
401
402 pub const CODE = bindings::io_uring_op_IORING_OP_POLL_REMOVE;
403
404 pub fn build(self) -> Entry {
405 let PollRemove { user_data } = self;
406
407 let mut sqe = sqe_zeroed();
408 sqe.opcode = Self::CODE;
409 sqe.fd = -1;
410 sqe.__bindgen_anon_2.addr = user_data;
411 Entry(sqe)
412 }
413}
414
415opcode! {
416 /// Sync a file segment with disk, equivalent to `sync_file_range(2)`.
417 #[derive(Debug)]
418 pub struct SyncFileRange {
419 fd: { RawFd },
420 len: { u32 },
421 ;;
422 /// the offset method holds the offset in bytes
423 offset: u64 = 0,
424 /// the flags method holds the flags for the command
425 flags: u32 = 0
426 }
427
428 pub const CODE = bindings::io_uring_op_IORING_OP_SYNC_FILE_RANGE;
429
430 pub fn build(self) -> Entry {
431 let SyncFileRange {
432 fd,
433 len, offset,
434 flags
435 } = self;
436
437 let mut sqe = sqe_zeroed();
438 sqe.opcode = Self::CODE;
439 sqe.fd = fd;
440 sqe.len = len;
441 sqe.__bindgen_anon_1.off = offset;
442 sqe.__bindgen_anon_3.sync_range_flags = flags;
443 Entry(sqe)
444 }
445}
446
447opcode! {
448 /// Send a message on a socket, equivalent to `send(2)`.
449 ///
450 /// fd must be set to the socket file descriptor, addr must contains a pointer to the msghdr
451 /// structure, and flags holds the flags associated with the system call.
452 #[derive(Debug)]
453 pub struct SendMsg {
454 fd: { RawFd },
455 msg: { *const libc::msghdr },
456 ;;
457 ioprio: u16 = 0,
458 flags: u32 = 0
459 }
460
461 pub const CODE = bindings::io_uring_op_IORING_OP_SENDMSG;
462
463 pub fn build(self) -> Entry {
464 let SendMsg { fd, msg, ioprio, flags } = self;
465
466 let mut sqe = sqe_zeroed();
467 sqe.opcode = Self::CODE;
468 sqe.fd = fd;
469 sqe.ioprio = ioprio;
470 sqe.__bindgen_anon_2.addr = msg as _;
471 sqe.len = 1;
472 sqe.__bindgen_anon_3.msg_flags = flags;
473 Entry(sqe)
474 }
475}
476
477opcode! {
478 /// Receive a message on a socket, equivalent to `recvmsg(2)`.
479 ///
480 /// See also the description of [`SendMsg`].
481 #[derive(Debug)]
482 pub struct RecvMsg {
483 fd: { RawFd },
484 msg: { *mut libc::msghdr },
485 ;;
486 ioprio: u16 = 0,
487 flags: u32 = 0,
488 buf_group: u16 = 0
489 }
490
491 pub const CODE = bindings::io_uring_op_IORING_OP_RECVMSG;
492
493 pub fn build(self) -> Entry {
494 let RecvMsg { fd, msg, ioprio, flags, buf_group } = self;
495
496 let mut sqe = sqe_zeroed();
497 sqe.opcode = Self::CODE;
498 sqe.fd = fd;
499 sqe.ioprio = ioprio;
500 sqe.__bindgen_anon_2.addr = msg as _;
501 sqe.len = 1;
502 sqe.__bindgen_anon_3.msg_flags = flags;
503 sqe.__bindgen_anon_4.buf_group = buf_group;
504 Entry(sqe)
505 }
506}
507
508// TODO
509opcode! {
510 /// Receive multiple messages on a socket, equivalent to `recvmsg(2)`.
511 ///
512 /// Parameters:
513 /// msg: For this multishot variant of ResvMsg, only the msg_namelen and msg_controllen
514 /// fields are relevant.
515 /// buf_group: The id of the provided buffer pool to use for each received message.
516 ///
517 /// See also the description of [`SendMsg`] and [`types::RecvMsgOut`].
518 ///
519 /// The multishot version allows the application to issue a single receive request, which
520 /// repeatedly posts a CQE when data is available. It requires the MSG_WAITALL flag is not set.
521 /// Each CQE will take a buffer out of a provided buffer pool for receiving. The application
522 /// should check the flags of each CQE, regardless of its result. If a posted CQE does not have
523 /// the IORING_CQE_F_MORE flag set then the multishot receive will be done and the application
524 /// should issue a new request.
525 ///
526 /// Unlike [`RecvMsg`], this multishot recvmsg will prepend a struct which describes the layout
527 /// of the rest of the buffer in combination with the initial msghdr structure submitted with
528 /// the request. Use [`types::RecvMsgOut`] to parse the data received and access its
529 /// components.
530 ///
531 /// The recvmsg multishot variant is available since kernel 6.0.
532 #[derive(Debug)]
533 pub struct RecvMsgMulti {
534 fd: { RawFd },
535 msg: { *const libc::msghdr },
536 buf_group: { u16 },
537 ;;
538 ioprio: u16 = 0,
539 flags: u32 = 0
540 }
541
542 pub const CODE = bindings::io_uring_op_IORING_OP_RECVMSG;
543
544 pub fn build(self) -> Entry {
545 let RecvMsgMulti { fd, msg, buf_group, ioprio, flags } = self;
546
547 let mut sqe = sqe_zeroed();
548 sqe.opcode = Self::CODE;
549 sqe.fd = fd;
550 sqe.__bindgen_anon_2.addr = msg as _;
551 sqe.len = 1;
552 sqe.__bindgen_anon_3.msg_flags = flags;
553 sqe.__bindgen_anon_4.buf_group = buf_group;
554 sqe.flags |= SqeFlags::BUFFER_SELECT.bits();
555 sqe.ioprio = ioprio | (bindings::IORING_RECV_MULTISHOT as u16);
556 Entry(sqe)
557 }
558}
559
560#[derive(Debug, Clone, Copy)]
561pub struct TimeoutFlags(u32);
562
563/// Options for [`Timeout`](super::Timeout).
564///
565/// The default behavior is to treat the timespec as a relative time interval. `flags` may
566/// contain [`TimeoutFlags::ABS`] to indicate the timespec represents an absolute
567/// time. When an absolute time is being specified, the kernel will use its monotonic clock
568/// unless one of the following flags is set (they may not both be set):
569/// [`TimeoutFlags::BOOTTIME`] or [`TimeoutFlags::REALTIME`].
570///
571/// The default behavior when the timeout expires is to sever dependent links, as a failed
572/// request normally would. To keep the links untouched include [`TimeoutFlags::ETIME_SUCCESS`].
573/// CQE will still contain -libc::ETIME in the res field
574impl TimeoutFlags {
575 const ABS: Self = Self(bindings::IORING_TIMEOUT_ABS);
576
577 const BOOTTIME: Self = Self(bindings::IORING_TIMEOUT_BOOTTIME);
578
579 const REALTIME: Self = Self(bindings::IORING_TIMEOUT_REALTIME);
580
581 const LINK_TIMEOUT_UPDATE: Self = Self(bindings::IORING_LINK_TIMEOUT_UPDATE);
582
583 const ETIME_SUCCESS: Self = Self(bindings::IORING_TIMEOUT_ETIME_SUCCESS);
584
585 const MULTISHOT: Self = Self(bindings::IORING_TIMEOUT_MULTISHOT);
586
587 pub fn empty() -> Self {
588 Self(0)
589 }
590 pub fn bits(self) -> u32 {
591 self.0
592 }
593}
594
595opcode! {
596 /// Register a timeout operation.
597 ///
598 /// A timeout will trigger a wakeup event on the completion ring for anyone waiting for events.
599 /// A timeout condition is met when either the specified timeout expires, or the specified number of events have completed.
600 /// Either condition will trigger the event.
601 /// The request will complete with `-ETIME` if the timeout got completed through expiration of the timer,
602 /// or 0 if the timeout got completed through requests completing on their own.
603 /// If the timeout was cancelled before it expired, the request will complete with `-ECANCELED`.
604 #[derive(Debug)]
605 pub struct Timeout {
606 timespec: { *const bindings::__kernel_timespec },
607 ;;
608 /// `count` may contain a completion event count.
609 /// If [`TimeoutFlags::MULTISHOT`](types::TimeoutFlags::MULTISHOT) is set in `flags`, this is the number of repeats.
610 /// A value of 0 means the timeout is indefinite and can only be stopped by a removal request.
611 count: u32 = 0,
612
613 flags: TimeoutFlags = TimeoutFlags::empty()
614 }
615
616 pub const CODE = bindings::io_uring_op_IORING_OP_TIMEOUT;
617
618 pub fn build(self) -> Entry {
619 let Timeout { timespec, count, flags } = self;
620
621 let mut sqe = sqe_zeroed();
622 sqe.opcode = Self::CODE;
623 sqe.fd = -1;
624 sqe.__bindgen_anon_2.addr = timespec as _;
625 sqe.len = 1;
626 sqe.__bindgen_anon_1.off = count as _;
627 sqe.__bindgen_anon_3.timeout_flags = flags.bits();
628 Entry(sqe)
629 }
630}
631
632// === 5.5 ===
633
634opcode! {
635 /// Attempt to remove an existing [timeout operation](Timeout).
636 pub struct TimeoutRemove {
637 user_data: { u64 },
638 ;;
639 }
640
641 pub const CODE = bindings::io_uring_op_IORING_OP_TIMEOUT_REMOVE;
642
643 pub fn build(self) -> Entry {
644 let TimeoutRemove { user_data } = self;
645
646 let mut sqe = sqe_zeroed();
647 sqe.opcode = Self::CODE;
648 sqe.fd = -1;
649 sqe.__bindgen_anon_2.addr = user_data;
650 Entry(sqe)
651 }
652}
653
654opcode! {
655 /// Attempt to update an existing [timeout operation](Timeout) with a new timespec.
656 /// The optional `count` value of the original timeout value cannot be updated.
657 pub struct TimeoutUpdate {
658 user_data: { u64 },
659 timespec: { *const bindings::__kernel_timespec },
660 ;;
661 flags: TimeoutFlags = TimeoutFlags::empty()
662 }
663
664 pub const CODE = bindings::io_uring_op_IORING_OP_TIMEOUT_REMOVE;
665
666 pub fn build(self) -> Entry {
667 let TimeoutUpdate { user_data, timespec, flags } = self;
668
669 let mut sqe = sqe_zeroed();
670 sqe.opcode = Self::CODE;
671 sqe.fd = -1;
672 sqe.__bindgen_anon_1.off = timespec as _;
673 sqe.__bindgen_anon_2.addr = user_data;
674 sqe.__bindgen_anon_3.timeout_flags = flags.bits() | bindings::IORING_TIMEOUT_UPDATE;
675 Entry(sqe)
676 }
677}
678
679opcode! {
680 /// Accept a new connection on a socket, equivalent to `accept4(2)`.
681 pub struct Accept {
682 fd: { RawFd },
683 addr: { *mut libc::sockaddr },
684 addrlen: { *mut libc::socklen_t },
685 ;;
686 flags: i32 = 0
687 }
688
689 pub const CODE = bindings::io_uring_op_IORING_OP_ACCEPT;
690
691 pub fn build(self) -> Entry {
692 let Accept { fd, addr, addrlen, flags } = self;
693
694 let mut sqe = sqe_zeroed();
695 sqe.opcode = Self::CODE;
696 sqe.fd = fd;
697 sqe.__bindgen_anon_2.addr = addr as _;
698 sqe.__bindgen_anon_1.addr2 = addrlen as _;
699 sqe.__bindgen_anon_3.accept_flags = flags as _;
700 Entry(sqe)
701 }
702}
703
704// opcode! {
705// /// Set a socket option.
706// pub struct SetSockOpt {
707// fd: { RawFd },
708// level: { u32 },
709// optname: { u32 },
710// optval: { *const libc::c_void },
711// optlen: { u32 },
712// ;;
713// flags: u32 = 0
714// }
715//
716// pub const CODE = bindings::io_uring_op_IORING_OP_URING_CMD;
717//
718// pub fn build(self) -> Entry {
719// let SetSockOpt { fd, level, optname, optval, optlen, flags } = self;
720// let mut sqe = sqe_zeroed();
721// sqe.opcode = Self::CODE;
722// sqe.fd = fd;
723// sqe.__bindgen_anon_1.__bindgen_anon_1.cmd_op = bindings::SOCKET_URING_OP_SETSOCKOPT;
724//
725// sqe.__bindgen_anon_2.__bindgen_anon_1.level = level;
726// sqe.__bindgen_anon_2.__bindgen_anon_1.optname = optname;
727// sqe.__bindgen_anon_3.uring_cmd_flags = flags;
728// sqe.__bindgen_anon_5.optlen = optlen;
729// unsafe { *sqe.__bindgen_anon_6.optval.as_mut() = optval as u64 };
730// Entry(sqe)
731// }
732// }
733
734opcode! {
735 /// Attempt to cancel an already issued request.
736 pub struct AsyncCancel {
737 user_data: { u64 }
738 ;;
739
740 // TODO flags
741 }
742
743 pub const CODE = bindings::io_uring_op_IORING_OP_ASYNC_CANCEL;
744
745 pub fn build(self) -> Entry {
746 let AsyncCancel { user_data } = self;
747
748 let mut sqe = sqe_zeroed();
749 sqe.opcode = Self::CODE;
750 sqe.fd = -1;
751 sqe.__bindgen_anon_2.addr = user_data;
752 Entry(sqe)
753 }
754}
755
756opcode! {
757 /// This request must be linked with another request through
758 /// [`Flags::IO_LINK`](SqeFlags::IO_LINK) which is described below.
759 /// Unlike [`Timeout`], [`LinkTimeout`] acts on the linked request, not the completion queue.
760 pub struct LinkTimeout {
761 timespec: { *const bindings::__kernel_timespec },
762 ;;
763 flags: TimeoutFlags = TimeoutFlags::empty()
764 }
765
766 pub const CODE = bindings::io_uring_op_IORING_OP_LINK_TIMEOUT;
767
768 pub fn build(self) -> Entry {
769 let LinkTimeout { timespec, flags } = self;
770
771 let mut sqe = sqe_zeroed();
772 sqe.opcode = Self::CODE;
773 sqe.fd = -1;
774 sqe.__bindgen_anon_2.addr = timespec as _;
775 sqe.len = 1;
776 sqe.__bindgen_anon_3.timeout_flags = flags.bits();
777 Entry(sqe)
778 }
779}
780
781opcode! {
782 /// Connect a socket, equivalent to `connect(2)`.
783 pub struct Connect {
784 fd: { RawFd },
785 addr: { *const libc::sockaddr },
786 addrlen: { libc::socklen_t }
787 ;;
788 }
789
790 pub const CODE = bindings::io_uring_op_IORING_OP_CONNECT;
791
792 pub fn build(self) -> Entry {
793 let Connect { fd, addr, addrlen } = self;
794
795 let mut sqe = sqe_zeroed();
796 sqe.opcode = Self::CODE;
797 sqe.fd = fd;
798 sqe.__bindgen_anon_2.addr = addr as _;
799 sqe.__bindgen_anon_1.off = addrlen as _;
800 Entry(sqe)
801 }
802}
803
804// === 5.6 ===
805
806opcode! {
807 /// Preallocate or deallocate space to a file, equivalent to `fallocate(2)`.
808 pub struct Fallocate {
809 fd: { RawFd },
810 len: { u64 },
811 ;;
812 offset: u64 = 0,
813 mode: i32 = 0
814 }
815
816 pub const CODE = bindings::io_uring_op_IORING_OP_FALLOCATE;
817
818 pub fn build(self) -> Entry {
819 let Fallocate { fd, len, offset, mode } = self;
820
821 let mut sqe = sqe_zeroed();
822 sqe.opcode = Self::CODE;
823 sqe.fd = fd;
824 sqe.__bindgen_anon_2.addr = len;
825 sqe.len = mode as _;
826 sqe.__bindgen_anon_1.off = offset;
827 Entry(sqe)
828 }
829}
830
831opcode! {
832 /// Open a file, equivalent to `openat(2)`.
833 pub struct OpenAt {
834 dirfd: { RawFd },
835 pathname: { *const libc::c_char },
836 ;;
837 flags: i32 = 0,
838 mode: libc::mode_t = 0
839 }
840
841 pub const CODE = bindings::io_uring_op_IORING_OP_OPENAT;
842
843 pub fn build(self) -> Entry {
844 let OpenAt { dirfd, pathname, flags, mode } = self;
845
846 let mut sqe = sqe_zeroed();
847 sqe.opcode = Self::CODE;
848 sqe.fd = dirfd;
849 sqe.__bindgen_anon_2.addr = pathname as _;
850 sqe.len = mode;
851 sqe.__bindgen_anon_3.open_flags = flags as _;
852 Entry(sqe)
853 }
854}
855
856opcode! {
857 /// Close a file descriptor, equivalent to `close(2)`.
858 ///
859 /// Use a types::Fixed(fd) argument to close an io_uring direct descriptor.
860 pub struct Close {
861 fd: { RawFd },
862 ;;
863 }
864
865 pub const CODE = bindings::io_uring_op_IORING_OP_CLOSE;
866
867 pub fn build(self) -> Entry {
868 let Close { fd } = self;
869
870 let mut sqe = sqe_zeroed();
871 sqe.opcode = Self::CODE;
872 sqe.fd = fd;
873 Entry(sqe)
874 }
875}
876
877opcode! {
878 /// This command is an alternative to using
879 /// [`Submitter::register_files_update`](crate::Submitter::register_files_update) which then
880 /// works in an async fashion, like the rest of the io_uring commands.
881 pub struct FilesUpdate {
882 fds: { *const RawFd },
883 len: { u32 },
884 ;;
885 offset: i32 = 0
886 }
887
888 pub const CODE = bindings::io_uring_op_IORING_OP_FILES_UPDATE;
889
890 pub fn build(self) -> Entry {
891 let FilesUpdate { fds, len, offset } = self;
892
893 let mut sqe = sqe_zeroed();
894 sqe.opcode = Self::CODE;
895 sqe.fd = -1;
896 sqe.__bindgen_anon_2.addr = fds as _;
897 sqe.len = len;
898 sqe.__bindgen_anon_1.off = offset as _;
899 Entry(sqe)
900 }
901}
902
903opcode! {
904 /// Get file status, equivalent to `statx(2)`.
905 pub struct Statx {
906 dirfd: { RawFd },
907 pathname: { *const libc::c_char },
908 statxbuf: { *mut libc::statx },
909 ;;
910 flags: i32 = 0,
911 mask: u32 = 0
912 }
913
914 pub const CODE = bindings::io_uring_op_IORING_OP_STATX;
915
916 pub fn build(self) -> Entry {
917 let Statx {
918 dirfd, pathname, statxbuf,
919 flags, mask
920 } = self;
921
922 let mut sqe = sqe_zeroed();
923 sqe.opcode = Self::CODE;
924 sqe.fd = dirfd;
925 sqe.__bindgen_anon_2.addr = pathname as _;
926 sqe.len = mask;
927 sqe.__bindgen_anon_1.off = statxbuf as _;
928 sqe.__bindgen_anon_3.statx_flags = flags as _;
929 Entry(sqe)
930 }
931}
932
933opcode! {
934 /// Issue the equivalent of a `pread(2)` or `pwrite(2)` system call
935 ///
936 /// * `fd` is the file descriptor to be operated on,
937 /// * `addr` contains the buffer in question,
938 /// * `len` contains the length of the IO operation,
939 ///
940 /// These are non-vectored versions of the `IORING_OP_READV` and `IORING_OP_WRITEV` opcodes.
941 /// See also `read(2)` and `write(2)` for the general description of the related system call.
942 ///
943 /// Available since 5.6.
944 pub struct Read {
945 fd: { RawFd },
946 buf: { *mut u8 },
947 len: { u32 },
948 ;;
949 /// `offset` contains the read or write offset.
950 ///
951 /// If `fd` does not refer to a seekable file, `offset` must be set to zero.
952 /// If `offset` is set to `-1`, the offset will use (and advance) the file position,
953 /// like the `read(2)` and `write(2)` system calls.
954 offset: u64 = 0,
955 ioprio: u16 = 0,
956 rw_flags: i32 = 0,
957 buf_group: u16 = 0
958 }
959
960 pub const CODE = bindings::io_uring_op_IORING_OP_READ;
961
962 pub fn build(self) -> Entry {
963 let Read {
964 fd,
965 buf, len, offset,
966 ioprio, rw_flags,
967 buf_group
968 } = self;
969
970 let mut sqe = sqe_zeroed();
971 sqe.opcode = Self::CODE;
972 sqe.fd = fd;
973 sqe.ioprio = ioprio;
974 sqe.__bindgen_anon_2.addr = buf as _;
975 sqe.len = len;
976 sqe.__bindgen_anon_1.off = offset;
977 sqe.__bindgen_anon_3.rw_flags = rw_flags as _;
978 sqe.__bindgen_anon_4.buf_group = buf_group;
979 Entry(sqe)
980 }
981}
982
983opcode! {
984 /// Issue the equivalent of a `pread(2)` or `pwrite(2)` system call
985 ///
986 /// * `fd` is the file descriptor to be operated on,
987 /// * `addr` contains the buffer in question,
988 /// * `len` contains the length of the IO operation,
989 ///
990 /// These are non-vectored versions of the `IORING_OP_READV` and `IORING_OP_WRITEV` opcodes.
991 /// See also `read(2)` and `write(2)` for the general description of the related system call.
992 ///
993 /// Available since 5.6.
994 pub struct Write {
995 fd: { RawFd },
996 buf: { *const u8 },
997 len: { u32 },
998 ;;
999 /// `offset` contains the read or write offset.
1000 ///
1001 /// If `fd` does not refer to a seekable file, `offset` must be set to zero.
1002 /// If `offsett` is set to `-1`, the offset will use (and advance) the file position,
1003 /// like the `read(2)` and `write(2)` system calls.
1004 offset: u64 = 0,
1005 ioprio: u16 = 0,
1006 rw_flags: i32 = 0
1007 }
1008
1009 pub const CODE = bindings::io_uring_op_IORING_OP_WRITE;
1010
1011 pub fn build(self) -> Entry {
1012 let Write {
1013 fd,
1014 buf, len, offset,
1015 ioprio, rw_flags
1016 } = self;
1017
1018 let mut sqe = sqe_zeroed();
1019 sqe.opcode = Self::CODE;
1020 sqe.fd = fd;
1021 sqe.ioprio = ioprio;
1022 sqe.__bindgen_anon_2.addr = buf as _;
1023 sqe.len = len;
1024 sqe.__bindgen_anon_1.off = offset;
1025 sqe.__bindgen_anon_3.rw_flags = rw_flags as _;
1026 Entry(sqe)
1027 }
1028}
1029
1030opcode! {
1031 /// Predeclare an access pattern for file data, equivalent to `posix_fadvise(2)`.
1032 pub struct Fadvise {
1033 fd: { RawFd },
1034 len: { libc::off_t },
1035 advice: { i32 },
1036 ;;
1037 offset: u64 = 0,
1038 }
1039
1040 pub const CODE = bindings::io_uring_op_IORING_OP_FADVISE;
1041
1042 pub fn build(self) -> Entry {
1043 let Fadvise { fd, len, advice, offset } = self;
1044
1045 let mut sqe = sqe_zeroed();
1046 sqe.opcode = Self::CODE;
1047 sqe.fd = fd;
1048 sqe.len = len as _;
1049 sqe.__bindgen_anon_1.off = offset;
1050 sqe.__bindgen_anon_3.fadvise_advice = advice as _;
1051 Entry(sqe)
1052 }
1053}
1054
1055opcode! {
1056 /// Give advice about use of memory, equivalent to `madvise(2)`.
1057 pub struct Madvise {
1058 addr: { *const libc::c_void },
1059 len: { libc::off_t },
1060 advice: { i32 },
1061 ;;
1062 }
1063
1064 pub const CODE = bindings::io_uring_op_IORING_OP_MADVISE;
1065
1066 pub fn build(self) -> Entry {
1067 let Madvise { addr, len, advice } = self;
1068
1069 let mut sqe = sqe_zeroed();
1070 sqe.opcode = Self::CODE;
1071 sqe.fd = -1;
1072 sqe.__bindgen_anon_2.addr = addr as _;
1073 sqe.len = len as _;
1074 sqe.__bindgen_anon_3.fadvise_advice = advice as _;
1075 Entry(sqe)
1076 }
1077}
1078
1079opcode! {
1080 /// Send a message on a socket, equivalent to `send(2)`.
1081 pub struct Send {
1082 fd: { RawFd },
1083 buf: { *const u8 },
1084 len: { u32 },
1085 ;;
1086 flags: i32 = 0,
1087
1088 /// Set the destination address, for sending from an unconnected socket.
1089 ///
1090 /// When set, `dest_addr_len` must be set as well.
1091 /// See also `man 3 io_uring_prep_send_set_addr`.
1092 dest_addr: *const libc::sockaddr = core::ptr::null(),
1093 dest_addr_len: libc::socklen_t = 0,
1094 }
1095
1096 pub const CODE = bindings::io_uring_op_IORING_OP_SEND;
1097
1098 pub fn build(self) -> Entry {
1099 let Send { fd, buf, len, flags, dest_addr, dest_addr_len } = self;
1100
1101 let mut sqe = sqe_zeroed();
1102 sqe.opcode = Self::CODE;
1103 sqe.fd = fd;
1104 sqe.__bindgen_anon_2.addr = buf as _;
1105 sqe.__bindgen_anon_1.addr2 = dest_addr as _;
1106 sqe.__bindgen_anon_5.__bindgen_anon_1.addr_len = dest_addr_len as _;
1107 sqe.len = len;
1108 sqe.__bindgen_anon_3.msg_flags = flags as _;
1109 Entry(sqe)
1110 }
1111}
1112
1113opcode! {
1114 /// Receive a message from a socket, equivalent to `recv(2)`.
1115 pub struct Recv {
1116 fd: { RawFd },
1117 buf: { *mut u8 },
1118 len: { u32 },
1119 ;;
1120 flags: i32 = 0,
1121 buf_group: u16 = 0
1122 }
1123
1124 pub const CODE = bindings::io_uring_op_IORING_OP_RECV;
1125
1126 pub fn build(self) -> Entry {
1127 let Recv { fd, buf, len, flags, buf_group } = self;
1128
1129 let mut sqe = sqe_zeroed();
1130 sqe.opcode = Self::CODE;
1131 sqe.fd = fd;
1132 sqe.__bindgen_anon_2.addr = buf as _;
1133 sqe.len = len;
1134 sqe.__bindgen_anon_3.msg_flags = flags as _;
1135 sqe.__bindgen_anon_4.buf_group = buf_group;
1136 Entry(sqe)
1137 }
1138}
1139
1140opcode! {
1141 /// Receive multiple messages from a socket, equivalent to `recv(2)`.
1142 ///
1143 /// Parameter:
1144 /// buf_group: The id of the provided buffer pool to use for each received message.
1145 ///
1146 /// MSG_WAITALL should not be set in flags.
1147 ///
1148 /// The multishot version allows the application to issue a single receive request, which
1149 /// repeatedly posts a CQE when data is available. Each CQE will take a buffer out of a
1150 /// provided buffer pool for receiving. The application should check the flags of each CQE,
1151 /// regardless of its result. If a posted CQE does not have the IORING_CQE_F_MORE flag set then
1152 /// the multishot receive will be done and the application should issue a new request.
1153 ///
1154 /// Multishot variants are available since kernel 6.0.
1155
1156 pub struct RecvMulti {
1157 fd: { RawFd },
1158 buf_group: { u16 },
1159 ;;
1160 flags: i32 = 0,
1161 }
1162
1163 pub const CODE = bindings::io_uring_op_IORING_OP_RECV;
1164
1165 pub fn build(self) -> Entry {
1166 let RecvMulti { fd, buf_group, flags } = self;
1167
1168 let mut sqe = sqe_zeroed();
1169 sqe.opcode = Self::CODE;
1170 sqe.fd = fd;
1171 sqe.__bindgen_anon_3.msg_flags = flags as _;
1172 sqe.__bindgen_anon_4.buf_group = buf_group;
1173 sqe.flags |= SqeFlags::BUFFER_SELECT.bits();
1174 sqe.ioprio = bindings::IORING_RECV_MULTISHOT as _;
1175 Entry(sqe)
1176 }
1177}
1178
1179// opcode! {
1180// /// Open a file, equivalent to `openat2(2)`.
1181// pub struct OpenAt2 {
1182// dirfd: { RawFd },
1183// pathname: { *const libc::c_char },
1184// how: { *const libc::open_how }
1185// ;;
1186// file_index: Option<types::DestinationSlot> = None,
1187// }
1188//
1189// pub const CODE = bindings::io_uring_op_IORING_OP_OPENAT2;
1190//
1191// pub fn build(self) -> Entry {
1192// let OpenAt2 { dirfd, pathname, how, file_index } = self;
1193//
1194// let mut sqe = sqe_zeroed();
1195// sqe.opcode = Self::CODE;
1196// sqe.fd = dirfd;
1197// sqe.__bindgen_anon_2.addr = pathname as _;
1198// sqe.len = mem::size_of::<bindings::open_how>() as _;
1199// sqe.__bindgen_anon_1.off = how as _;
1200// if let Some(dest) = file_index {
1201// sqe.__bindgen_anon_5.file_index = dest.kernel_index_arg();
1202// }
1203// Entry(sqe)
1204// }
1205// }
1206//
1207opcode! {
1208 /// Modify an epoll file descriptor, equivalent to `epoll_ctl(2)`.
1209 pub struct EpollCtl {
1210 epfd: { RawFd },
1211 fd: { RawFd },
1212 op: { i32 },
1213 ev: { *const libc::epoll_event },
1214 ;;
1215 }
1216
1217 pub const CODE = bindings::io_uring_op_IORING_OP_EPOLL_CTL;
1218
1219 pub fn build(self) -> Entry {
1220 let EpollCtl { epfd, fd, op, ev } = self;
1221
1222 let mut sqe = sqe_zeroed();
1223 sqe.opcode = Self::CODE;
1224 sqe.fd = epfd;
1225 sqe.__bindgen_anon_2.addr = ev as _;
1226 sqe.len = op as _;
1227 sqe.__bindgen_anon_1.off = fd as _;
1228 Entry(sqe)
1229 }
1230}
1231
1232// === 5.7 ===
1233
1234opcode! {
1235 /// Splice data to/from a pipe, equivalent to `splice(2)`.
1236 ///
1237 /// if `fd_in` refers to a pipe, `off_in` must be `-1`;
1238 /// The description of `off_in` also applied to `off_out`.
1239 pub struct Splice {
1240 fd_in: { RawFd },
1241 off_in: { i64 },
1242 fd_out: { RawFd },
1243 off_out: { i64 },
1244 len: { u32 },
1245 ;;
1246 /// see man `splice(2)` for description of flags.
1247 flags: u32 = 0
1248 }
1249
1250 pub const CODE = bindings::io_uring_op_IORING_OP_SPLICE;
1251
1252 pub fn build(self) -> Entry {
1253 let Splice { fd_in, off_in, fd_out, off_out, len, flags } = self;
1254
1255 let mut sqe = sqe_zeroed();
1256 sqe.opcode = Self::CODE;
1257 sqe.fd = fd_out;
1258 sqe.len = len;
1259 sqe.__bindgen_anon_1.off = off_out as _;
1260
1261 sqe.__bindgen_anon_5.splice_fd_in = fd_in;
1262
1263 sqe.__bindgen_anon_2.splice_off_in = off_in as _;
1264 sqe.__bindgen_anon_3.splice_flags = flags;
1265 Entry(sqe)
1266 }
1267}
1268
1269opcode! {
1270 /// Register `nbufs` buffers that each have the length `len` with ids starting from `bid` in the
1271 /// group `bgid` that can be used for any request. See
1272 /// [`BUFFER_SELECT`](SqeFlags::BUFFER_SELECT) for more info.
1273 pub struct ProvideBuffers {
1274 addr: { *mut u8 },
1275 len: { i32 },
1276 nbufs: { u16 },
1277 bgid: { u16 },
1278 bid: { u16 }
1279 ;;
1280 }
1281
1282 pub const CODE = bindings::io_uring_op_IORING_OP_PROVIDE_BUFFERS;
1283
1284 pub fn build(self) -> Entry {
1285 let ProvideBuffers { addr, len, nbufs, bgid, bid } = self;
1286
1287 let mut sqe = sqe_zeroed();
1288 sqe.opcode = Self::CODE;
1289 sqe.fd = nbufs as _;
1290 sqe.__bindgen_anon_2.addr = addr as _;
1291 sqe.len = len as _;
1292 sqe.__bindgen_anon_1.off = bid as _;
1293 sqe.__bindgen_anon_4.buf_group = bgid;
1294 Entry(sqe)
1295 }
1296}
1297
1298opcode! {
1299 /// Remove some number of buffers from a buffer group. See
1300 /// [`BUFFER_SELECT`](SqeFlags::BUFFER_SELECT) for more info.
1301 pub struct RemoveBuffers {
1302 nbufs: { u16 },
1303 bgid: { u16 }
1304 ;;
1305 }
1306
1307 pub const CODE = bindings::io_uring_op_IORING_OP_REMOVE_BUFFERS;
1308
1309 pub fn build(self) -> Entry {
1310 let RemoveBuffers { nbufs, bgid } = self;
1311
1312 let mut sqe = sqe_zeroed();
1313 sqe.opcode = Self::CODE;
1314 sqe.fd = nbufs as _;
1315 sqe.__bindgen_anon_4.buf_group = bgid;
1316 Entry(sqe)
1317 }
1318}
1319
1320// === 5.8 ===
1321
1322opcode! {
1323 /// Duplicate pipe content, equivalent to `tee(2)`.
1324 pub struct Tee {
1325 fd_in: { RawFd },
1326 fd_out: { RawFd },
1327 len: { u32 }
1328 ;;
1329 flags: u32 = 0
1330 }
1331
1332 pub const CODE = bindings::io_uring_op_IORING_OP_TEE;
1333
1334 pub fn build(self) -> Entry {
1335 let Tee { fd_in, fd_out, len, flags } = self;
1336
1337 let mut sqe = sqe_zeroed();
1338 sqe.opcode = Self::CODE;
1339
1340 sqe.fd = fd_out;
1341 sqe.len = len;
1342
1343 sqe.__bindgen_anon_5.splice_fd_in = fd_in;
1344
1345 sqe.__bindgen_anon_3.splice_flags = flags;
1346
1347 Entry(sqe)
1348 }
1349}
1350
1351// === 5.11 ===
1352
1353opcode! {
1354 /// Shut down all or part of a full duplex connection on a socket, equivalent to `shutdown(2)`.
1355 /// Available since kernel 5.11.
1356 pub struct Shutdown {
1357 fd: { RawFd },
1358 how: { i32 },
1359 ;;
1360 }
1361
1362 pub const CODE = bindings::io_uring_op_IORING_OP_SHUTDOWN;
1363
1364 pub fn build(self) -> Entry {
1365 let Shutdown { fd, how } = self;
1366
1367 let mut sqe = sqe_zeroed();
1368 sqe.opcode = Self::CODE;
1369 sqe.fd = fd;
1370 sqe.len = how as _;
1371 Entry(sqe)
1372 }
1373}
1374
1375opcode! {
1376 // Change the name or location of a file, equivalent to `renameat2(2)`.
1377 // Available since kernel 5.11.
1378 pub struct RenameAt {
1379 olddirfd: { RawFd },
1380 oldpath: { *const libc::c_char },
1381 newdirfd: { RawFd },
1382 newpath: { *const libc::c_char },
1383 ;;
1384 flags: u32 = 0
1385 }
1386
1387 pub const CODE = bindings::io_uring_op_IORING_OP_RENAMEAT;
1388
1389 pub fn build(self) -> Entry {
1390 let RenameAt {
1391 olddirfd, oldpath,
1392 newdirfd, newpath,
1393 flags
1394 } = self;
1395
1396 let mut sqe = sqe_zeroed();
1397 sqe.opcode = Self::CODE;
1398 sqe.fd = olddirfd;
1399 sqe.__bindgen_anon_2.addr = oldpath as _;
1400 sqe.len = newdirfd as _;
1401 sqe.__bindgen_anon_1.off = newpath as _;
1402 sqe.__bindgen_anon_3.rename_flags = flags;
1403 Entry(sqe)
1404 }
1405}
1406
1407opcode! {
1408 // Delete a name and possible the file it refers to, equivalent to `unlinkat(2)`.
1409 // Available since kernel 5.11.
1410 pub struct UnlinkAt {
1411 dirfd: { RawFd },
1412 pathname: { *const libc::c_char },
1413 ;;
1414 flags: i32 = 0
1415 }
1416
1417 pub const CODE = bindings::io_uring_op_IORING_OP_UNLINKAT;
1418
1419 pub fn build(self) -> Entry {
1420 let UnlinkAt { dirfd, pathname, flags } = self;
1421
1422 let mut sqe = sqe_zeroed();
1423 sqe.opcode = Self::CODE;
1424 sqe.fd = dirfd;
1425 sqe.__bindgen_anon_2.addr = pathname as _;
1426 sqe.__bindgen_anon_3.unlink_flags = flags as _;
1427 Entry(sqe)
1428 }
1429}
1430
1431// === 5.15 ===
1432
1433opcode! {
1434 /// Make a directory, equivalent to `mkdirat(2)`.
1435 pub struct MkDirAt {
1436 dirfd: { RawFd },
1437 pathname: { *const libc::c_char },
1438 ;;
1439 mode: libc::mode_t = 0
1440 }
1441
1442 pub const CODE = bindings::io_uring_op_IORING_OP_MKDIRAT;
1443
1444 pub fn build(self) -> Entry {
1445 let MkDirAt { dirfd, pathname, mode } = self;
1446
1447 let mut sqe = sqe_zeroed();
1448 sqe.opcode = Self::CODE;
1449 sqe.fd = dirfd;
1450 sqe.__bindgen_anon_2.addr = pathname as _;
1451 sqe.len = mode;
1452 Entry(sqe)
1453 }
1454}
1455
1456opcode! {
1457 /// Create a symlink, equivalent to `symlinkat(2)`.
1458 pub struct SymlinkAt {
1459 newdirfd: { RawFd },
1460 target: { *const libc::c_char },
1461 linkpath: { *const libc::c_char },
1462 ;;
1463 }
1464
1465 pub const CODE = bindings::io_uring_op_IORING_OP_SYMLINKAT;
1466
1467 pub fn build(self) -> Entry {
1468 let SymlinkAt { newdirfd, target, linkpath } = self;
1469
1470 let mut sqe = sqe_zeroed();
1471 sqe.opcode = Self::CODE;
1472 sqe.fd = newdirfd;
1473 sqe.__bindgen_anon_2.addr = target as _;
1474 sqe.__bindgen_anon_1.addr2 = linkpath as _;
1475 Entry(sqe)
1476 }
1477}
1478
1479opcode! {
1480 /// Create a hard link, equivalent to `linkat(2)`.
1481 pub struct LinkAt {
1482 olddirfd: { RawFd },
1483 oldpath: { *const libc::c_char },
1484 newdirfd: { RawFd },
1485 newpath: { *const libc::c_char },
1486 ;;
1487 flags: i32 = 0
1488 }
1489
1490 pub const CODE = bindings::io_uring_op_IORING_OP_LINKAT;
1491
1492 pub fn build(self) -> Entry {
1493 let LinkAt { olddirfd, oldpath, newdirfd, newpath, flags } = self;
1494
1495 let mut sqe = sqe_zeroed();
1496 sqe.opcode = Self::CODE;
1497 sqe.fd = olddirfd as _;
1498 sqe.__bindgen_anon_2.addr = oldpath as _;
1499 sqe.len = newdirfd as _;
1500 sqe.__bindgen_anon_1.addr2 = newpath as _;
1501 sqe.__bindgen_anon_3.hardlink_flags = flags as _;
1502 Entry(sqe)
1503 }
1504}
1505
1506// === 5.17 ===
1507
1508opcode! {
1509 /// Get extended attribute, equivalent to `getxattr(2)`.
1510 pub struct GetXattr {
1511 name: { *const libc::c_char },
1512 value: { *mut libc::c_void },
1513 path: { *const libc::c_char },
1514 len: { u32 },
1515 ;;
1516 }
1517
1518 pub const CODE = bindings::io_uring_op_IORING_OP_GETXATTR;
1519
1520 pub fn build(self) -> Entry {
1521 let GetXattr { name, value, path, len } = self;
1522
1523 let mut sqe = sqe_zeroed();
1524 sqe.opcode = Self::CODE;
1525 sqe.__bindgen_anon_2.addr = name as _;
1526 sqe.len = len;
1527 sqe.__bindgen_anon_1.off = value as _;
1528 unsafe { sqe.__bindgen_anon_6.__bindgen_anon_1.as_mut().addr3 = path as _ };
1529 sqe.__bindgen_anon_3.xattr_flags = 0;
1530 Entry(sqe)
1531 }
1532}
1533
1534opcode! {
1535 /// Set extended attribute, equivalent to `setxattr(2)`.
1536 pub struct SetXattr {
1537 name: { *const libc::c_char },
1538 value: { *const libc::c_void },
1539 path: { *const libc::c_char },
1540 len: { u32 },
1541 ;;
1542 flags: i32 = 0
1543 }
1544
1545 pub const CODE = bindings::io_uring_op_IORING_OP_SETXATTR;
1546
1547 pub fn build(self) -> Entry {
1548 let SetXattr { name, value, path, flags, len } = self;
1549
1550 let mut sqe = sqe_zeroed();
1551 sqe.opcode = Self::CODE;
1552 sqe.__bindgen_anon_2.addr = name as _;
1553 sqe.len = len;
1554 sqe.__bindgen_anon_1.off = value as _;
1555 unsafe { sqe.__bindgen_anon_6.__bindgen_anon_1.as_mut().addr3 = path as _ };
1556 sqe.__bindgen_anon_3.xattr_flags = flags as _;
1557 Entry(sqe)
1558 }
1559}
1560
1561opcode! {
1562 /// Get extended attribute from a file descriptor, equivalent to `fgetxattr(2)`.
1563 pub struct FGetXattr {
1564 fd: { RawFd },
1565 name: { *const libc::c_char },
1566 value: { *mut libc::c_void },
1567 len: { u32 },
1568 ;;
1569 }
1570
1571 pub const CODE = bindings::io_uring_op_IORING_OP_FGETXATTR;
1572
1573 pub fn build(self) -> Entry {
1574 let FGetXattr { fd, name, value, len } = self;
1575
1576 let mut sqe = sqe_zeroed();
1577 sqe.opcode = Self::CODE;
1578 sqe.fd = fd;
1579 sqe.__bindgen_anon_2.addr = name as _;
1580 sqe.len = len;
1581 sqe.__bindgen_anon_1.off = value as _;
1582 sqe.__bindgen_anon_3.xattr_flags = 0;
1583 Entry(sqe)
1584 }
1585}
1586
1587opcode! {
1588 /// Set extended attribute on a file descriptor, equivalent to `fsetxattr(2)`.
1589 pub struct FSetXattr {
1590 fd: { RawFd },
1591 name: { *const libc::c_char },
1592 value: { *const libc::c_void },
1593 len: { u32 },
1594 ;;
1595 flags: i32 = 0
1596 }
1597
1598 pub const CODE = bindings::io_uring_op_IORING_OP_FSETXATTR;
1599
1600 pub fn build(self) -> Entry {
1601 let FSetXattr { fd, name, value, flags, len } = self;
1602
1603 let mut sqe = sqe_zeroed();
1604 sqe.opcode = Self::CODE;
1605 sqe.fd = fd;
1606 sqe.__bindgen_anon_2.addr = name as _;
1607 sqe.len = len;
1608 sqe.__bindgen_anon_1.off = value as _;
1609 sqe.__bindgen_anon_3.xattr_flags = flags as _;
1610 Entry(sqe)
1611 }
1612}
1613
1614// === 5.18 ===
1615
1616// opcode! {
1617// /// Send a message (with data) to a target ring.
1618// pub struct MsgRingData {
1619// ring_fd: { RawFd },
1620// result: { i32 },
1621// user_data: { u64 },
1622// user_flags: { Option<u32> },
1623// ;;
1624// opcode_flags: u32 = 0
1625// }
1626//
1627// pub const CODE = bindings::io_uring_op_IORING_OP_MSG_RING;
1628//
1629// pub fn build(self) -> Entry {
1630// let MsgRingData { ring_fd, result, user_data, user_flags, opcode_flags } = self;
1631//
1632// let mut sqe = sqe_zeroed();
1633// sqe.opcode = Self::CODE;
1634// sqe.__bindgen_anon_2.addr = bindings::io_uring_op_IORING_MSG_DATA.into();
1635// sqe.fd = ring_fd;
1636// sqe.len = result as u32;
1637// sqe.__bindgen_anon_1.off = user_data;
1638// sqe.__bindgen_anon_3.msg_ring_flags = opcode_flags;
1639// if let Some(flags) = user_flags {
1640// sqe.__bindgen_anon_5.file_index = flags;
1641// unsafe {sqe.__bindgen_anon_3.msg_ring_flags |= bindings::IORING_MSG_RING_FLAGS_PASS};
1642// }
1643// Entry(sqe)
1644// }
1645// }
1646
1647// === 5.19 ===
1648
1649// opcode! {
1650// /// Attempt to cancel an already issued request, receiving a cancellation
1651// /// builder, which allows for the new cancel criterias introduced since
1652// /// 5.19.
1653// pub struct AsyncCancel2 {
1654// builder: { types::CancelBuilder }
1655// ;;
1656// }
1657//
1658// pub const CODE = bindings::io_uring_op_IORING_OP_ASYNC_CANCEL;
1659//
1660// pub fn build(self) -> Entry {
1661// let AsyncCancel2 { builder } = self;
1662//
1663// let mut sqe = sqe_zeroed();
1664// sqe.opcode = Self::CODE;
1665// sqe.fd = builder.to_fd();
1666// sqe.__bindgen_anon_2.addr = builder.user_data.unwrap_or(0);
1667// sqe.__bindgen_anon_3.cancel_flags = builder.flags.bits();
1668// Entry(sqe)
1669// }
1670// }
1671
1672opcode! {
1673 /// A file/device-specific 16-byte command, akin (but not equivalent) to `ioctl(2)`.
1674 pub struct UringCmd16 {
1675 fd: { RawFd },
1676 cmd_op: { u32 },
1677 ;;
1678 /// The `buf_index` is an index into an array of fixed buffers,
1679 /// and is only valid if fixed buffers were registered.
1680 buf_index: Option<u16> = None,
1681 /// Arbitrary command data.
1682 cmd: [u8; 16] = [0u8; 16]
1683 }
1684
1685 pub const CODE = bindings::io_uring_op_IORING_OP_URING_CMD;
1686
1687 pub fn build(self) -> Entry {
1688 let UringCmd16 { fd, cmd_op, cmd, buf_index } = self;
1689
1690 let mut sqe = sqe_zeroed();
1691 sqe.opcode = Self::CODE;
1692 sqe.fd = fd;
1693 sqe.__bindgen_anon_1.__bindgen_anon_1.cmd_op = cmd_op;
1694 unsafe { *sqe.__bindgen_anon_6.cmd.as_mut().as_mut_ptr().cast::<[u8; 16]>() = cmd };
1695 if let Some(buf_index) = buf_index {
1696 sqe.__bindgen_anon_4.buf_index = buf_index;
1697 unsafe {
1698 sqe.__bindgen_anon_3.uring_cmd_flags |= bindings::IORING_URING_CMD_FIXED;
1699 }
1700 }
1701 Entry(sqe)
1702 }
1703}
1704
1705// opcode! {
1706// /// A file/device-specific 80-byte command, akin (but not equivalent) to `ioctl(2)`.
1707// pub struct UringCmd80 {
1708// fd: { RawFd },
1709// cmd_op: { u32 },
1710// ;;
1711// /// The `buf_index` is an index into an array of fixed buffers,
1712// /// and is only valid if fixed buffers were registered.
1713// buf_index: Option<u16> = None,
1714// /// Arbitrary command data.
1715// cmd: [u8; 80] = [0u8; 80]
1716// }
1717//
1718// pub const CODE = bindings::io_uring_op_IORING_OP_URING_CMD;
1719//
1720// pub fn build(self) -> Entry128 {
1721// let UringCmd80 { fd, cmd_op, cmd, buf_index } = self;
1722//
1723// let cmd1 = cmd[..16].try_into().unwrap();
1724// let cmd2 = cmd[16..].try_into().unwrap();
1725//
1726// let mut sqe = sqe_zeroed();
1727// sqe.opcode = Self::CODE;
1728// sqe.fd = fd;
1729// sqe.__bindgen_anon_1.__bindgen_anon_1.cmd_op = cmd_op;
1730// unsafe { *sqe.__bindgen_anon_6.cmd.as_mut().as_mut_ptr().cast::<[u8; 16]>() = cmd1 };
1731// if let Some(buf_index) = buf_index {
1732// sqe.__bindgen_anon_4.buf_index = buf_index;
1733// unsafe {
1734// sqe.__bindgen_anon_3.uring_cmd_flags |= bindings::IORING_URING_CMD_FIXED;
1735// }
1736// }
1737// Entry128(Entry(sqe), cmd2)
1738// }
1739// }
1740
1741opcode! {
1742 /// Create an endpoint for communication, equivalent to `socket(2)`.
1743 ///
1744 /// Available since 5.19.
1745 pub struct Socket {
1746 domain: { i32 },
1747 socket_type: { i32 },
1748 protocol: { i32 },
1749 ;;
1750 flags: i32 = 0
1751 }
1752
1753 pub const CODE = bindings::io_uring_op_IORING_OP_SOCKET;
1754
1755 pub fn build(self) -> Entry {
1756 let Socket { domain, socket_type, protocol, flags } = self;
1757
1758 let mut sqe = sqe_zeroed();
1759 sqe.opcode = Self::CODE;
1760 sqe.fd = domain as _;
1761 sqe.__bindgen_anon_1.off = socket_type as _;
1762 sqe.len = protocol as _;
1763 sqe.__bindgen_anon_3.rw_flags = flags as _;
1764 Entry(sqe)
1765 }
1766}
1767
1768opcode! {
1769 /// Accept multiple new connections on a socket.
1770 ///
1771 /// Set the `allocate_file_index` property if fixed file table entries should be used.
1772 ///
1773 /// Available since 5.19.
1774 pub struct AcceptMulti {
1775 fd: { RawFd },
1776 ;;
1777 allocate_file_index: bool = false,
1778 flags: i32 = 0
1779 }
1780
1781 pub const CODE = bindings::io_uring_op_IORING_OP_ACCEPT;
1782
1783 pub fn build(self) -> Entry {
1784 let AcceptMulti { fd, allocate_file_index, flags } = self;
1785
1786 let mut sqe = sqe_zeroed();
1787 sqe.opcode = Self::CODE;
1788 sqe.fd = fd;
1789 sqe.ioprio = bindings::IORING_ACCEPT_MULTISHOT as u16;
1790 // No out SockAddr is passed for the multishot accept case.
1791 // The user should perform a syscall to get any resulting connection's remote address.
1792 sqe.__bindgen_anon_3.accept_flags = flags as _;
1793 if allocate_file_index {
1794 sqe.__bindgen_anon_5.file_index = bindings::IORING_FILE_INDEX_ALLOC as u32;
1795 }
1796 Entry(sqe)
1797 }
1798}
1799
1800// === 6.0 ===
1801
1802// opcode! {
1803// /// Send a message (with fixed FD) to a target ring.
1804// pub struct MsgRingSendFd {
1805// ring_fd: { RawFd },
1806// fixed_slot_src: { types::Fixed },
1807// dest_slot_index: { types::DestinationSlot },
1808// user_data: { u64 },
1809// ;;
1810// opcode_flags: u32 = 0
1811// }
1812//
1813// pub const CODE = bindings::io_uring_op_IORING_OP_MSG_RING;
1814//
1815// pub fn build(self) -> Entry {
1816// let MsgRingSendFd { ring_fd, fixed_slot_src, dest_slot_index, user_data, opcode_flags } = self;
1817//
1818// let mut sqe = sqe_zeroed();
1819// sqe.opcode = Self::CODE;
1820// sqe.__bindgen_anon_2.addr = bindings::io_uring_op_IORING_MSG_SEND_FD.into();
1821// sqe.fd = ring_fd;
1822// sqe.__bindgen_anon_1.off = user_data;
1823// unsafe { sqe.__bindgen_anon_6.__bindgen_anon_1.as_mut().addr3 = fixed_slot_src.0 as u64 };
1824// sqe.__bindgen_anon_5.file_index = dest_slot_index.kernel_index_arg();
1825// sqe.__bindgen_anon_3.msg_ring_flags = opcode_flags;
1826// Entry(sqe)
1827// }
1828// }
1829
1830// === 6.0 ===
1831
1832opcode! {
1833 /// Send a zerocopy message on a socket, equivalent to `send(2)`.
1834 ///
1835 /// When `dest_addr` is non-zero it points to the address of the target with `dest_addr_len`
1836 /// specifying its size, turning the request into a `sendto(2)`
1837 ///
1838 /// A fixed (pre-mapped) buffer can optionally be used from pre-mapped buffers that have been
1839 /// previously registered with [`Submitter::register_buffers`](crate::Submitter::register_buffers).
1840 ///
1841 /// This operation might result in two completion queue entries.
1842 /// See the `IORING_OP_SEND_ZC` section at [io_uring_enter][] for the exact semantics.
1843 /// Notifications posted by this operation can be checked with [notif](crate::cqueue::notif).
1844 ///
1845 /// [io_uring_enter]: https://man7.org/linux/man-pages/man2/io_uring_enter.2.html
1846 pub struct SendZc {
1847 fd: { RawFd },
1848 buf: { *const u8 },
1849 len: { u32 },
1850 ;;
1851 /// The `buf_index` is an index into an array of fixed buffers, and is only valid if fixed
1852 /// buffers were registered.
1853 ///
1854 /// The buf and len arguments must fall within a region specified by buf_index in the
1855 /// previously registered buffer. The buffer need not be aligned with the start of the
1856 /// registered buffer.
1857 buf_index: Option<u16> = None,
1858 dest_addr: *const libc::sockaddr = core::ptr::null(),
1859 dest_addr_len: libc::socklen_t = 0,
1860 flags: i32 = 0,
1861 zc_flags: u16 = 0,
1862 }
1863
1864 pub const CODE = bindings::io_uring_op_IORING_OP_SEND_ZC;
1865
1866 pub fn build(self) -> Entry {
1867 let SendZc { fd, buf, len, buf_index, dest_addr, dest_addr_len, flags, zc_flags } = self;
1868
1869 let mut sqe = sqe_zeroed();
1870 sqe.opcode = Self::CODE;
1871 sqe.fd = fd;
1872 sqe.__bindgen_anon_2.addr = buf as _;
1873 sqe.len = len;
1874 sqe.__bindgen_anon_3.msg_flags = flags as _;
1875 sqe.ioprio = zc_flags;
1876 if let Some(buf_index) = buf_index {
1877 sqe.__bindgen_anon_4.buf_index = buf_index;
1878 sqe.ioprio |= bindings::IORING_RECVSEND_FIXED_BUF as u16;
1879 }
1880 sqe.__bindgen_anon_1.addr2 = dest_addr as _;
1881 sqe.__bindgen_anon_5.__bindgen_anon_1.addr_len = dest_addr_len as _;
1882 Entry(sqe)
1883 }
1884}
1885
1886// === 6.1 ===
1887
1888opcode! {
1889 /// Send a zerocopy message on a socket, equivalent to `send(2)`.
1890 ///
1891 /// fd must be set to the socket file descriptor, addr must contains a pointer to the msghdr
1892 /// structure, and flags holds the flags associated with the system call.
1893 #[derive(Debug)]
1894 pub struct SendMsgZc {
1895 fd: { RawFd },
1896 msg: { *const libc::msghdr },
1897 ;;
1898 ioprio: u16 = 0,
1899 flags: u32 = 0
1900 }
1901
1902 pub const CODE = bindings::io_uring_op_IORING_OP_SENDMSG_ZC;
1903
1904 pub fn build(self) -> Entry {
1905 let SendMsgZc { fd, msg, ioprio, flags } = self;
1906
1907 let mut sqe = sqe_zeroed();
1908 sqe.opcode = Self::CODE;
1909 sqe.fd = fd;
1910 sqe.ioprio = ioprio;
1911 sqe.__bindgen_anon_2.addr = msg as _;
1912 sqe.len = 1;
1913 sqe.__bindgen_anon_3.msg_flags = flags;
1914 Entry(sqe)
1915 }
1916}
1917
1918// === 6.7 ===
1919
1920opcode! {
1921 /// Issue the equivalent of `pread(2)` with multi-shot semantics.
1922 pub struct ReadMulti {
1923 fd: { RawFd },
1924 len: { u32 },
1925 buf_group: { u16 },
1926 ;;
1927 offset: u64 = 0,
1928 }
1929
1930 pub const CODE = bindings::io_uring_op_IORING_OP_READ_MULTISHOT;
1931
1932 pub fn build(self) -> Entry {
1933 let Self { fd, len, buf_group, offset } = self;
1934
1935 let mut sqe = sqe_zeroed();
1936 sqe.opcode = Self::CODE;
1937 sqe.fd = fd;
1938 sqe.__bindgen_anon_1.off = offset;
1939 sqe.len = len;
1940 sqe.__bindgen_anon_4.buf_group = buf_group;
1941 sqe.flags = SqeFlags::BUFFER_SELECT.bits();
1942 Entry(sqe)
1943 }
1944}
1945
1946opcode! {
1947 /// Wait on a futex, like but not equivalant to `futex(2)`'s `FUTEX_WAIT_BITSET`.
1948 ///
1949 /// Wait on a futex at address `futex` and which still has the value `val` and with `futex2(2)`
1950 /// flags of `futex_flags`. `musk` can be set to a specific bitset mask, which will be matched
1951 /// by the waking side to decide who to wake up. To always get woken, an application may use
1952 /// `FUTEX_BITSET_MATCH_ANY` (truncated to futex bits). `futex_flags` follows the `futex2(2)`
1953 /// flags, not the `futex(2)` v1 interface flags. `flags` are currently unused and hence `0`
1954 /// must be passed.
1955 #[derive(Debug)]
1956 pub struct FutexWait {
1957 futex: { *const u32 },
1958 val: { u64 },
1959 mask: { u64 },
1960 futex_flags: { u32 },
1961 ;;
1962 flags: u32 = 0
1963 }
1964
1965 pub const CODE = bindings::io_uring_op_IORING_OP_FUTEX_WAIT;
1966
1967 pub fn build(self) -> Entry {
1968 let FutexWait { futex, val, mask, futex_flags, flags } = self;
1969
1970 let mut sqe = sqe_zeroed();
1971 sqe.opcode = Self::CODE;
1972 sqe.fd = futex_flags as _;
1973 sqe.__bindgen_anon_2.addr = futex as usize as _;
1974 sqe.__bindgen_anon_1.off = val;
1975 unsafe { sqe.__bindgen_anon_6.__bindgen_anon_1.as_mut().addr3 = mask };
1976 sqe.__bindgen_anon_3.futex_flags = flags;
1977 Entry(sqe)
1978 }
1979}
1980
1981opcode! {
1982 /// Wake up waiters on a futex, like but not equivalant to `futex(2)`'s `FUTEX_WAKE_BITSET`.
1983 ///
1984 /// Wake any waiters on the futex indicated by `futex` and at most `val` futexes. `futex_flags`
1985 /// indicates the `futex2(2)` modifier flags. If a given bitset for who to wake is desired,
1986 /// then that must be set in `mask`. Use `FUTEX_BITSET_MATCH_ANY` (truncated to futex bits) to
1987 /// match any waiter on the given futex. `flags` are currently unused and hence `0` must be
1988 /// passed.
1989 #[derive(Debug)]
1990 pub struct FutexWake {
1991 futex: { *const u32 },
1992 val: { u64 },
1993 mask: { u64 },
1994 futex_flags: { u32 },
1995 ;;
1996 flags: u32 = 0
1997 }
1998
1999 pub const CODE = bindings::io_uring_op_IORING_OP_FUTEX_WAKE;
2000
2001 pub fn build(self) -> Entry {
2002 let FutexWake { futex, val, mask, futex_flags, flags } = self;
2003
2004 let mut sqe = sqe_zeroed();
2005 sqe.opcode = Self::CODE;
2006 sqe.fd = futex_flags as _;
2007 sqe.__bindgen_anon_2.addr = futex as usize as _;
2008 sqe.__bindgen_anon_1.off = val;
2009 unsafe { sqe.__bindgen_anon_6.__bindgen_anon_1.as_mut().addr3 = mask };
2010 sqe.__bindgen_anon_3.futex_flags = flags;
2011 Entry(sqe)
2012 }
2013}
2014
2015// opcode! {
2016// /// Wait on multiple futexes.
2017// ///
2018// /// Wait on multiple futexes at the same time. Futexes are given by `futexv` and `nr_futex` is
2019// /// the number of futexes in that array. Unlike `FutexWait`, the desired bitset mask and values
2020// /// are passed in `futexv`. `flags` are currently unused and hence `0` must be passed.
2021// #[derive(Debug)]
2022// pub struct FutexWaitV {
2023// futexv: { *const types::FutexWaitV },
2024// nr_futex: { u32 },
2025// ;;
2026// flags: u32 = 0
2027// }
2028//
2029// pub const CODE = bindings::io_uring_op_IORING_OP_FUTEX_WAITV;
2030//
2031// pub fn build(self) -> Entry {
2032// let FutexWaitV { futexv, nr_futex, flags } = self;
2033//
2034// let mut sqe = sqe_zeroed();
2035// sqe.opcode = Self::CODE;
2036// sqe.__bindgen_anon_2.addr = futexv as usize as _;
2037// sqe.len = nr_futex;
2038// sqe.__bindgen_anon_3.futex_flags = flags;
2039// Entry(sqe)
2040// }
2041// }
2042
2043opcode! {
2044 /// Issue the equivalent of a `waitid(2)` system call.
2045 ///
2046 /// Available since kernel 6.7.
2047 #[derive(Debug)]
2048 pub struct WaitId {
2049 idtype: { libc::idtype_t },
2050 id: { libc::id_t },
2051 options: { libc::c_int },
2052 ;;
2053 infop: *const libc::siginfo_t = std::ptr::null(),
2054 flags: libc::c_uint = 0,
2055 }
2056
2057 pub const CODE = bindings::io_uring_op_IORING_OP_WAITID;
2058
2059 pub fn build(self) -> Entry {
2060 let mut sqe = sqe_zeroed();
2061 sqe.opcode = Self::CODE;
2062 sqe.fd = self.id as _;
2063 sqe.len = self.idtype as _;
2064 sqe.__bindgen_anon_3.waitid_flags = self.flags;
2065 sqe.__bindgen_anon_5.file_index = self.options as _;
2066 sqe.__bindgen_anon_1.addr2 = self.infop as _;
2067 Entry(sqe)
2068 }
2069}
2070
2071// === 6.8 ===
2072
2073// opcode! {
2074// /// Install a fixed file descriptor
2075// ///
2076// /// Turns a direct descriptor into a regular file descriptor that can be later used by regular
2077// /// system calls that take a normal raw file descriptor
2078// #[derive(Debug)]
2079// pub struct FixedFdInstall {
2080// fd: { types::Fixed },
2081// file_flags: { u32 },
2082// ;;
2083// }
2084//
2085// pub const CODE = bindings::io_uring_op_IORING_OP_FIXED_FD_INSTALL;
2086//
2087// pub fn build(self) -> Entry {
2088// let FixedFdInstall { fd, file_flags } = self;
2089//
2090// let mut sqe = sqe_zeroed();
2091// sqe.opcode = Self::CODE;
2092// sqe.fd = fd.0 as _;
2093// sqe.flags = SqeFlags::FIXED_FILE.bits();
2094// sqe.__bindgen_anon_3.install_fd_flags = file_flags;
2095// Entry(sqe)
2096// }
2097// }
2098
2099// === 6.9 ===
2100
2101opcode! {
2102 /// Perform file truncation, equivalent to `ftruncate(2)`.
2103 #[derive(Debug)]
2104 pub struct Ftruncate {
2105 fd: { RawFd },
2106 len: { u64 },
2107 ;;
2108 }
2109
2110 pub const CODE = bindings::io_uring_op_IORING_OP_FTRUNCATE;
2111
2112 pub fn build(self) -> Entry {
2113 let Ftruncate { fd, len } = self;
2114
2115 let mut sqe = sqe_zeroed();
2116 sqe.opcode = Self::CODE;
2117 sqe.fd = fd;
2118 sqe.__bindgen_anon_1.off = len;
2119 Entry(sqe)
2120 }
2121}
2122
2123// === 6.10 ===
2124
2125opcode! {
2126 /// Send a bundle of messages on a socket in a single request.
2127 pub struct SendBundle {
2128 fd: { RawFd },
2129 buf_group: { u16 },
2130 ;;
2131 flags: i32 = 0,
2132 len: u32 = 0
2133 }
2134
2135 pub const CODE = bindings::io_uring_op_IORING_OP_SEND;
2136
2137 pub fn build(self) -> Entry {
2138 let SendBundle { fd, len, flags, buf_group } = self;
2139
2140 let mut sqe = sqe_zeroed();
2141 sqe.opcode = Self::CODE;
2142 sqe.fd = fd;
2143 sqe.len = len;
2144 sqe.__bindgen_anon_3.msg_flags = flags as _;
2145 sqe.ioprio |= bindings::IORING_RECVSEND_BUNDLE as u16;
2146 sqe.flags |= SqeFlags::BUFFER_SELECT.bits();
2147 sqe.__bindgen_anon_4.buf_group = buf_group;
2148 Entry(sqe)
2149 }
2150}
2151
2152opcode! {
2153 /// Receive a bundle of buffers from a socket.
2154 ///
2155 /// Parameter
2156 /// buf_group: The id of the provided buffer pool to use for the bundle.
2157 ///
2158 /// Note that as of kernel 6.10 first recv always gets a single buffer, while second
2159 /// obtains the bundle of remaining buffers. This behavior may change in the future.
2160 ///
2161 /// Bundle variant is available since kernel 6.10
2162 pub struct RecvBundle {
2163 fd: { RawFd },
2164 buf_group: { u16 },
2165 ;;
2166 flags: i32 = 0
2167 }
2168
2169 pub const CODE = bindings::io_uring_op_IORING_OP_RECV;
2170
2171 pub fn build(self) -> Entry {
2172 let RecvBundle { fd, buf_group, flags } = self;
2173
2174 let mut sqe = sqe_zeroed();
2175 sqe.opcode = Self::CODE;
2176 sqe.fd = fd;
2177 sqe.__bindgen_anon_3.msg_flags = flags as _;
2178 sqe.__bindgen_anon_4.buf_group = buf_group;
2179 sqe.flags |= SqeFlags::BUFFER_SELECT.bits();
2180 sqe.ioprio |= bindings::IORING_RECVSEND_BUNDLE as u16;
2181 Entry(sqe)
2182 }
2183}
2184
2185opcode! {
2186 /// Receive multiple messages from a socket as a bundle.
2187 ///
2188 /// Parameter:
2189 /// buf_group: The id of the provided buffer pool to use for each received message.
2190 ///
2191 /// MSG_WAITALL should not be set in flags.
2192 ///
2193 /// The multishot version allows the application to issue a single receive request, which
2194 /// repeatedly posts a CQE when data is available. Each CQE will take a bundle of buffers
2195 /// out of a provided buffer pool for receiving. The application should check the flags of each CQE,
2196 /// regardless of its result. If a posted CQE does not have the IORING_CQE_F_MORE flag set then
2197 /// the multishot receive will be done and the application should issue a new request.
2198 ///
2199 /// Note that as of kernel 6.10 first CQE always gets a single buffer, while second
2200 /// obtains the bundle of remaining buffers. This behavior may change in the future.
2201 ///
2202 /// Multishot bundle variant is available since kernel 6.10.
2203 pub struct RecvMultiBundle {
2204 fd: { RawFd },
2205 buf_group: { u16 },
2206 ;;
2207 flags: i32 = 0
2208 }
2209
2210 pub const CODE = bindings::io_uring_op_IORING_OP_RECV;
2211
2212 pub fn build(self) -> Entry {
2213 let RecvMultiBundle { fd, buf_group, flags } = self;
2214
2215 let mut sqe = sqe_zeroed();
2216 sqe.opcode = Self::CODE;
2217 sqe.fd = fd;
2218 sqe.__bindgen_anon_3.msg_flags = flags as _;
2219 sqe.__bindgen_anon_4.buf_group = buf_group;
2220 sqe.flags |= SqeFlags::BUFFER_SELECT.bits();
2221 sqe.ioprio = bindings::IORING_RECV_MULTISHOT as _;
2222 sqe.ioprio |= bindings::IORING_RECVSEND_BUNDLE as u16;
2223 Entry(sqe)
2224 }
2225}
2226
2227// === 6.11 ===
2228
2229opcode! {
2230 /// Bind a socket, equivalent to `bind(2)`.
2231 pub struct Bind {
2232 fd: { RawFd },
2233 addr: { *const libc::sockaddr },
2234 addrlen: { libc::socklen_t }
2235 ;;
2236 }
2237
2238 pub const CODE = bindings::io_uring_op_IORING_OP_BIND;
2239
2240 pub fn build(self) -> Entry {
2241 let Bind { fd, addr, addrlen } = self;
2242
2243 let mut sqe = sqe_zeroed();
2244 sqe.opcode = Self::CODE;
2245 sqe.fd = fd;
2246 sqe.__bindgen_anon_2.addr = addr as _;
2247 sqe.__bindgen_anon_1.off = addrlen as _;
2248 Entry(sqe)
2249 }
2250}
2251
2252opcode! {
2253 /// Listen on a socket, equivalent to `listen(2)`.
2254 pub struct Listen {
2255 fd: { RawFd },
2256 backlog: { i32 },
2257 ;;
2258 }
2259
2260 pub const CODE = bindings::io_uring_op_IORING_OP_LISTEN;
2261
2262 pub fn build(self) -> Entry {
2263 let Listen { fd, backlog } = self;
2264
2265 let mut sqe = sqe_zeroed();
2266 sqe.opcode = Self::CODE;
2267 sqe.fd = fd;
2268 sqe.len = backlog as _;
2269 Entry(sqe)
2270 }
2271}
2272
2273// === 6.15 ===
2274
2275opcode! {
2276 /// Issue the zerocopy equivalent of a `recv(2)` system call.
2277 pub struct RecvZc {
2278 fd: { RawFd },
2279 len: { u32 },
2280 ;;
2281 ifq: u32 = 0,
2282 ioprio: u16 = 0,
2283 }
2284
2285 pub const CODE = bindings::io_uring_op_IORING_OP_RECV_ZC;
2286
2287 pub fn build(self) -> Entry {
2288 let Self { fd, len, ifq, ioprio } = self;
2289
2290 let mut sqe = sqe_zeroed();
2291 sqe.opcode = Self::CODE;
2292 sqe.fd = fd;
2293 sqe.len = len;
2294 sqe.ioprio = ioprio | bindings::IORING_RECV_MULTISHOT as u16;
2295 sqe.__bindgen_anon_5.zcrx_ifq_idx = ifq;
2296 Entry(sqe)
2297 }
2298}
2299
2300opcode! {
2301 /// Issue the equivalent of a `epoll_wait(2)` system call.
2302 pub struct EpollWait {
2303 fd: { RawFd },
2304 events: { *mut libc::epoll_event },
2305 max_events: { u32 },
2306 ;;
2307 flags: u32 = 0,
2308 }
2309
2310 pub const CODE = bindings::io_uring_op_IORING_OP_EPOLL_WAIT;
2311
2312 pub fn build(self) -> Entry {
2313 let Self { fd, events, max_events, flags } = self;
2314
2315 let mut sqe = sqe_zeroed();
2316 sqe.opcode = Self::CODE;
2317 sqe.fd = fd;
2318 sqe.__bindgen_anon_2.addr = events as u64;
2319 sqe.len = max_events;
2320 sqe.__bindgen_anon_3.poll32_events = flags;
2321 Entry(sqe)
2322 }
2323}
2324
2325opcode! {
2326 /// Vectored read into a fixed buffer, equivalent to `preadv2(2)`.
2327 pub struct ReadvFixed {
2328 fd: { RawFd },
2329 iovec: { *const ::libc::iovec },
2330 len: { u32 },
2331 buf_index: { u16 },
2332 ;;
2333 ioprio: u16 = 0,
2334 offset: u64 = 0,
2335 rw_flags: i32 = 0,
2336 }
2337
2338 pub const CODE = bindings::io_uring_op_IORING_OP_READV_FIXED;
2339
2340 pub fn build(self) -> Entry {
2341 let Self { fd, iovec, len, buf_index, offset, ioprio, rw_flags } = self;
2342
2343 let mut sqe = sqe_zeroed();
2344 sqe.opcode = Self::CODE;
2345 sqe.fd = fd;
2346 sqe.__bindgen_anon_1.off = offset as _;
2347 sqe.__bindgen_anon_2.addr = iovec as _;
2348 sqe.len = len;
2349 sqe.__bindgen_anon_4.buf_index = buf_index;
2350 sqe.ioprio = ioprio;
2351 sqe.__bindgen_anon_3.rw_flags = rw_flags as _;
2352 Entry(sqe)
2353 }
2354}
2355
2356opcode! {
2357 /// Vectored write from a fixed buffer, equivalent to `pwritev2(2)`.
2358 pub struct WritevFixed {
2359 fd: { RawFd },
2360 iovec: { *const ::libc::iovec },
2361 len: { u32 },
2362 buf_index: { u16 },
2363 ;;
2364 ioprio: u16 = 0,
2365 offset: u64 = 0,
2366 rw_flags: i32 = 0,
2367 }
2368
2369 pub const CODE = bindings::io_uring_op_IORING_OP_WRITEV_FIXED;
2370
2371 pub fn build(self) -> Entry {
2372 let Self { fd, iovec, len, buf_index, offset, ioprio, rw_flags } = self;
2373
2374 let mut sqe = sqe_zeroed();
2375 sqe.opcode = Self::CODE;
2376 sqe.fd = fd;
2377 sqe.__bindgen_anon_1.off = offset as _;
2378 sqe.__bindgen_anon_2.addr = iovec as _;
2379 sqe.len = len;
2380 sqe.__bindgen_anon_4.buf_index = buf_index;
2381 sqe.ioprio = ioprio;
2382 sqe.__bindgen_anon_3.rw_flags = rw_flags as _;
2383 Entry(sqe)
2384 }
2385}
2386
2387// === 6.16 ===
2388
2389opcode! {
2390 // Create a pipe, equivalent to `pipe(2)`.
2391 pub struct Pipe {
2392 fds: { *mut RawFd },
2393 ;;
2394 flags: u32 = 0,
2395 }
2396
2397 pub const CODE = bindings::io_uring_op_IORING_OP_PIPE;
2398
2399 pub fn build(self) -> Entry {
2400 let Self { fds, flags } = self;
2401
2402 let mut sqe = sqe_zeroed();
2403 sqe.opcode = Self::CODE;
2404 sqe.fd = 0;
2405 sqe.__bindgen_anon_2.addr = fds as _;
2406 sqe.__bindgen_anon_3.pipe_flags = flags;
2407 Entry(sqe)
2408 }
2409}
2410
2411// // Operations defined using the macro
2412// define_operation!(Nop => io_uring_prep_nop());
2413//
2414// define_operation!(Read {
2415// fd: RawFd,
2416// ptr: *mut libc::c_void,
2417// len: u32,
2418// offset: u64,
2419// } => io_uring_prep_read(fd, ptr, len, offset));
2420
2421// define_operation!(Write {
2422// fd: RawFd,
2423// ptr: *mut libc::c_void,
2424// len: u32,
2425// offset: u64,
2426// }, |self, sqe| {bindings::io_uring_prep_write(&raw mut sqe, self.fd, self.ptr, self.len, self.offset)});
2427
2428// define_operation!(Fsync {
2429// fd: RawFd,
2430// flags: u32,
2431// } => io_uring_prep_fsync(fd, flags));
2432//
2433// define_operation!(ReadFixed {
2434// fd: RawFd,
2435// buf: *mut libc::c_void,
2436// nbytes: u32,
2437// offset: u64,
2438// buf_index: i32,
2439// } => io_uring_prep_read_fixed(fd, buf, nbytes, offset, buf_index));
2440//
2441// define_operation!(WriteFixed {
2442// fd: RawFd,
2443// buf: *const libc::c_void,
2444// nbytes: u32,
2445// offset: u64,
2446// buf_index: i32,
2447// } => io_uring_prep_write_fixed(fd, buf, nbytes, offset, buf_index));
2448//
2449// define_operation!(Accept {
2450// fd: RawFd,
2451// addr: *mut bindings::sockaddr,
2452// addrlen: *mut libc::socklen_t,
2453// flags: i32,
2454// } => io_uring_prep_accept(fd, addr, addrlen, flags));
2455//
2456// define_operation!(Connect {
2457// fd: RawFd,
2458// addr: *const bindings::sockaddr,
2459// addrlen: libc::socklen_t,
2460// } => io_uring_prep_connect(fd, addr, addrlen));
2461//
2462// define_operation!(Recv {
2463// sockfd: i32,
2464// buf: *mut libc::c_void,
2465// len: usize,
2466// flags: i32,
2467// } => io_uring_prep_recv(sockfd, buf, len, flags));
2468//
2469// define_operation!(Send {
2470// sockfd: i32,
2471// buf: *const libc::c_void,
2472// len: usize,
2473// flags: i32,
2474// } => io_uring_prep_send(sockfd, buf, len, flags));
2475//
2476// define_operation!(Close {
2477// fd: RawFd,
2478// } => io_uring_prep_close(fd));
2479//
2480// define_operation!(Openat {
2481// dfd: RawFd,
2482// path: *const libc::c_char,
2483// flags: i32,
2484// mode: libc::mode_t,
2485// } => io_uring_prep_openat(dfd, path, flags, mode));
2486//
2487// define_operation!(PollAdd {
2488// fd: RawFd,
2489// poll_mask: u32,
2490// } => io_uring_prep_poll_add(fd, poll_mask));
2491//
2492// define_operation!(PollRemove {
2493// user_data: u64,
2494// } => io_uring_prep_poll_remove(user_data));
2495//
2496// define_operation!(Timeout {
2497// ts: *mut bindings::__kernel_timespec,
2498// count: u32,
2499// flags: u32,
2500// } => io_uring_prep_timeout(ts, count, flags));
2501//
2502// define_operation!(TimeoutRemove {
2503// user_data: u64,
2504// flags: u32,
2505// } => io_uring_prep_timeout_remove(user_data, flags));
2506//
2507// // Cancel requires casting u64 to pointer
2508// pub struct Cancel {
2509// pub user_data: u64,
2510// pub flags: i32,
2511// }
2512//
2513// impl UringOperation for Cancel {
2514// fn to_entry(&self) -> crate::submission::Entry {
2515// let mut sqe = unsafe { core::mem::zeroed() };
2516// unsafe {
2517// bindings::io_uring_prep_cancel(
2518// &mut sqe,
2519// self.user_data as *mut libc::c_void,
2520// self.flags,
2521// )
2522// };
2523// crate::submission::Entry::from_sqe(sqe)
2524// }
2525// }
2526//
2527// define_operation!(Readv {
2528// fd: RawFd,
2529// iovecs: *const bindings::iovec,
2530// nr_vecs: u32,
2531// offset: u64,
2532// } => io_uring_prep_readv(fd, iovecs, nr_vecs, offset));
2533//
2534// define_operation!(Writev {
2535// fd: RawFd,
2536// iovecs: *const bindings::iovec,
2537// nr_vecs: u32,
2538// offset: u64,
2539// } => io_uring_prep_writev(fd, iovecs, nr_vecs, offset));
2540//
2541// define_operation!(Recvmsg {
2542// fd: RawFd,
2543// msg: *mut bindings::msghdr,
2544// flags: u32,
2545// } => io_uring_prep_recvmsg(fd, msg, flags));
2546//
2547// define_operation!(Sendmsg {
2548// fd: RawFd,
2549// msg: *const bindings::msghdr,
2550// flags: u32,
2551// } => io_uring_prep_sendmsg(fd, msg, flags));
2552//
2553// define_operation!(Fallocate {
2554// fd: RawFd,
2555// mode: i32,
2556// offset: u64,
2557// len: u64,
2558// } => io_uring_prep_fallocate(fd, mode, offset, len));
2559//
2560// define_operation!(Fadvise {
2561// fd: RawFd,
2562// offset: u64,
2563// len: u32,
2564// advice: i32,
2565// } => io_uring_prep_fadvise(fd, offset, len, advice));
2566//
2567// define_operation!(Madvise {
2568// addr: *mut libc::c_void,
2569// length: u32,
2570// advice: i32,
2571// } => io_uring_prep_madvise(addr, length, advice));
2572//
2573// define_operation!(Splice {
2574// fd_in: RawFd,
2575// off_in: i64,
2576// fd_out: RawFd,
2577// off_out: i64,
2578// nbytes: u32,
2579// splice_flags: u32,
2580// } => io_uring_prep_splice(fd_in, off_in, fd_out, off_out, nbytes, splice_flags));
2581//
2582// define_operation!(Tee {
2583// fd_in: RawFd,
2584// fd_out: RawFd,
2585// nbytes: u32,
2586// splice_flags: u32,
2587// } => io_uring_prep_tee(fd_in, fd_out, nbytes, splice_flags));
2588//
2589// define_operation!(Shutdown {
2590// fd: RawFd,
2591// how: i32,
2592// } => io_uring_prep_shutdown(fd, how));
2593//
2594// define_operation!(Renameat {
2595// olddfd: RawFd,
2596// oldpath: *const libc::c_char,
2597// newdfd: RawFd,
2598// newpath: *const libc::c_char,
2599// flags: u32,
2600// } => io_uring_prep_renameat(olddfd, oldpath, newdfd, newpath, flags));
2601//
2602// define_operation!(Unlinkat {
2603// dfd: RawFd,
2604// path: *const libc::c_char,
2605// flags: i32,
2606// } => io_uring_prep_unlinkat(dfd, path, flags));
2607//
2608// define_operation!(Mkdirat {
2609// dfd: RawFd,
2610// path: *const libc::c_char,
2611// mode: libc::mode_t,
2612// } => io_uring_prep_mkdirat(dfd, path, mode));
2613//
2614// define_operation!(Symlinkat {
2615// target: *const libc::c_char,
2616// newdirfd: RawFd,
2617// linkpath: *const libc::c_char,
2618// } => io_uring_prep_symlinkat(target, newdirfd, linkpath));
2619//
2620// define_operation!(Linkat {
2621// olddfd: RawFd,
2622// oldpath: *const libc::c_char,
2623// newdfd: RawFd,
2624// newpath: *const libc::c_char,
2625// flags: i32,
2626// } => io_uring_prep_linkat(olddfd, oldpath, newdfd, newpath, flags));
2627//
2628// define_operation!(Statx {
2629// dfd: RawFd,
2630// path: *const libc::c_char,
2631// flags: i32,
2632// mask: u32,
2633// statxbuf: *mut bindings::statx,
2634// } => io_uring_prep_statx(dfd, path, flags, mask, statxbuf));
2635//
2636// define_operation!(Fgetxattr {
2637// fd: RawFd,
2638// name: *const libc::c_char,
2639// value: *mut libc::c_char,
2640// len: u32,
2641// } => io_uring_prep_fgetxattr(fd, name, value, len));
2642//
2643// define_operation!(Fsetxattr {
2644// fd: RawFd,
2645// name: *const libc::c_char,
2646// value: *const libc::c_char,
2647// flags: i32,
2648// len: u32,
2649// } => io_uring_prep_fsetxattr(fd, name, value, flags, len));
2650//
2651// define_operation!(Socket {
2652// domain: i32,
2653// sock_type: i32,
2654// protocol: i32,
2655// flags: u32,
2656// } => io_uring_prep_socket(domain, sock_type, protocol, flags));
2657//
2658// define_operation!(SocketDirect {
2659// domain: i32,
2660// sock_type: i32,
2661// protocol: i32,
2662// file_index: u32,
2663// flags: u32,
2664// } => io_uring_prep_socket_direct(domain, sock_type, protocol, file_index, flags));
2665//
2666// define_operation!(RecvMulti {
2667// fd: RawFd,
2668// buf: *mut libc::c_void,
2669// len: usize,
2670// flags: i32,
2671// } => io_uring_prep_recv_multishot(fd, buf, len, flags));
2672//
2673// define_operation!(AcceptMulti {
2674// fd: RawFd,
2675// addr: *mut bindings::sockaddr,
2676// addrlen: *mut libc::socklen_t,
2677// flags: i32,
2678// } => io_uring_prep_multishot_accept(fd, addr, addrlen, flags));
2679//
2680// define_operation!(FilesUpdate {
2681// fds: *mut i32,
2682// nr_fds: u32,
2683// offset: i32,
2684// } => io_uring_prep_files_update(fds, nr_fds, offset));
2685//
2686// define_operation!(Waitid {
2687// idtype: bindings::idtype_t,
2688// id: bindings::id_t,
2689// infop: *mut bindings::siginfo_t,
2690// options: i32,
2691// flags: u32,
2692// } => io_uring_prep_waitid(idtype, id, infop, options, flags));
2693//
2694// define_operation!(Getxattr {
2695// name: *const libc::c_char,
2696// value: *mut libc::c_char,
2697// path: *const libc::c_char,
2698// len: u32,
2699// } => io_uring_prep_getxattr(name, value, path, len));
2700//
2701// define_operation!(Setxattr {
2702// name: *const libc::c_char,
2703// path: *const libc::c_char,
2704// value: *const libc::c_char,
2705// flags: i32,
2706// len: u32,
2707// } => io_uring_prep_setxattr(name, path, value, flags, len));
2708//
2709// define_operation!(SyncFileRange {
2710// fd: RawFd,
2711// len: u32,
2712// offset: u64,
2713// flags: i32,
2714// } => io_uring_prep_sync_file_range(fd, len, offset, flags));
2715//
2716// define_operation!(Epoll {
2717// epfd: RawFd,
2718// fd: RawFd,
2719// op: i32,
2720// ev: *mut bindings::epoll_event,
2721// } => io_uring_prep_epoll_ctl(epfd, fd, op, ev));
2722//
2723// define_operation!(ProvideBuffers {
2724// addr: *mut libc::c_void,
2725// len: i32,
2726// nr: i32,
2727// bgid: i32,
2728// bid: i32,
2729// } => io_uring_prep_provide_buffers(addr, len, nr, bgid, bid));
2730//
2731// define_operation!(RemoveBuffers {
2732// nr: i32,
2733// bgid: i32,
2734// } => io_uring_prep_remove_buffers(nr, bgid));
2735//
2736// define_operation!(MsgRing {
2737// fd: RawFd,
2738// len: u32,
2739// data: u64,
2740// flags: u32,
2741// } => io_uring_prep_msg_ring(fd, len, data, flags));
2742//
2743// define_operation!(SendZc {
2744// sockfd: i32,
2745// buf: *const libc::c_void,
2746// len: usize,
2747// flags: i32,
2748// zc_flags: u32,
2749// } => io_uring_prep_send_zc(sockfd, buf, len, flags, zc_flags));
2750//
2751// define_operation!(SendmsgZc {
2752// fd: RawFd,
2753// msg: *const bindings::msghdr,
2754// flags: u32,
2755// } => io_uring_prep_sendmsg_zc(fd, msg, flags));
2756//
2757// define_operation!(PollUpdate {
2758// old_user_data: u64,
2759// new_user_data: u64,
2760// poll_mask: u32,
2761// flags: u32,
2762// } => io_uring_prep_poll_update(old_user_data, new_user_data, poll_mask, flags));
2763//
2764// define_operation!(Ftruncate {
2765// fd: RawFd,
2766// len: libc::off_t,
2767// } => io_uring_prep_ftruncate(fd, len));
2768//
2769// define_operation!(LinkTimeout {
2770// ts: *mut bindings::__kernel_timespec,
2771// flags: u32,
2772// } => io_uring_prep_link_timeout(ts, flags));
2773//
2774// define_operation!(Bind {
2775// sockfd: i32,
2776// addr: *const bindings::sockaddr,
2777// addrlen: libc::socklen_t,
2778// } => io_uring_prep_bind(sockfd, addr, addrlen));
2779//
2780// define_operation!(Listen {
2781// sockfd: i32,
2782// backlog: i32,
2783// } => io_uring_prep_listen(sockfd, backlog));
2784//
2785// define_operation!(FixedFdInstall {
2786// fd: RawFd,
2787// flags: u32,
2788// } => io_uring_prep_fixed_fd_install(fd, flags));
2789//
2790// define_operation!(SendZcFixed {
2791// sockfd: i32,
2792// buf: *const libc::c_void,
2793// len: usize,
2794// flags: i32,
2795// zc_flags: u32,
2796// buf_index: u32,
2797// } => io_uring_prep_send_zc_fixed(sockfd, buf, len, flags, zc_flags, buf_index));
2798//
2799// define_operation!(Openat2 {
2800// dfd: RawFd,
2801// path: *const libc::c_char,
2802// how: *mut bindings::open_how,
2803// } => io_uring_prep_openat2(dfd, path, how));
2804//
2805// define_operation!(MsgRingCqeFlags {
2806// fd: RawFd,
2807// len: u32,
2808// data: u64,
2809// flags: u32,
2810// cqe_flags: u32,
2811// } => io_uring_prep_msg_ring_cqe_flags(fd, len, data, flags, cqe_flags));
2812//
2813// define_operation!(CloseFixed {
2814// file_index: u32,
2815// } => io_uring_prep_close_direct(file_index));
2816//
2817// define_operation!(ReadFixed2 {
2818// fd: RawFd,
2819// buf: *mut libc::c_void,
2820// nbytes: u32,
2821// offset: u64,
2822// buf_index: i32,
2823// } => io_uring_prep_read_fixed(fd, buf, nbytes, offset, buf_index));
2824//
2825// define_operation!(WriteFixed2 {
2826// fd: RawFd,
2827// buf: *const libc::c_void,
2828// nbytes: u32,
2829// offset: u64,
2830// buf_index: i32,
2831// } => io_uring_prep_write_fixed(fd, buf, nbytes, offset, buf_index));
2832//
2833// // UringCmd takes op and fd only (no arg parameter in basic version)
2834// pub struct UringCmd {
2835// pub op: i32,
2836// pub fd: RawFd,
2837// }
2838//
2839// impl UringOperation for UringCmd {
2840// fn to_entry(&self) -> crate::submission::Entry {
2841// let mut sqe = unsafe { core::mem::zeroed() };
2842// unsafe { bindings::io_uring_prep_uring_cmd(&mut sqe, self.op, self.fd) };
2843// crate::submission::Entry::from_sqe(sqe)
2844// }
2845// }
2846//
2847// define_operation!(FutexWait {
2848// futex: *mut u32,
2849// val: u64,
2850// mask: u64,
2851// futex_flags: u32,
2852// flags: u32,
2853// } => io_uring_prep_futex_wait(futex, val, mask, futex_flags, flags));
2854//
2855// define_operation!(FutexWake {
2856// futex: *mut u32,
2857// val: u64,
2858// mask: u64,
2859// futex_flags: u32,
2860// flags: u32,
2861// } => io_uring_prep_futex_wake(futex, val, mask, futex_flags, flags));
2862//
2863// define_operation!(FutexWaitv {
2864// futex: *mut bindings::futex_waitv,
2865// nr_futex: u32,
2866// flags: u32,
2867// } => io_uring_prep_futex_waitv(futex, nr_futex, flags));
2868
2869#[cfg(test)]
2870mod smoke_tests {
2871 use super::*;
2872 use std::fs::File;
2873 use std::os::fd::AsRawFd;
2874
2875 macro_rules! smoke_test {
2876 ($name:ident, $op:expr) => {
2877 pastey::paste! {
2878 #[test]
2879 fn [<smoke_ $name:snake>]() {
2880 let mut ring = crate::LioUring::new(2).unwrap();
2881 let op = $op;
2882 unsafe { ring.push(op.build(), 0x1234) }.unwrap();
2883 }
2884 }
2885 };
2886 }
2887
2888 #[test]
2889 fn smoke_nop() {
2890 let mut ring = crate::LioUring::new(2).unwrap();
2891 let op = Nop::new();
2892 unsafe { ring.push(op.build(), 0x1234) }.unwrap();
2893 ring.submit().unwrap();
2894 let completion = ring.wait().unwrap();
2895 assert_eq!(completion.user_data(), 0x1234);
2896 assert_eq!(completion.result(), 0);
2897 }
2898
2899 #[test]
2900 fn smoke_read() {
2901 let mut ring = crate::LioUring::new(2).unwrap();
2902 let mut buf = vec![0u8; 1024];
2903 let file = File::open("/dev/null").unwrap();
2904 let op =
2905 Read::new(file.as_raw_fd(), buf.as_mut_ptr().cast(), buf.len() as u32);
2906 unsafe { ring.push(op.build(), 0x1234) }.unwrap();
2907 ring.submit().unwrap();
2908 let completion = ring.wait().unwrap();
2909 assert_eq!(completion.user_data(), 0x1234);
2910 assert_eq!(completion.result(), 0); // /dev/null returns 0 bytes read
2911 }
2912
2913 #[test]
2914 fn smoke_write() {
2915 let mut ring = crate::LioUring::new(2).unwrap();
2916 let buf = b"Hello, io_uring!";
2917 let file = File::create("/tmp/lio_uring_test_write").unwrap();
2918 let op = Write::new(file.as_raw_fd(), buf.as_ptr(), buf.len() as u32);
2919 unsafe { ring.push(op.build(), 0x1234) }.unwrap();
2920 ring.submit().unwrap();
2921 let completion = ring.wait().unwrap();
2922 assert_eq!(completion.user_data(), 0x1234);
2923 assert_eq!(completion.result(), buf.len() as i32);
2924 }
2925
2926 #[test]
2927 fn smoke_fsync() {
2928 let mut ring = crate::LioUring::new(2).unwrap();
2929 let file = File::create("/tmp/lio_uring_test_fsync").unwrap();
2930 let op = Fsync::new(file.as_raw_fd());
2931 unsafe { ring.push(op.build(), 0x1234) }.unwrap();
2932 ring.submit().unwrap();
2933 let completion = ring.wait().unwrap();
2934 assert_eq!(completion.user_data(), 0x1234);
2935 assert_eq!(completion.result(), 0); // fsync returns 0 on success
2936 }
2937
2938 #[test]
2939 fn smoke_close() {
2940 let mut ring = crate::LioUring::new(2).unwrap();
2941 let file = File::create("/tmp/lio_uring_test_close").unwrap();
2942 let fd = file.as_raw_fd();
2943 core::mem::forget(file); // Don't close it normally
2944 let op = Close::new(fd);
2945 unsafe { ring.push(op.build(), 0x1234) }.unwrap();
2946 ring.submit().unwrap();
2947 let completion = ring.wait().unwrap();
2948 assert_eq!(completion.user_data(), 0x1234);
2949 assert_eq!(completion.result(), 0); // close returns 0 on success
2950 }
2951
2952 #[test]
2953 fn smoke_openat() {
2954 let mut ring = crate::LioUring::new(2).unwrap();
2955 let path = b"/tmp/lio_uring_test_open\0";
2956 let op = Openat::new(-100, path.as_ptr().cast()) // AT_FDCWD
2957 .flags(libc::O_CREAT | libc::O_WRONLY)
2958 .mode(0o644);
2959 unsafe { ring.push(op.build(), 0x1234) }.unwrap();
2960 ring.submit().unwrap();
2961 let completion = ring.wait().unwrap();
2962 assert_eq!(completion.user_data(), 0x1234);
2963 assert!(completion.result() >= 0); // openat returns fd >= 0 on success
2964 // Close the fd returned by openat
2965 unsafe { libc::close(completion.result()) };
2966 }
2967
2968 #[test]
2969 fn smoke_readv() {
2970 let mut ring = crate::LioUring::new(2).unwrap();
2971 let mut buf1 = vec![0u8; 512];
2972 let mut buf2 = vec![0u8; 512];
2973 let iovecs = [
2974 libc::iovec { iov_base: buf1.as_mut_ptr().cast(), iov_len: buf1.len() },
2975 libc::iovec { iov_base: buf2.as_mut_ptr().cast(), iov_len: buf2.len() },
2976 ];
2977 let file = File::open("/dev/zero").unwrap();
2978 let op = Readv::new(file.as_raw_fd(), iovecs.as_ptr().cast(), 2);
2979 unsafe { ring.push(op.build(), 0x1234) }.unwrap();
2980 ring.submit().unwrap();
2981 let completion = ring.wait().unwrap();
2982 assert_eq!(completion.user_data(), 0x1234);
2983 assert_eq!(completion.result(), 1024); // Should read 1024 bytes from /dev/zero
2984 }
2985
2986 #[test]
2987 fn smoke_writev() {
2988 let mut ring = crate::LioUring::new(2).unwrap();
2989 let buf1 = b"Hello, ";
2990 let buf2 = b"io_uring!";
2991 let iovecs = [
2992 libc::iovec { iov_base: buf1.as_ptr() as *mut _, iov_len: buf1.len() },
2993 libc::iovec { iov_base: buf2.as_ptr() as *mut _, iov_len: buf2.len() },
2994 ];
2995 let file = File::create("/tmp/lio_uring_test_writev").unwrap();
2996 let op = Writev::new(file.as_raw_fd(), iovecs.as_ptr().cast(), 2);
2997 unsafe { ring.push(op.build(), 0x1234) }.unwrap();
2998 ring.submit().unwrap();
2999 let completion = ring.wait().unwrap();
3000 assert_eq!(completion.user_data(), 0x1234);
3001 assert_eq!(completion.result(), 16); // "Hello, " + "io_uring!" = 16 bytes
3002 }
3003
3004 #[test]
3005 fn smoke_poll_add() {
3006 let mut ring = crate::LioUring::new(2).unwrap();
3007 let file = File::open("/dev/null").unwrap();
3008 let op = PollAdd::new(file.as_raw_fd(), libc::POLLIN as u32);
3009 unsafe { ring.push(op.build(), 0x1234) }.unwrap();
3010 ring.submit().unwrap();
3011 let completion = ring.wait().unwrap();
3012 assert_eq!(completion.user_data(), 0x1234);
3013 // Poll on /dev/null should return immediately with POLLIN set
3014 assert!(completion.result() >= 0);
3015 }
3016
3017 #[test]
3018 fn smoke_fallocate() {
3019 let mut ring = crate::LioUring::new(2).unwrap();
3020 let file = File::create("/tmp/lio_uring_test_fallocate").unwrap();
3021 let op = Fallocate::new(file.as_raw_fd(), 4096);
3022 unsafe { ring.push(op.build(), 0x1234) }.unwrap();
3023 ring.submit().unwrap();
3024 let completion = ring.wait().unwrap();
3025 assert_eq!(completion.user_data(), 0x1234);
3026 assert_eq!(completion.result(), 0); // fallocate returns 0 on success
3027 }
3028
3029 #[test]
3030 fn smoke_fadvise() {
3031 let mut ring = crate::LioUring::new(2).unwrap();
3032 let file = File::open("/dev/null").unwrap();
3033 let op =
3034 Fadvise::new(file.as_raw_fd(), 0, 1024, libc::POSIX_FADV_SEQUENTIAL);
3035 unsafe { ring.push(op.build(), 0x1234) }.unwrap();
3036 ring.submit().unwrap();
3037 let completion = ring.wait().unwrap();
3038 assert_eq!(completion.user_data(), 0x1234);
3039 assert_eq!(completion.result(), 0); // fadvise returns 0 on success
3040 }
3041
3042 #[test]
3043 fn smoke_ftruncate() {
3044 use std::io::Write;
3045 let mut ring = crate::LioUring::new(2).unwrap();
3046 let mut file = File::create("/tmp/lio_uring_test_ftruncate").unwrap();
3047 // Write some data first so we have something to truncate
3048 file.write_all(&[0u8; 2048]).unwrap();
3049 file.flush().unwrap();
3050 let op = Ftruncate::new(file.as_raw_fd(), 1024);
3051 unsafe { ring.push(op.build(), 0x1234) }.unwrap();
3052 ring.submit().unwrap();
3053 let completion = ring.wait().unwrap();
3054 assert_eq!(completion.user_data(), 0x1234);
3055 // ftruncate returns 0 on success, or negative errno
3056 // EINVAL (-22) can occur on some kernel versions, so we just verify we got a completion
3057 println!("ftruncate result: {}", completion.result());
3058 }
3059
3060 smoke_test!(PollRemove, PollRemove::new(0x5678));
3061 smoke_test!(TimeoutRemove, TimeoutRemove::new(0x5678));
3062 smoke_test!(Cancel, Cancel::new(0x5678));
3063 smoke_test!(Recvmsg, Recvmsg::new(0, core::ptr::null_mut()));
3064 smoke_test!(Sendmsg, Sendmsg::new(1, core::ptr::null()));
3065 smoke_test!(Madvise, Madvise::new(core::ptr::null_mut(), 0, 0));
3066 smoke_test!(Splice, Splice::new(0, 1, 0));
3067 smoke_test!(Tee, Tee::new(0, 1, 0));
3068 smoke_test!(Shutdown, Shutdown::new(0, 0));
3069 smoke_test!(
3070 Renameat,
3071 Renameat::new(-100, core::ptr::null(), -100, core::ptr::null())
3072 );
3073 smoke_test!(Unlinkat, Unlinkat::new(-100, core::ptr::null()));
3074 smoke_test!(Mkdirat, Mkdirat::new(-100, core::ptr::null(), 0));
3075 smoke_test!(
3076 Symlinkat,
3077 Symlinkat::new(core::ptr::null(), -100, core::ptr::null())
3078 );
3079 smoke_test!(
3080 Linkat,
3081 Linkat::new(-100, core::ptr::null(), -100, core::ptr::null())
3082 );
3083 smoke_test!(
3084 Statx,
3085 Statx::new(-100, core::ptr::null(), 0, 0, core::ptr::null_mut())
3086 );
3087 smoke_test!(
3088 Fgetxattr,
3089 Fgetxattr::new(0, core::ptr::null(), core::ptr::null_mut(), 0)
3090 );
3091 smoke_test!(
3092 Fsetxattr,
3093 Fsetxattr::new(0, core::ptr::null(), core::ptr::null(), 0, 0)
3094 );
3095 smoke_test!(Socket, Socket::new(2, 1, 0));
3096 smoke_test!(SocketDirect, SocketDirect::new(2, 1, 0, 0));
3097 smoke_test!(RecvMulti, RecvMulti::new(0, core::ptr::null_mut(), 0));
3098 smoke_test!(
3099 AcceptMulti,
3100 AcceptMulti::new(0, core::ptr::null_mut(), core::ptr::null_mut())
3101 );
3102 smoke_test!(FilesUpdate, FilesUpdate::new(core::ptr::null_mut(), 0, 0));
3103 smoke_test!(Waitid, Waitid::new(0, 0, core::ptr::null_mut(), 0));
3104 smoke_test!(
3105 Getxattr,
3106 Getxattr::new(
3107 core::ptr::null(),
3108 core::ptr::null(),
3109 core::ptr::null_mut(),
3110 0
3111 )
3112 );
3113 smoke_test!(
3114 Setxattr,
3115 Setxattr::new(
3116 core::ptr::null(),
3117 core::ptr::null(),
3118 core::ptr::null(),
3119 0,
3120 0
3121 )
3122 );
3123 smoke_test!(SyncFileRange, SyncFileRange::new(0, 0, 0));
3124 smoke_test!(Epoll, Epoll::new(0, 1, 0, core::ptr::null_mut()));
3125 smoke_test!(
3126 ProvideBuffers,
3127 ProvideBuffers::new(core::ptr::null_mut(), 0, 0, 0, 0)
3128 );
3129 smoke_test!(RemoveBuffers, RemoveBuffers::new(0, 0));
3130 smoke_test!(MsgRing, MsgRing::new(0, 0, 0));
3131 smoke_test!(SendZc, SendZc::new(1, core::ptr::null(), 0));
3132 smoke_test!(SendmsgZc, SendmsgZc::new(1, core::ptr::null()));
3133 smoke_test!(PollUpdate, PollUpdate::new(0, 0, 0));
3134 smoke_test!(LinkTimeout, LinkTimeout::new(core::ptr::null_mut()));
3135 smoke_test!(Bind, Bind::new(0, core::ptr::null(), 0));
3136 smoke_test!(Listen, Listen::new(0, 0));
3137 smoke_test!(FixedFdInstall, FixedFdInstall::new(0));
3138 smoke_test!(SendZcFixed, SendZcFixed::new(1, core::ptr::null(), 0, 0));
3139 smoke_test!(
3140 Openat2,
3141 Openat2::new(-100, core::ptr::null(), core::ptr::null_mut())
3142 );
3143 smoke_test!(MsgRingCqeFlags, MsgRingCqeFlags::new(0, 0, 0, 0));
3144 smoke_test!(CloseFixed, CloseFixed::new(0));
3145 smoke_test!(ReadFixed2, ReadFixed2::new(0, core::ptr::null_mut(), 0, 0, 0));
3146 smoke_test!(WriteFixed2, WriteFixed2::new(1, core::ptr::null(), 0, 0, 0));
3147 smoke_test!(UringCmd, UringCmd::new(0, 0));
3148 smoke_test!(FutexWait, FutexWait::new(core::ptr::null_mut(), 0, 0, 0));
3149 smoke_test!(FutexWake, FutexWake::new(core::ptr::null_mut(), 0, 0, 0));
3150 smoke_test!(FutexWaitv, FutexWaitv::new(core::ptr::null_mut(), 0));
3151}