blob: 4e0d009c05b4dedfb8eda13c4d8cf741eac434de [file] [log] [blame] [edit]
//! Operation codes that can be used to construct [`squeue::Entry`](crate::squeue::Entry)s.
#![allow(clippy::new_without_default)]
use std::convert::TryInto;
use std::mem;
use std::os::unix::io::RawFd;
use crate::squeue::Entry;
use crate::squeue::Entry128;
use crate::sys;
use crate::types::{self, sealed};
macro_rules! assign_fd {
( $sqe:ident . fd = $opfd:expr ) => {
match $opfd {
sealed::Target::Fd(fd) => $sqe.fd = fd,
sealed::Target::Fixed(idx) => {
$sqe.fd = idx as _;
$sqe.flags |= crate::squeue::Flags::FIXED_FILE.bits();
}
}
};
}
macro_rules! opcode {
(@type impl sealed::UseFixed ) => {
sealed::Target
};
(@type impl sealed::UseFd ) => {
RawFd
};
(@type $name:ty ) => {
$name
};
(
$( #[$outer:meta] )*
pub struct $name:ident {
$( #[$new_meta:meta] )*
$( $field:ident : { $( $tnt:tt )+ } ),*
$(,)?
;;
$(
$( #[$opt_meta:meta] )*
$opt_field:ident : $opt_tname:ty = $default:expr
),*
$(,)?
}
pub const CODE = $opcode:expr;
$( #[$build_meta:meta] )*
pub fn build($self:ident) -> $entry:ty $build_block:block
) => {
$( #[$outer] )*
pub struct $name {
$( $field : opcode!(@type $( $tnt )*), )*
$( $opt_field : $opt_tname, )*
}
impl $name {
$( #[$new_meta] )*
#[inline]
pub fn new($( $field : $( $tnt )* ),*) -> Self {
$name {
$( $field: $field.into(), )*
$( $opt_field: $default, )*
}
}
/// The opcode of the operation. This can be passed to
/// [`Probe::is_supported`](crate::Probe::is_supported) to check if this operation is
/// supported with the current kernel.
pub const CODE: u8 = $opcode as _;
$(
$( #[$opt_meta] )*
#[inline]
pub const fn $opt_field(mut self, $opt_field: $opt_tname) -> Self {
self.$opt_field = $opt_field;
self
}
)*
$( #[$build_meta] )*
#[inline]
pub fn build($self) -> $entry $build_block
}
}
}
/// inline zeroed to improve codegen
#[inline(always)]
fn sqe_zeroed() -> sys::io_uring_sqe {
unsafe { mem::zeroed() }
}
opcode! {
/// Do not perform any I/O.
///
/// This is useful for testing the performance of the io_uring implementation itself.
#[derive(Debug)]
pub struct Nop { ;; }
pub const CODE = sys::IORING_OP_NOP;
pub fn build(self) -> Entry {
let Nop {} = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.fd = -1;
Entry(sqe)
}
}
opcode! {
/// Vectored read, equivalent to `preadv2(2)`.
#[derive(Debug)]
pub struct Readv {
fd: { impl sealed::UseFixed },
iovec: { *const libc::iovec },
len: { u32 },
;;
ioprio: u16 = 0,
offset: u64 = 0,
/// specified for read operations, contains a bitwise OR of per-I/O flags,
/// as described in the `preadv2(2)` man page.
rw_flags: types::RwFlags = 0,
buf_group: u16 = 0
}
pub const CODE = sys::IORING_OP_READV;
pub fn build(self) -> Entry {
let Readv {
fd,
iovec, len, offset,
ioprio, rw_flags,
buf_group
} = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.ioprio = ioprio;
sqe.__bindgen_anon_2.addr = iovec as _;
sqe.len = len;
sqe.__bindgen_anon_1.off = offset;
sqe.__bindgen_anon_3.rw_flags = rw_flags;
sqe.__bindgen_anon_4.buf_group = buf_group;
Entry(sqe)
}
}
opcode! {
/// Vectored write, equivalent to `pwritev2(2)`.
#[derive(Debug)]
pub struct Writev {
fd: { impl sealed::UseFixed },
iovec: { *const libc::iovec },
len: { u32 },
;;
ioprio: u16 = 0,
offset: u64 = 0,
/// specified for write operations, contains a bitwise OR of per-I/O flags,
/// as described in the `preadv2(2)` man page.
rw_flags: types::RwFlags = 0
}
pub const CODE = sys::IORING_OP_WRITEV;
pub fn build(self) -> Entry {
let Writev {
fd,
iovec, len, offset,
ioprio, rw_flags
} = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.ioprio = ioprio;
sqe.__bindgen_anon_2.addr = iovec as _;
sqe.len = len;
sqe.__bindgen_anon_1.off = offset;
sqe.__bindgen_anon_3.rw_flags = rw_flags;
Entry(sqe)
}
}
opcode! {
/// File sync, equivalent to `fsync(2)`.
///
/// Note that, while I/O is initiated in the order in which it appears in the submission queue,
/// completions are unordered. For example, an application which places a write I/O followed by
/// an fsync in the submission queue cannot expect the fsync to apply to the write. The two
/// operations execute in parallel, so the fsync may complete before the write is issued to the
/// storage. The same is also true for previously issued writes that have not completed prior to
/// the fsync.
#[derive(Debug)]
pub struct Fsync {
fd: { impl sealed::UseFixed },
;;
/// The `flags` bit mask may contain either 0, for a normal file integrity sync,
/// or [types::FsyncFlags::DATASYNC] to provide data sync only semantics.
/// See the descriptions of `O_SYNC` and `O_DSYNC` in the `open(2)` manual page for more information.
flags: types::FsyncFlags = types::FsyncFlags::empty()
}
pub const CODE = sys::IORING_OP_FSYNC;
pub fn build(self) -> Entry {
let Fsync { fd, flags } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.__bindgen_anon_3.fsync_flags = flags.bits();
Entry(sqe)
}
}
opcode! {
/// Read from a file into a fixed buffer that has been previously registered with
/// [`Submitter::register_buffers`](crate::Submitter::register_buffers).
///
/// The return values match those documented in the `preadv2(2)` man pages.
#[derive(Debug)]
pub struct ReadFixed {
fd: { impl sealed::UseFixed },
buf: { *mut u8 },
len: { u32 },
buf_index: { u16 },
;;
ioprio: u16 = 0,
/// The offset of the file to read from.
offset: u64 = 0,
/// Specified for read operations, contains a bitwise OR of per-I/O flags, as described in
/// the `preadv2(2)` man page.
rw_flags: types::RwFlags = 0
}
pub const CODE = sys::IORING_OP_READ_FIXED;
pub fn build(self) -> Entry {
let ReadFixed {
fd,
buf, len, offset,
buf_index,
ioprio, rw_flags
} = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.ioprio = ioprio;
sqe.__bindgen_anon_2.addr = buf as _;
sqe.len = len;
sqe.__bindgen_anon_1.off = offset;
sqe.__bindgen_anon_3.rw_flags = rw_flags;
sqe.__bindgen_anon_4.buf_index = buf_index;
Entry(sqe)
}
}
opcode! {
/// Write to a file from a fixed buffer that have been previously registered with
/// [`Submitter::register_buffers`](crate::Submitter::register_buffers).
///
/// The return values match those documented in the `pwritev2(2)` man pages.
#[derive(Debug)]
pub struct WriteFixed {
fd: { impl sealed::UseFixed },
buf: { *const u8 },
len: { u32 },
buf_index: { u16 },
;;
ioprio: u16 = 0,
/// The offset of the file to write to.
offset: u64 = 0,
/// Specified for write operations, contains a bitwise OR of per-I/O flags, as described in
/// the `pwritev2(2)` man page.
rw_flags: types::RwFlags = 0
}
pub const CODE = sys::IORING_OP_WRITE_FIXED;
pub fn build(self) -> Entry {
let WriteFixed {
fd,
buf, len, offset,
buf_index,
ioprio, rw_flags
} = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.ioprio = ioprio;
sqe.__bindgen_anon_2.addr = buf as _;
sqe.len = len;
sqe.__bindgen_anon_1.off = offset;
sqe.__bindgen_anon_3.rw_flags = rw_flags;
sqe.__bindgen_anon_4.buf_index = buf_index;
Entry(sqe)
}
}
opcode! {
/// Poll the specified fd.
///
/// Unlike poll or epoll without `EPOLLONESHOT`, this interface defaults to work in one shot mode.
/// That is, once the poll operation is completed, it will have to be resubmitted.
///
/// If multi is set, the poll will work in multi shot mode instead. That means it will
/// repeatedly trigger when the requested event becomes true, and hence multiple CQEs can be
/// generated from this single submission. The CQE flags field will have IORING_CQE_F_MORE set
/// on completion if the application should expect further CQE entries from the original
/// request. If this flag isn't set on completion, then the poll request has been terminated
/// and no further events will be generated. This mode is available since 5.13.
#[derive(Debug)]
pub struct PollAdd {
/// The bits that may be set in `flags` are defined in `<poll.h>`,
/// and documented in `poll(2)`.
fd: { impl sealed::UseFixed },
flags: { u32 },
;;
multi: bool = false
}
pub const CODE = sys::IORING_OP_POLL_ADD;
pub fn build(self) -> Entry {
let PollAdd { fd, flags, multi } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
if multi {
sqe.len = sys::IORING_POLL_ADD_MULTI;
}
#[cfg(target_endian = "little")] {
sqe.__bindgen_anon_3.poll32_events = flags;
}
#[cfg(target_endian = "big")] {
let x = flags << 16;
let y = flags >> 16;
let flags = x | y;
sqe.__bindgen_anon_3.poll32_events = flags;
}
Entry(sqe)
}
}
opcode! {
/// Remove an existing [poll](PollAdd) request.
///
/// If found, the `result` method of the `cqueue::Entry` will return 0.
/// If not found, `result` will return `-libc::ENOENT`.
#[derive(Debug)]
pub struct PollRemove {
user_data: { u64 }
;;
}
pub const CODE = sys::IORING_OP_POLL_REMOVE;
pub fn build(self) -> Entry {
let PollRemove { user_data } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.fd = -1;
sqe.__bindgen_anon_2.addr = user_data;
Entry(sqe)
}
}
opcode! {
/// Sync a file segment with disk, equivalent to `sync_file_range(2)`.
#[derive(Debug)]
pub struct SyncFileRange {
fd: { impl sealed::UseFixed },
len: { u32 },
;;
/// the offset method holds the offset in bytes
offset: u64 = 0,
/// the flags method holds the flags for the command
flags: u32 = 0
}
pub const CODE = sys::IORING_OP_SYNC_FILE_RANGE;
pub fn build(self) -> Entry {
let SyncFileRange {
fd,
len, offset,
flags
} = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.len = len;
sqe.__bindgen_anon_1.off = offset;
sqe.__bindgen_anon_3.sync_range_flags = flags;
Entry(sqe)
}
}
opcode! {
/// Send a message on a socket, equivalent to `send(2)`.
///
/// fd must be set to the socket file descriptor, addr must contains a pointer to the msghdr
/// structure, and flags holds the flags associated with the system call.
#[derive(Debug)]
pub struct SendMsg {
fd: { impl sealed::UseFixed },
msg: { *const libc::msghdr },
;;
ioprio: u16 = 0,
flags: u32 = 0
}
pub const CODE = sys::IORING_OP_SENDMSG;
pub fn build(self) -> Entry {
let SendMsg { fd, msg, ioprio, flags } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.ioprio = ioprio;
sqe.__bindgen_anon_2.addr = msg as _;
sqe.len = 1;
sqe.__bindgen_anon_3.msg_flags = flags;
Entry(sqe)
}
}
opcode! {
/// Receive a message on a socket, equivalent to `recvmsg(2)`.
///
/// See also the description of [`SendMsg`].
#[derive(Debug)]
pub struct RecvMsg {
fd: { impl sealed::UseFixed },
msg: { *mut libc::msghdr },
;;
ioprio: u16 = 0,
flags: u32 = 0,
buf_group: u16 = 0
}
pub const CODE = sys::IORING_OP_RECVMSG;
pub fn build(self) -> Entry {
let RecvMsg { fd, msg, ioprio, flags, buf_group } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.ioprio = ioprio;
sqe.__bindgen_anon_2.addr = msg as _;
sqe.len = 1;
sqe.__bindgen_anon_3.msg_flags = flags;
sqe.__bindgen_anon_4.buf_group = buf_group;
Entry(sqe)
}
}
opcode! {
/// Receive multiple messages on a socket, equivalent to `recvmsg(2)`.
///
/// Parameters:
/// msg: For this multishot variant of ResvMsg, only the msg_namelen and msg_controllen
/// fields are relevant.
/// buf_group: The id of the provided buffer pool to use for each received message.
///
/// See also the description of [`SendMsg`] and [`types::RecvMsgOut`].
///
/// The multishot version allows the application to issue a single receive request, which
/// repeatedly posts a CQE when data is available. It requires the MSG_WAITALL flag is not set.
/// Each CQE will take a buffer out of a provided buffer pool for receiving. The application
/// should check the flags of each CQE, regardless of its result. If a posted CQE does not have
/// the IORING_CQE_F_MORE flag set then the multishot receive will be done and the application
/// should issue a new request.
///
/// Unlike [`RecvMsg`], this multishot recvmsg will prepend a struct which describes the layout
/// of the rest of the buffer in combination with the initial msghdr structure submitted with
/// the request. Use [`types::RecvMsgOut`] to parse the data received and access its
/// components.
///
/// The recvmsg multishot variant is available since kernel 6.0.
#[derive(Debug)]
pub struct RecvMsgMulti {
fd: { impl sealed::UseFixed },
msg: { *const libc::msghdr },
buf_group: { u16 },
;;
ioprio: u16 = 0,
flags: u32 = 0
}
pub const CODE = sys::IORING_OP_RECVMSG;
pub fn build(self) -> Entry {
let RecvMsgMulti { fd, msg, buf_group, ioprio, flags } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.__bindgen_anon_2.addr = msg as _;
sqe.len = 1;
sqe.__bindgen_anon_3.msg_flags = flags;
sqe.__bindgen_anon_4.buf_group = buf_group;
sqe.flags |= crate::squeue::Flags::BUFFER_SELECT.bits();
sqe.ioprio = ioprio | (sys::IORING_RECV_MULTISHOT as u16);
Entry(sqe)
}
}
opcode! {
/// Register a timeout operation.
///
/// A timeout will trigger a wakeup event on the completion ring for anyone waiting for events.
/// A timeout condition is met when either the specified timeout expires, or the specified number of events have completed.
/// Either condition will trigger the event.
/// The request will complete with `-ETIME` if the timeout got completed through expiration of the timer,
/// or 0 if the timeout got completed through requests completing on their own.
/// If the timeout was cancelled before it expired, the request will complete with `-ECANCELED`.
#[derive(Debug)]
pub struct Timeout {
timespec: { *const types::Timespec },
;;
/// `count` may contain a completion event count.
/// If [`TimeoutFlags::MULTISHOT`](types::TimeoutFlags::MULTISHOT) is set in `flags`, this is the number of repeats.
/// A value of 0 means the timeout is indefinite and can only be stopped by a removal request.
count: u32 = 0,
flags: types::TimeoutFlags = types::TimeoutFlags::empty()
}
pub const CODE = sys::IORING_OP_TIMEOUT;
pub fn build(self) -> Entry {
let Timeout { timespec, count, flags } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.fd = -1;
sqe.__bindgen_anon_2.addr = timespec as _;
sqe.len = 1;
sqe.__bindgen_anon_1.off = count as _;
sqe.__bindgen_anon_3.timeout_flags = flags.bits();
Entry(sqe)
}
}
// === 5.5 ===
opcode! {
/// Attempt to remove an existing [timeout operation](Timeout).
pub struct TimeoutRemove {
user_data: { u64 },
;;
}
pub const CODE = sys::IORING_OP_TIMEOUT_REMOVE;
pub fn build(self) -> Entry {
let TimeoutRemove { user_data } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.fd = -1;
sqe.__bindgen_anon_2.addr = user_data;
Entry(sqe)
}
}
opcode! {
/// Attempt to update an existing [timeout operation](Timeout) with a new timespec.
/// The optional `count` value of the original timeout value cannot be updated.
pub struct TimeoutUpdate {
user_data: { u64 },
timespec: { *const types::Timespec },
;;
flags: types::TimeoutFlags = types::TimeoutFlags::empty()
}
pub const CODE = sys::IORING_OP_TIMEOUT_REMOVE;
pub fn build(self) -> Entry {
let TimeoutUpdate { user_data, timespec, flags } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.fd = -1;
sqe.__bindgen_anon_1.off = timespec as _;
sqe.__bindgen_anon_2.addr = user_data;
sqe.__bindgen_anon_3.timeout_flags = flags.bits() | sys::IORING_TIMEOUT_UPDATE;
Entry(sqe)
}
}
opcode! {
/// Accept a new connection on a socket, equivalent to `accept4(2)`.
pub struct Accept {
fd: { impl sealed::UseFixed },
addr: { *mut libc::sockaddr },
addrlen: { *mut libc::socklen_t },
;;
file_index: Option<types::DestinationSlot> = None,
flags: i32 = 0
}
pub const CODE = sys::IORING_OP_ACCEPT;
pub fn build(self) -> Entry {
let Accept { fd, addr, addrlen, file_index, flags } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.__bindgen_anon_2.addr = addr as _;
sqe.__bindgen_anon_1.addr2 = addrlen as _;
sqe.__bindgen_anon_3.accept_flags = flags as _;
if let Some(dest) = file_index {
sqe.__bindgen_anon_5.file_index = dest.kernel_index_arg();
}
Entry(sqe)
}
}
opcode! {
/// Set a socket option.
pub struct SetSockOpt {
fd: { impl sealed::UseFixed },
level: { u32 },
optname: { u32 },
optval: { *const libc::c_void },
optlen: { u32 },
;;
flags: u32 = 0
}
pub const CODE = sys::IORING_OP_URING_CMD;
pub fn build(self) -> Entry {
let SetSockOpt { fd, level, optname, optval, optlen, flags } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.__bindgen_anon_1.__bindgen_anon_1.cmd_op = sys::SOCKET_URING_OP_SETSOCKOPT;
sqe.__bindgen_anon_2.__bindgen_anon_1.level = level;
sqe.__bindgen_anon_2.__bindgen_anon_1.optname = optname;
sqe.__bindgen_anon_3.uring_cmd_flags = flags;
sqe.__bindgen_anon_5.optlen = optlen;
unsafe { *sqe.__bindgen_anon_6.optval.as_mut() = optval as u64 };
Entry(sqe)
}
}
opcode! {
/// Attempt to cancel an already issued request.
pub struct AsyncCancel {
user_data: { u64 }
;;
// TODO flags
}
pub const CODE = sys::IORING_OP_ASYNC_CANCEL;
pub fn build(self) -> Entry {
let AsyncCancel { user_data } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.fd = -1;
sqe.__bindgen_anon_2.addr = user_data;
Entry(sqe)
}
}
opcode! {
/// This request must be linked with another request through
/// [`Flags::IO_LINK`](crate::squeue::Flags::IO_LINK) which is described below.
/// Unlike [`Timeout`], [`LinkTimeout`] acts on the linked request, not the completion queue.
pub struct LinkTimeout {
timespec: { *const types::Timespec },
;;
flags: types::TimeoutFlags = types::TimeoutFlags::empty()
}
pub const CODE = sys::IORING_OP_LINK_TIMEOUT;
pub fn build(self) -> Entry {
let LinkTimeout { timespec, flags } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.fd = -1;
sqe.__bindgen_anon_2.addr = timespec as _;
sqe.len = 1;
sqe.__bindgen_anon_3.timeout_flags = flags.bits();
Entry(sqe)
}
}
opcode! {
/// Connect a socket, equivalent to `connect(2)`.
pub struct Connect {
fd: { impl sealed::UseFixed },
addr: { *const libc::sockaddr },
addrlen: { libc::socklen_t }
;;
}
pub const CODE = sys::IORING_OP_CONNECT;
pub fn build(self) -> Entry {
let Connect { fd, addr, addrlen } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.__bindgen_anon_2.addr = addr as _;
sqe.__bindgen_anon_1.off = addrlen as _;
Entry(sqe)
}
}
// === 5.6 ===
opcode! {
/// Preallocate or deallocate space to a file, equivalent to `fallocate(2)`.
pub struct Fallocate {
fd: { impl sealed::UseFixed },
len: { u64 },
;;
offset: u64 = 0,
mode: i32 = 0
}
pub const CODE = sys::IORING_OP_FALLOCATE;
pub fn build(self) -> Entry {
let Fallocate { fd, len, offset, mode } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.__bindgen_anon_2.addr = len;
sqe.len = mode as _;
sqe.__bindgen_anon_1.off = offset;
Entry(sqe)
}
}
opcode! {
/// Open a file, equivalent to `openat(2)`.
pub struct OpenAt {
dirfd: { impl sealed::UseFd },
pathname: { *const libc::c_char },
;;
file_index: Option<types::DestinationSlot> = None,
flags: i32 = 0,
mode: libc::mode_t = 0
}
pub const CODE = sys::IORING_OP_OPENAT;
pub fn build(self) -> Entry {
let OpenAt { dirfd, pathname, file_index, flags, mode } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.fd = dirfd;
sqe.__bindgen_anon_2.addr = pathname as _;
sqe.len = mode;
sqe.__bindgen_anon_3.open_flags = flags as _;
if let Some(dest) = file_index {
sqe.__bindgen_anon_5.file_index = dest.kernel_index_arg();
}
Entry(sqe)
}
}
opcode! {
/// Close a file descriptor, equivalent to `close(2)`.
///
/// Use a types::Fixed(fd) argument to close an io_uring direct descriptor.
pub struct Close {
fd: { impl sealed::UseFixed },
;;
}
pub const CODE = sys::IORING_OP_CLOSE;
pub fn build(self) -> Entry {
let Close { fd } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
match fd {
sealed::Target::Fd(fd) => sqe.fd = fd,
sealed::Target::Fixed(idx) => {
sqe.fd = 0;
sqe.__bindgen_anon_5.file_index = idx + 1;
}
}
Entry(sqe)
}
}
opcode! {
/// This command is an alternative to using
/// [`Submitter::register_files_update`](crate::Submitter::register_files_update) which then
/// works in an async fashion, like the rest of the io_uring commands.
pub struct FilesUpdate {
fds: { *const RawFd },
len: { u32 },
;;
offset: i32 = 0
}
pub const CODE = sys::IORING_OP_FILES_UPDATE;
pub fn build(self) -> Entry {
let FilesUpdate { fds, len, offset } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.fd = -1;
sqe.__bindgen_anon_2.addr = fds as _;
sqe.len = len;
sqe.__bindgen_anon_1.off = offset as _;
Entry(sqe)
}
}
opcode! {
/// Get file status, equivalent to `statx(2)`.
pub struct Statx {
dirfd: { impl sealed::UseFd },
pathname: { *const libc::c_char },
statxbuf: { *mut types::statx },
;;
flags: i32 = 0,
mask: u32 = 0
}
pub const CODE = sys::IORING_OP_STATX;
pub fn build(self) -> Entry {
let Statx {
dirfd, pathname, statxbuf,
flags, mask
} = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.fd = dirfd;
sqe.__bindgen_anon_2.addr = pathname as _;
sqe.len = mask;
sqe.__bindgen_anon_1.off = statxbuf as _;
sqe.__bindgen_anon_3.statx_flags = flags as _;
Entry(sqe)
}
}
opcode! {
/// Issue the equivalent of a `pread(2)` or `pwrite(2)` system call
///
/// * `fd` is the file descriptor to be operated on,
/// * `addr` contains the buffer in question,
/// * `len` contains the length of the IO operation,
///
/// These are non-vectored versions of the `IORING_OP_READV` and `IORING_OP_WRITEV` opcodes.
/// See also `read(2)` and `write(2)` for the general description of the related system call.
///
/// Available since 5.6.
pub struct Read {
fd: { impl sealed::UseFixed },
buf: { *mut u8 },
len: { u32 },
;;
/// `offset` contains the read or write offset.
///
/// If `fd` does not refer to a seekable file, `offset` must be set to zero.
/// If `offset` is set to `-1`, the offset will use (and advance) the file position,
/// like the `read(2)` and `write(2)` system calls.
offset: u64 = 0,
ioprio: u16 = 0,
rw_flags: types::RwFlags = 0,
buf_group: u16 = 0
}
pub const CODE = sys::IORING_OP_READ;
pub fn build(self) -> Entry {
let Read {
fd,
buf, len, offset,
ioprio, rw_flags,
buf_group
} = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.ioprio = ioprio;
sqe.__bindgen_anon_2.addr = buf as _;
sqe.len = len;
sqe.__bindgen_anon_1.off = offset;
sqe.__bindgen_anon_3.rw_flags = rw_flags;
sqe.__bindgen_anon_4.buf_group = buf_group;
Entry(sqe)
}
}
opcode! {
/// Issue the equivalent of a `pread(2)` or `pwrite(2)` system call
///
/// * `fd` is the file descriptor to be operated on,
/// * `addr` contains the buffer in question,
/// * `len` contains the length of the IO operation,
///
/// These are non-vectored versions of the `IORING_OP_READV` and `IORING_OP_WRITEV` opcodes.
/// See also `read(2)` and `write(2)` for the general description of the related system call.
///
/// Available since 5.6.
pub struct Write {
fd: { impl sealed::UseFixed },
buf: { *const u8 },
len: { u32 },
;;
/// `offset` contains the read or write offset.
///
/// If `fd` does not refer to a seekable file, `offset` must be set to zero.
/// If `offsett` is set to `-1`, the offset will use (and advance) the file position,
/// like the `read(2)` and `write(2)` system calls.
offset: u64 = 0,
ioprio: u16 = 0,
rw_flags: types::RwFlags = 0
}
pub const CODE = sys::IORING_OP_WRITE;
pub fn build(self) -> Entry {
let Write {
fd,
buf, len, offset,
ioprio, rw_flags
} = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.ioprio = ioprio;
sqe.__bindgen_anon_2.addr = buf as _;
sqe.len = len;
sqe.__bindgen_anon_1.off = offset;
sqe.__bindgen_anon_3.rw_flags = rw_flags;
Entry(sqe)
}
}
opcode! {
/// Predeclare an access pattern for file data, equivalent to `posix_fadvise(2)`.
pub struct Fadvise {
fd: { impl sealed::UseFixed },
len: { libc::off_t },
advice: { i32 },
;;
offset: u64 = 0,
}
pub const CODE = sys::IORING_OP_FADVISE;
pub fn build(self) -> Entry {
let Fadvise { fd, len, advice, offset } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.len = len as _;
sqe.__bindgen_anon_1.off = offset;
sqe.__bindgen_anon_3.fadvise_advice = advice as _;
Entry(sqe)
}
}
opcode! {
/// Give advice about use of memory, equivalent to `madvise(2)`.
pub struct Madvise {
addr: { *const libc::c_void },
len: { libc::off_t },
advice: { i32 },
;;
}
pub const CODE = sys::IORING_OP_MADVISE;
pub fn build(self) -> Entry {
let Madvise { addr, len, advice } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.fd = -1;
sqe.__bindgen_anon_2.addr = addr as _;
sqe.len = len as _;
sqe.__bindgen_anon_3.fadvise_advice = advice as _;
Entry(sqe)
}
}
opcode! {
/// Send a message on a socket, equivalent to `send(2)`.
pub struct Send {
fd: { impl sealed::UseFixed },
buf: { *const u8 },
len: { u32 },
;;
flags: i32 = 0,
/// Set the destination address, for sending from an unconnected socket.
///
/// When set, `dest_addr_len` must be set as well.
/// See also `man 3 io_uring_prep_send_set_addr`.
dest_addr: *const libc::sockaddr = core::ptr::null(),
dest_addr_len: libc::socklen_t = 0,
}
pub const CODE = sys::IORING_OP_SEND;
pub fn build(self) -> Entry {
let Send { fd, buf, len, flags, dest_addr, dest_addr_len } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.__bindgen_anon_2.addr = buf as _;
sqe.__bindgen_anon_1.addr2 = dest_addr as _;
sqe.__bindgen_anon_5.__bindgen_anon_1.addr_len = dest_addr_len as _;
sqe.len = len;
sqe.__bindgen_anon_3.msg_flags = flags as _;
Entry(sqe)
}
}
opcode! {
/// Receive a message from a socket, equivalent to `recv(2)`.
pub struct Recv {
fd: { impl sealed::UseFixed },
buf: { *mut u8 },
len: { u32 },
;;
flags: i32 = 0,
buf_group: u16 = 0
}
pub const CODE = sys::IORING_OP_RECV;
pub fn build(self) -> Entry {
let Recv { fd, buf, len, flags, buf_group } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.__bindgen_anon_2.addr = buf as _;
sqe.len = len;
sqe.__bindgen_anon_3.msg_flags = flags as _;
sqe.__bindgen_anon_4.buf_group = buf_group;
Entry(sqe)
}
}
opcode! {
/// Receive multiple messages from a socket, equivalent to `recv(2)`.
///
/// Parameter:
/// buf_group: The id of the provided buffer pool to use for each received message.
///
/// MSG_WAITALL should not be set in flags.
///
/// The multishot version allows the application to issue a single receive request, which
/// repeatedly posts a CQE when data is available. Each CQE will take a buffer out of a
/// provided buffer pool for receiving. The application should check the flags of each CQE,
/// regardless of its result. If a posted CQE does not have the IORING_CQE_F_MORE flag set then
/// the multishot receive will be done and the application should issue a new request.
///
/// Multishot variants are available since kernel 6.0.
pub struct RecvMulti {
fd: { impl sealed::UseFixed },
buf_group: { u16 },
;;
flags: i32 = 0,
}
pub const CODE = sys::IORING_OP_RECV;
pub fn build(self) -> Entry {
let RecvMulti { fd, buf_group, flags } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.__bindgen_anon_3.msg_flags = flags as _;
sqe.__bindgen_anon_4.buf_group = buf_group;
sqe.flags |= crate::squeue::Flags::BUFFER_SELECT.bits();
sqe.ioprio = sys::IORING_RECV_MULTISHOT as _;
Entry(sqe)
}
}
opcode! {
/// Open a file, equivalent to `openat2(2)`.
pub struct OpenAt2 {
dirfd: { impl sealed::UseFd },
pathname: { *const libc::c_char },
how: { *const types::OpenHow }
;;
file_index: Option<types::DestinationSlot> = None,
}
pub const CODE = sys::IORING_OP_OPENAT2;
pub fn build(self) -> Entry {
let OpenAt2 { dirfd, pathname, how, file_index } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.fd = dirfd;
sqe.__bindgen_anon_2.addr = pathname as _;
sqe.len = mem::size_of::<sys::open_how>() as _;
sqe.__bindgen_anon_1.off = how as _;
if let Some(dest) = file_index {
sqe.__bindgen_anon_5.file_index = dest.kernel_index_arg();
}
Entry(sqe)
}
}
opcode! {
/// Modify an epoll file descriptor, equivalent to `epoll_ctl(2)`.
pub struct EpollCtl {
epfd: { impl sealed::UseFixed },
fd: { impl sealed::UseFd },
op: { i32 },
ev: { *const types::epoll_event },
;;
}
pub const CODE = sys::IORING_OP_EPOLL_CTL;
pub fn build(self) -> Entry {
let EpollCtl { epfd, fd, op, ev } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = epfd);
sqe.__bindgen_anon_2.addr = ev as _;
sqe.len = op as _;
sqe.__bindgen_anon_1.off = fd as _;
Entry(sqe)
}
}
// === 5.7 ===
opcode! {
/// Splice data to/from a pipe, equivalent to `splice(2)`.
///
/// if `fd_in` refers to a pipe, `off_in` must be `-1`;
/// The description of `off_in` also applied to `off_out`.
pub struct Splice {
fd_in: { impl sealed::UseFixed },
off_in: { i64 },
fd_out: { impl sealed::UseFixed },
off_out: { i64 },
len: { u32 },
;;
/// see man `splice(2)` for description of flags.
flags: u32 = 0
}
pub const CODE = sys::IORING_OP_SPLICE;
pub fn build(self) -> Entry {
let Splice { fd_in, off_in, fd_out, off_out, len, mut flags } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd_out);
sqe.len = len;
sqe.__bindgen_anon_1.off = off_out as _;
sqe.__bindgen_anon_5.splice_fd_in = match fd_in {
sealed::Target::Fd(fd) => fd,
sealed::Target::Fixed(idx) => {
flags |= sys::SPLICE_F_FD_IN_FIXED;
idx as _
}
};
sqe.__bindgen_anon_2.splice_off_in = off_in as _;
sqe.__bindgen_anon_3.splice_flags = flags;
Entry(sqe)
}
}
opcode! {
/// Register `nbufs` buffers that each have the length `len` with ids starting from `bid` in the
/// group `bgid` that can be used for any request. See
/// [`BUFFER_SELECT`](crate::squeue::Flags::BUFFER_SELECT) for more info.
pub struct ProvideBuffers {
addr: { *mut u8 },
len: { i32 },
nbufs: { u16 },
bgid: { u16 },
bid: { u16 }
;;
}
pub const CODE = sys::IORING_OP_PROVIDE_BUFFERS;
pub fn build(self) -> Entry {
let ProvideBuffers { addr, len, nbufs, bgid, bid } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.fd = nbufs as _;
sqe.__bindgen_anon_2.addr = addr as _;
sqe.len = len as _;
sqe.__bindgen_anon_1.off = bid as _;
sqe.__bindgen_anon_4.buf_group = bgid;
Entry(sqe)
}
}
opcode! {
/// Remove some number of buffers from a buffer group. See
/// [`BUFFER_SELECT`](crate::squeue::Flags::BUFFER_SELECT) for more info.
pub struct RemoveBuffers {
nbufs: { u16 },
bgid: { u16 }
;;
}
pub const CODE = sys::IORING_OP_REMOVE_BUFFERS;
pub fn build(self) -> Entry {
let RemoveBuffers { nbufs, bgid } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.fd = nbufs as _;
sqe.__bindgen_anon_4.buf_group = bgid;
Entry(sqe)
}
}
// === 5.8 ===
opcode! {
/// Duplicate pipe content, equivalent to `tee(2)`.
pub struct Tee {
fd_in: { impl sealed::UseFixed },
fd_out: { impl sealed::UseFixed },
len: { u32 }
;;
flags: u32 = 0
}
pub const CODE = sys::IORING_OP_TEE;
pub fn build(self) -> Entry {
let Tee { fd_in, fd_out, len, mut flags } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd_out);
sqe.len = len;
sqe.__bindgen_anon_5.splice_fd_in = match fd_in {
sealed::Target::Fd(fd) => fd,
sealed::Target::Fixed(idx) => {
flags |= sys::SPLICE_F_FD_IN_FIXED;
idx as _
}
};
sqe.__bindgen_anon_3.splice_flags = flags;
Entry(sqe)
}
}
// === 5.11 ===
opcode! {
/// Shut down all or part of a full duplex connection on a socket, equivalent to `shutdown(2)`.
/// Available since kernel 5.11.
pub struct Shutdown {
fd: { impl sealed::UseFixed },
how: { i32 },
;;
}
pub const CODE = sys::IORING_OP_SHUTDOWN;
pub fn build(self) -> Entry {
let Shutdown { fd, how } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.len = how as _;
Entry(sqe)
}
}
opcode! {
// Change the name or location of a file, equivalent to `renameat2(2)`.
// Available since kernel 5.11.
pub struct RenameAt {
olddirfd: { impl sealed::UseFd },
oldpath: { *const libc::c_char },
newdirfd: { impl sealed::UseFd },
newpath: { *const libc::c_char },
;;
flags: u32 = 0
}
pub const CODE = sys::IORING_OP_RENAMEAT;
pub fn build(self) -> Entry {
let RenameAt {
olddirfd, oldpath,
newdirfd, newpath,
flags
} = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.fd = olddirfd;
sqe.__bindgen_anon_2.addr = oldpath as _;
sqe.len = newdirfd as _;
sqe.__bindgen_anon_1.off = newpath as _;
sqe.__bindgen_anon_3.rename_flags = flags;
Entry(sqe)
}
}
opcode! {
// Delete a name and possible the file it refers to, equivalent to `unlinkat(2)`.
// Available since kernel 5.11.
pub struct UnlinkAt {
dirfd: { impl sealed::UseFd },
pathname: { *const libc::c_char },
;;
flags: i32 = 0
}
pub const CODE = sys::IORING_OP_UNLINKAT;
pub fn build(self) -> Entry {
let UnlinkAt { dirfd, pathname, flags } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.fd = dirfd;
sqe.__bindgen_anon_2.addr = pathname as _;
sqe.__bindgen_anon_3.unlink_flags = flags as _;
Entry(sqe)
}
}
// === 5.15 ===
opcode! {
/// Make a directory, equivalent to `mkdirat(2)`.
pub struct MkDirAt {
dirfd: { impl sealed::UseFd },
pathname: { *const libc::c_char },
;;
mode: libc::mode_t = 0
}
pub const CODE = sys::IORING_OP_MKDIRAT;
pub fn build(self) -> Entry {
let MkDirAt { dirfd, pathname, mode } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.fd = dirfd;
sqe.__bindgen_anon_2.addr = pathname as _;
sqe.len = mode;
Entry(sqe)
}
}
opcode! {
/// Create a symlink, equivalent to `symlinkat(2)`.
pub struct SymlinkAt {
newdirfd: { impl sealed::UseFd },
target: { *const libc::c_char },
linkpath: { *const libc::c_char },
;;
}
pub const CODE = sys::IORING_OP_SYMLINKAT;
pub fn build(self) -> Entry {
let SymlinkAt { newdirfd, target, linkpath } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.fd = newdirfd;
sqe.__bindgen_anon_2.addr = target as _;
sqe.__bindgen_anon_1.addr2 = linkpath as _;
Entry(sqe)
}
}
opcode! {
/// Create a hard link, equivalent to `linkat(2)`.
pub struct LinkAt {
olddirfd: { impl sealed::UseFd },
oldpath: { *const libc::c_char },
newdirfd: { impl sealed::UseFd },
newpath: { *const libc::c_char },
;;
flags: i32 = 0
}
pub const CODE = sys::IORING_OP_LINKAT;
pub fn build(self) -> Entry {
let LinkAt { olddirfd, oldpath, newdirfd, newpath, flags } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.fd = olddirfd as _;
sqe.__bindgen_anon_2.addr = oldpath as _;
sqe.len = newdirfd as _;
sqe.__bindgen_anon_1.addr2 = newpath as _;
sqe.__bindgen_anon_3.hardlink_flags = flags as _;
Entry(sqe)
}
}
// === 5.17 ===
opcode! {
/// Get extended attribute, equivalent to `getxattr(2)`.
pub struct GetXattr {
name: { *const libc::c_char },
value: { *mut libc::c_void },
path: { *const libc::c_char },
len: { u32 },
;;
}
pub const CODE = sys::IORING_OP_GETXATTR;
pub fn build(self) -> Entry {
let GetXattr { name, value, path, len } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.__bindgen_anon_2.addr = name as _;
sqe.len = len;
sqe.__bindgen_anon_1.off = value as _;
unsafe { sqe.__bindgen_anon_6.__bindgen_anon_1.as_mut().addr3 = path as _ };
sqe.__bindgen_anon_3.xattr_flags = 0;
Entry(sqe)
}
}
opcode! {
/// Set extended attribute, equivalent to `setxattr(2)`.
pub struct SetXattr {
name: { *const libc::c_char },
value: { *const libc::c_void },
path: { *const libc::c_char },
len: { u32 },
;;
flags: i32 = 0
}
pub const CODE = sys::IORING_OP_SETXATTR;
pub fn build(self) -> Entry {
let SetXattr { name, value, path, flags, len } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.__bindgen_anon_2.addr = name as _;
sqe.len = len;
sqe.__bindgen_anon_1.off = value as _;
unsafe { sqe.__bindgen_anon_6.__bindgen_anon_1.as_mut().addr3 = path as _ };
sqe.__bindgen_anon_3.xattr_flags = flags as _;
Entry(sqe)
}
}
opcode! {
/// Get extended attribute from a file descriptor, equivalent to `fgetxattr(2)`.
pub struct FGetXattr {
fd: { impl sealed::UseFixed },
name: { *const libc::c_char },
value: { *mut libc::c_void },
len: { u32 },
;;
}
pub const CODE = sys::IORING_OP_FGETXATTR;
pub fn build(self) -> Entry {
let FGetXattr { fd, name, value, len } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.__bindgen_anon_2.addr = name as _;
sqe.len = len;
sqe.__bindgen_anon_1.off = value as _;
sqe.__bindgen_anon_3.xattr_flags = 0;
Entry(sqe)
}
}
opcode! {
/// Set extended attribute on a file descriptor, equivalent to `fsetxattr(2)`.
pub struct FSetXattr {
fd: { impl sealed::UseFixed },
name: { *const libc::c_char },
value: { *const libc::c_void },
len: { u32 },
;;
flags: i32 = 0
}
pub const CODE = sys::IORING_OP_FSETXATTR;
pub fn build(self) -> Entry {
let FSetXattr { fd, name, value, flags, len } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.__bindgen_anon_2.addr = name as _;
sqe.len = len;
sqe.__bindgen_anon_1.off = value as _;
sqe.__bindgen_anon_3.xattr_flags = flags as _;
Entry(sqe)
}
}
// === 5.18 ===
opcode! {
/// Send a message (with data) to a target ring.
pub struct MsgRingData {
ring_fd: { impl sealed::UseFd },
result: { i32 },
user_data: { u64 },
user_flags: { Option<u32> },
;;
opcode_flags: u32 = 0
}
pub const CODE = sys::IORING_OP_MSG_RING;
pub fn build(self) -> Entry {
let MsgRingData { ring_fd, result, user_data, user_flags, opcode_flags } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.__bindgen_anon_2.addr = sys::IORING_MSG_DATA.into();
sqe.fd = ring_fd;
sqe.len = result as u32;
sqe.__bindgen_anon_1.off = user_data;
sqe.__bindgen_anon_3.msg_ring_flags = opcode_flags;
if let Some(flags) = user_flags {
sqe.__bindgen_anon_5.file_index = flags;
unsafe {sqe.__bindgen_anon_3.msg_ring_flags |= sys::IORING_MSG_RING_FLAGS_PASS};
}
Entry(sqe)
}
}
// === 5.19 ===
opcode! {
/// Attempt to cancel an already issued request, receiving a cancellation
/// builder, which allows for the new cancel criterias introduced since
/// 5.19.
pub struct AsyncCancel2 {
builder: { types::CancelBuilder }
;;
}
pub const CODE = sys::IORING_OP_ASYNC_CANCEL;
pub fn build(self) -> Entry {
let AsyncCancel2 { builder } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.fd = builder.to_fd();
sqe.__bindgen_anon_2.addr = builder.user_data.unwrap_or(0);
sqe.__bindgen_anon_3.cancel_flags = builder.flags.bits();
Entry(sqe)
}
}
opcode! {
/// A file/device-specific 16-byte command, akin (but not equivalent) to `ioctl(2)`.
pub struct UringCmd16 {
fd: { impl sealed::UseFixed },
cmd_op: { u32 },
;;
/// The `buf_index` is an index into an array of fixed buffers,
/// and is only valid if fixed buffers were registered.
buf_index: Option<u16> = None,
/// Arbitrary command data.
cmd: [u8; 16] = [0u8; 16]
}
pub const CODE = sys::IORING_OP_URING_CMD;
pub fn build(self) -> Entry {
let UringCmd16 { fd, cmd_op, cmd, buf_index } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.__bindgen_anon_1.__bindgen_anon_1.cmd_op = cmd_op;
unsafe { *sqe.__bindgen_anon_6.cmd.as_mut().as_mut_ptr().cast::<[u8; 16]>() = cmd };
if let Some(buf_index) = buf_index {
sqe.__bindgen_anon_4.buf_index = buf_index;
unsafe {
sqe.__bindgen_anon_3.uring_cmd_flags |= sys::IORING_URING_CMD_FIXED;
}
}
Entry(sqe)
}
}
opcode! {
/// A file/device-specific 80-byte command, akin (but not equivalent) to `ioctl(2)`.
pub struct UringCmd80 {
fd: { impl sealed::UseFixed },
cmd_op: { u32 },
;;
/// The `buf_index` is an index into an array of fixed buffers,
/// and is only valid if fixed buffers were registered.
buf_index: Option<u16> = None,
/// Arbitrary command data.
cmd: [u8; 80] = [0u8; 80]
}
pub const CODE = sys::IORING_OP_URING_CMD;
pub fn build(self) -> Entry128 {
let UringCmd80 { fd, cmd_op, cmd, buf_index } = self;
let cmd1 = cmd[..16].try_into().unwrap();
let cmd2 = cmd[16..].try_into().unwrap();
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.__bindgen_anon_1.__bindgen_anon_1.cmd_op = cmd_op;
unsafe { *sqe.__bindgen_anon_6.cmd.as_mut().as_mut_ptr().cast::<[u8; 16]>() = cmd1 };
if let Some(buf_index) = buf_index {
sqe.__bindgen_anon_4.buf_index = buf_index;
unsafe {
sqe.__bindgen_anon_3.uring_cmd_flags |= sys::IORING_URING_CMD_FIXED;
}
}
Entry128(Entry(sqe), cmd2)
}
}
opcode! {
/// Create an endpoint for communication, equivalent to `socket(2)`.
///
/// If the `file_index` argument is set, the resulting socket is
/// directly mapped to the given fixed-file slot instead of being
/// returned as a normal file descriptor. The application must first
/// have registered a file table, and the target slot should fit into
/// it.
///
/// Available since 5.19.
pub struct Socket {
domain: { i32 },
socket_type: { i32 },
protocol: { i32 },
;;
file_index: Option<types::DestinationSlot> = None,
flags: types::RwFlags = 0,
}
pub const CODE = sys::IORING_OP_SOCKET;
pub fn build(self) -> Entry {
let Socket { domain, socket_type, protocol, file_index, flags } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.fd = domain as _;
sqe.__bindgen_anon_1.off = socket_type as _;
sqe.len = protocol as _;
sqe.__bindgen_anon_3.rw_flags = flags;
if let Some(dest) = file_index {
sqe.__bindgen_anon_5.file_index = dest.kernel_index_arg();
}
Entry(sqe)
}
}
opcode! {
/// Accept multiple new connections on a socket.
///
/// Set the `allocate_file_index` property if fixed file table entries should be used.
///
/// Available since 5.19.
pub struct AcceptMulti {
fd: { impl sealed::UseFixed },
;;
allocate_file_index: bool = false,
flags: i32 = 0
}
pub const CODE = sys::IORING_OP_ACCEPT;
pub fn build(self) -> Entry {
let AcceptMulti { fd, allocate_file_index, flags } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.ioprio = sys::IORING_ACCEPT_MULTISHOT as u16;
// No out SockAddr is passed for the multishot accept case.
// The user should perform a syscall to get any resulting connection's remote address.
sqe.__bindgen_anon_3.accept_flags = flags as _;
if allocate_file_index {
sqe.__bindgen_anon_5.file_index = sys::IORING_FILE_INDEX_ALLOC as u32;
}
Entry(sqe)
}
}
// === 6.0 ===
opcode! {
/// Send a message (with fixed FD) to a target ring.
pub struct MsgRingSendFd {
ring_fd: { impl sealed::UseFd },
fixed_slot_src: { types::Fixed },
dest_slot_index: { types::DestinationSlot },
user_data: { u64 },
;;
opcode_flags: u32 = 0
}
pub const CODE = sys::IORING_OP_MSG_RING;
pub fn build(self) -> Entry {
let MsgRingSendFd { ring_fd, fixed_slot_src, dest_slot_index, user_data, opcode_flags } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.__bindgen_anon_2.addr = sys::IORING_MSG_SEND_FD.into();
sqe.fd = ring_fd;
sqe.__bindgen_anon_1.off = user_data;
unsafe { sqe.__bindgen_anon_6.__bindgen_anon_1.as_mut().addr3 = fixed_slot_src.0 as u64 };
sqe.__bindgen_anon_5.file_index = dest_slot_index.kernel_index_arg();
sqe.__bindgen_anon_3.msg_ring_flags = opcode_flags;
Entry(sqe)
}
}
// === 6.0 ===
opcode! {
/// Send a zerocopy message on a socket, equivalent to `send(2)`.
///
/// When `dest_addr` is non-zero it points to the address of the target with `dest_addr_len`
/// specifying its size, turning the request into a `sendto(2)`
///
/// A fixed (pre-mapped) buffer can optionally be used from pre-mapped buffers that have been
/// previously registered with [`Submitter::register_buffers`](crate::Submitter::register_buffers).
///
/// This operation might result in two completion queue entries.
/// See the `IORING_OP_SEND_ZC` section at [io_uring_enter][] for the exact semantics.
/// Notifications posted by this operation can be checked with [notif](crate::cqueue::notif).
///
/// [io_uring_enter]: https://man7.org/linux/man-pages/man2/io_uring_enter.2.html
pub struct SendZc {
fd: { impl sealed::UseFixed },
buf: { *const u8 },
len: { u32 },
;;
/// The `buf_index` is an index into an array of fixed buffers, and is only valid if fixed
/// buffers were registered.
///
/// The buf and len arguments must fall within a region specified by buf_index in the
/// previously registered buffer. The buffer need not be aligned with the start of the
/// registered buffer.
buf_index: Option<u16> = None,
dest_addr: *const libc::sockaddr = core::ptr::null(),
dest_addr_len: libc::socklen_t = 0,
flags: i32 = 0,
zc_flags: u16 = 0,
}
pub const CODE = sys::IORING_OP_SEND_ZC;
pub fn build(self) -> Entry {
let SendZc { fd, buf, len, buf_index, dest_addr, dest_addr_len, flags, zc_flags } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.__bindgen_anon_2.addr = buf as _;
sqe.len = len;
sqe.__bindgen_anon_3.msg_flags = flags as _;
sqe.ioprio = zc_flags;
if let Some(buf_index) = buf_index {
sqe.__bindgen_anon_4.buf_index = buf_index;
sqe.ioprio |= sys::IORING_RECVSEND_FIXED_BUF as u16;
}
sqe.__bindgen_anon_1.addr2 = dest_addr as _;
sqe.__bindgen_anon_5.__bindgen_anon_1.addr_len = dest_addr_len as _;
Entry(sqe)
}
}
// === 6.1 ===
opcode! {
/// Send a zerocopy message on a socket, equivalent to `send(2)`.
///
/// fd must be set to the socket file descriptor, addr must contains a pointer to the msghdr
/// structure, and flags holds the flags associated with the system call.
#[derive(Debug)]
pub struct SendMsgZc {
fd: { impl sealed::UseFixed },
msg: { *const libc::msghdr },
;;
ioprio: u16 = 0,
flags: u32 = 0
}
pub const CODE = sys::IORING_OP_SENDMSG_ZC;
pub fn build(self) -> Entry {
let SendMsgZc { fd, msg, ioprio, flags } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.ioprio = ioprio;
sqe.__bindgen_anon_2.addr = msg as _;
sqe.len = 1;
sqe.__bindgen_anon_3.msg_flags = flags;
Entry(sqe)
}
}
// === 6.7 ===
opcode! {
/// Issue the equivalent of `pread(2)` with multi-shot semantics.
pub struct ReadMulti {
fd: { impl sealed::UseFixed },
len: { u32 },
buf_group: { u16 },
;;
offset: u64 = 0,
}
pub const CODE = sys::IORING_OP_READ_MULTISHOT;
pub fn build(self) -> Entry {
let Self { fd, len, buf_group, offset } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.__bindgen_anon_1.off = offset;
sqe.len = len;
sqe.__bindgen_anon_4.buf_group = buf_group;
sqe.flags = crate::squeue::Flags::BUFFER_SELECT.bits();
Entry(sqe)
}
}
opcode! {
/// Wait on a futex, like but not equivalant to `futex(2)`'s `FUTEX_WAIT_BITSET`.
///
/// Wait on a futex at address `futex` and which still has the value `val` and with `futex2(2)`
/// flags of `futex_flags`. `musk` can be set to a specific bitset mask, which will be matched
/// by the waking side to decide who to wake up. To always get woken, an application may use
/// `FUTEX_BITSET_MATCH_ANY` (truncated to futex bits). `futex_flags` follows the `futex2(2)`
/// flags, not the `futex(2)` v1 interface flags. `flags` are currently unused and hence `0`
/// must be passed.
#[derive(Debug)]
pub struct FutexWait {
futex: { *const u32 },
val: { u64 },
mask: { u64 },
futex_flags: { u32 },
;;
flags: u32 = 0
}
pub const CODE = sys::IORING_OP_FUTEX_WAIT;
pub fn build(self) -> Entry {
let FutexWait { futex, val, mask, futex_flags, flags } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.fd = futex_flags as _;
sqe.__bindgen_anon_2.addr = futex as usize as _;
sqe.__bindgen_anon_1.off = val;
unsafe { sqe.__bindgen_anon_6.__bindgen_anon_1.as_mut().addr3 = mask };
sqe.__bindgen_anon_3.futex_flags = flags;
Entry(sqe)
}
}
opcode! {
/// Wake up waiters on a futex, like but not equivalant to `futex(2)`'s `FUTEX_WAKE_BITSET`.
///
/// Wake any waiters on the futex indicated by `futex` and at most `val` futexes. `futex_flags`
/// indicates the `futex2(2)` modifier flags. If a given bitset for who to wake is desired,
/// then that must be set in `mask`. Use `FUTEX_BITSET_MATCH_ANY` (truncated to futex bits) to
/// match any waiter on the given futex. `flags` are currently unused and hence `0` must be
/// passed.
#[derive(Debug)]
pub struct FutexWake {
futex: { *const u32 },
val: { u64 },
mask: { u64 },
futex_flags: { u32 },
;;
flags: u32 = 0
}
pub const CODE = sys::IORING_OP_FUTEX_WAKE;
pub fn build(self) -> Entry {
let FutexWake { futex, val, mask, futex_flags, flags } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.fd = futex_flags as _;
sqe.__bindgen_anon_2.addr = futex as usize as _;
sqe.__bindgen_anon_1.off = val;
unsafe { sqe.__bindgen_anon_6.__bindgen_anon_1.as_mut().addr3 = mask };
sqe.__bindgen_anon_3.futex_flags = flags;
Entry(sqe)
}
}
opcode! {
/// Wait on multiple futexes.
///
/// Wait on multiple futexes at the same time. Futexes are given by `futexv` and `nr_futex` is
/// the number of futexes in that array. Unlike `FutexWait`, the desired bitset mask and values
/// are passed in `futexv`. `flags` are currently unused and hence `0` must be passed.
#[derive(Debug)]
pub struct FutexWaitV {
futexv: { *const types::FutexWaitV },
nr_futex: { u32 },
;;
flags: u32 = 0
}
pub const CODE = sys::IORING_OP_FUTEX_WAITV;
pub fn build(self) -> Entry {
let FutexWaitV { futexv, nr_futex, flags } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.__bindgen_anon_2.addr = futexv as usize as _;
sqe.len = nr_futex;
sqe.__bindgen_anon_3.futex_flags = flags;
Entry(sqe)
}
}
opcode! {
/// Issue the equivalent of a `waitid(2)` system call.
///
/// Available since kernel 6.7.
#[derive(Debug)]
pub struct WaitId {
idtype: { libc::idtype_t },
id: { libc::id_t },
options: { libc::c_int },
;;
infop: *const libc::siginfo_t = std::ptr::null(),
flags: libc::c_uint = 0,
}
pub const CODE = sys::IORING_OP_WAITID;
pub fn build(self) -> Entry {
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.fd = self.id as _;
sqe.len = self.idtype as _;
sqe.__bindgen_anon_3.waitid_flags = self.flags;
sqe.__bindgen_anon_5.file_index = self.options as _;
sqe.__bindgen_anon_1.addr2 = self.infop as _;
Entry(sqe)
}
}
// === 6.8 ===
opcode! {
/// Install a fixed file descriptor
///
/// Turns a direct descriptor into a regular file descriptor that can be later used by regular
/// system calls that take a normal raw file descriptor
#[derive(Debug)]
pub struct FixedFdInstall {
fd: { types::Fixed },
file_flags: { u32 },
;;
}
pub const CODE = sys::IORING_OP_FIXED_FD_INSTALL;
pub fn build(self) -> Entry {
let FixedFdInstall { fd, file_flags } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.fd = fd.0 as _;
sqe.flags = crate::squeue::Flags::FIXED_FILE.bits();
sqe.__bindgen_anon_3.install_fd_flags = file_flags;
Entry(sqe)
}
}
// === 6.9 ===
opcode! {
/// Perform file truncation, equivalent to `ftruncate(2)`.
#[derive(Debug)]
pub struct Ftruncate {
fd: { impl sealed::UseFixed },
len: { u64 },
;;
}
pub const CODE = sys::IORING_OP_FTRUNCATE;
pub fn build(self) -> Entry {
let Ftruncate { fd, len } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.__bindgen_anon_1.off = len;
Entry(sqe)
}
}
// === 6.10 ===
opcode! {
/// Send a bundle of messages on a socket in a single request.
pub struct SendBundle {
fd: { impl sealed::UseFixed },
buf_group: { u16 },
;;
flags: i32 = 0,
len: u32 = 0
}
pub const CODE = sys::IORING_OP_SEND;
pub fn build(self) -> Entry {
let SendBundle { fd, len, flags, buf_group } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.len = len;
sqe.__bindgen_anon_3.msg_flags = flags as _;
sqe.ioprio |= sys::IORING_RECVSEND_BUNDLE as u16;
sqe.flags |= crate::squeue::Flags::BUFFER_SELECT.bits();
sqe.__bindgen_anon_4.buf_group = buf_group;
Entry(sqe)
}
}
opcode! {
/// Receive a bundle of buffers from a socket.
///
/// Parameter
/// buf_group: The id of the provided buffer pool to use for the bundle.
///
/// Note that as of kernel 6.10 first recv always gets a single buffer, while second
/// obtains the bundle of remaining buffers. This behavior may change in the future.
///
/// Bundle variant is available since kernel 6.10
pub struct RecvBundle {
fd: { impl sealed::UseFixed },
buf_group: { u16 },
;;
flags: i32 = 0
}
pub const CODE = sys::IORING_OP_RECV;
pub fn build(self) -> Entry {
let RecvBundle { fd, buf_group, flags } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.__bindgen_anon_3.msg_flags = flags as _;
sqe.__bindgen_anon_4.buf_group = buf_group;
sqe.flags |= crate::squeue::Flags::BUFFER_SELECT.bits();
sqe.ioprio |= sys::IORING_RECVSEND_BUNDLE as u16;
Entry(sqe)
}
}
opcode! {
/// Receive multiple messages from a socket as a bundle.
///
/// Parameter:
/// buf_group: The id of the provided buffer pool to use for each received message.
///
/// MSG_WAITALL should not be set in flags.
///
/// The multishot version allows the application to issue a single receive request, which
/// repeatedly posts a CQE when data is available. Each CQE will take a bundle of buffers
/// out of a provided buffer pool for receiving. The application should check the flags of each CQE,
/// regardless of its result. If a posted CQE does not have the IORING_CQE_F_MORE flag set then
/// the multishot receive will be done and the application should issue a new request.
///
/// Note that as of kernel 6.10 first CQE always gets a single buffer, while second
/// obtains the bundle of remaining buffers. This behavior may change in the future.
///
/// Multishot bundle variant is available since kernel 6.10.
pub struct RecvMultiBundle {
fd: { impl sealed::UseFixed },
buf_group: { u16 },
;;
flags: i32 = 0
}
pub const CODE = sys::IORING_OP_RECV;
pub fn build(self) -> Entry {
let RecvMultiBundle { fd, buf_group, flags } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.__bindgen_anon_3.msg_flags = flags as _;
sqe.__bindgen_anon_4.buf_group = buf_group;
sqe.flags |= crate::squeue::Flags::BUFFER_SELECT.bits();
sqe.ioprio = sys::IORING_RECV_MULTISHOT as _;
sqe.ioprio |= sys::IORING_RECVSEND_BUNDLE as u16;
Entry(sqe)
}
}
// === 6.11 ===
opcode! {
/// Bind a socket, equivalent to `bind(2)`.
pub struct Bind {
fd: { impl sealed::UseFixed },
addr: { *const libc::sockaddr },
addrlen: { libc::socklen_t }
;;
}
pub const CODE = sys::IORING_OP_BIND;
pub fn build(self) -> Entry {
let Bind { fd, addr, addrlen } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.__bindgen_anon_2.addr = addr as _;
sqe.__bindgen_anon_1.off = addrlen as _;
Entry(sqe)
}
}
opcode! {
/// Listen on a socket, equivalent to `listen(2)`.
pub struct Listen {
fd: { impl sealed::UseFixed },
backlog: { i32 },
;;
}
pub const CODE = sys::IORING_OP_LISTEN;
pub fn build(self) -> Entry {
let Listen { fd, backlog } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.len = backlog as _;
Entry(sqe)
}
}
// === 6.15 ===
opcode! {
/// Issue the zerocopy equivalent of a `recv(2)` system call.
pub struct RecvZc {
fd: { impl sealed::UseFixed },
len: { u32 },
;;
ifq: u32 = 0,
ioprio: u16 = 0,
}
pub const CODE = sys::IORING_OP_RECV_ZC;
pub fn build(self) -> Entry {
let Self { fd, len, ifq, ioprio } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.len = len;
sqe.ioprio = ioprio | sys::IORING_RECV_MULTISHOT as u16;
sqe.__bindgen_anon_5.zcrx_ifq_idx = ifq;
Entry(sqe)
}
}
opcode! {
/// Issue the equivalent of a `epoll_wait(2)` system call.
pub struct EpollWait {
fd: { impl sealed::UseFixed },
events: { *mut types::epoll_event },
max_events: { u32 },
;;
flags: u32 = 0,
}
pub const CODE = sys::IORING_OP_EPOLL_WAIT;
pub fn build(self) -> Entry {
let Self { fd, events, max_events, flags } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.__bindgen_anon_2.addr = events as u64;
sqe.len = max_events;
sqe.__bindgen_anon_3.poll32_events = flags;
Entry(sqe)
}
}
opcode! {
/// Vectored read into a fixed buffer, equivalent to `preadv2(2)`.
pub struct ReadvFixed {
fd: { impl sealed::UseFixed },
iovec: { *const ::libc::iovec },
len: { u32 },
buf_index: { u16 },
;;
ioprio: u16 = 0,
offset: u64 = 0,
rw_flags: i32 = 0,
}
pub const CODE = sys::IORING_OP_READV_FIXED;
pub fn build(self) -> Entry {
let Self { fd, iovec, len, buf_index, offset, ioprio, rw_flags } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.__bindgen_anon_1.off = offset as _;
sqe.__bindgen_anon_2.addr = iovec as _;
sqe.len = len;
sqe.__bindgen_anon_4.buf_index = buf_index;
sqe.ioprio = ioprio;
sqe.__bindgen_anon_3.rw_flags = rw_flags;
Entry(sqe)
}
}
opcode! {
/// Vectored write from a fixed buffer, equivalent to `pwritev2(2)`.
pub struct WritevFixed {
fd: { impl sealed::UseFixed },
iovec: { *const ::libc::iovec },
len: { u32 },
buf_index: { u16 },
;;
ioprio: u16 = 0,
offset: u64 = 0,
rw_flags: i32 = 0,
}
pub const CODE = sys::IORING_OP_WRITEV_FIXED;
pub fn build(self) -> Entry {
let Self { fd, iovec, len, buf_index, offset, ioprio, rw_flags } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
assign_fd!(sqe.fd = fd);
sqe.__bindgen_anon_1.off = offset as _;
sqe.__bindgen_anon_2.addr = iovec as _;
sqe.len = len;
sqe.__bindgen_anon_4.buf_index = buf_index;
sqe.ioprio = ioprio;
sqe.__bindgen_anon_3.rw_flags = rw_flags;
Entry(sqe)
}
}
// === 6.16 ===
opcode! {
// Create a pipe, equivalent to `pipe(2)`.
pub struct Pipe {
fds: { *mut RawFd },
;;
flags: u32 = 0,
file_index: Option<types::DestinationSlot> = None,
}
pub const CODE = sys::IORING_OP_PIPE;
pub fn build(self) -> Entry {
let Self { fds, flags, file_index } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.fd = 0;
sqe.__bindgen_anon_2.addr = fds as _;
sqe.__bindgen_anon_3.pipe_flags = flags;
if let Some(dest) = file_index {
sqe.__bindgen_anon_5.file_index = dest.kernel_index_arg();
}
Entry(sqe)
}
}