#![allow(clippy::new_without_default)]
use core::convert::TryInto;
use core::mem;
use rustix::fd::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 |= sys::IoringSqeFlags::FIXED_FILE;
}
}
};
}
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, )*
}
}
pub const CODE: sys::IoringOp = $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(always)]
fn sqe_zeroed() -> sys::io_uring_sqe {
Default::default()
}
opcode! {
#[derive(Debug)]
pub struct Nop { ;; }
pub const CODE = sys::IoringOp::Nop;
pub fn build(self) -> Entry {
let Nop {} = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.fd = -1;
Entry(sqe)
}
}
opcode! {
#[derive(Debug)]
pub struct Readv {
fd: { impl sealed::UseFixed },
iovec: { *const sys::iovec },
len: { u32 },
;;
ioprio: u16 = 0,
offset: u64 = 0,
rw_flags: types::RwFlags = types::RwFlags::empty(),
buf_group: u16 = 0
}
pub const CODE = sys::IoringOp::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 = ioprio;
sqe.addr_or_splice_off_in.addr.ptr = iovec as _;
sqe.len.len = len;
sqe.off_or_addr2.off = offset as _;
sqe.op_flags.rw_flags = rw_flags;
sqe.buf.buf_group = buf_group;
Entry(sqe)
}
}
opcode! {
#[derive(Debug)]
pub struct Writev {
fd: { impl sealed::UseFixed },
iovec: { *const sys::iovec },
len: { u32 },
;;
ioprio: u16 = 0,
offset: u64 = 0,
rw_flags: types::RwFlags = types::RwFlags::empty()
}
pub const CODE = sys::IoringOp::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 = ioprio;
sqe.addr_or_splice_off_in.addr.ptr = iovec as _;
sqe.len.len = len;
sqe.off_or_addr2.off = offset as _;
sqe.op_flags.rw_flags = rw_flags;
Entry(sqe)
}
}
opcode! {
#[derive(Debug)]
pub struct Fsync {
fd: { impl sealed::UseFixed },
;;
flags: types::FsyncFlags = types::FsyncFlags::empty()
}
pub const CODE = sys::IoringOp::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.op_flags.fsync_flags = sys::IoringFsyncFlags::from_bits_retain(flags.bits());
Entry(sqe)
}
}
opcode! {
#[derive(Debug)]
pub struct ReadFixed {
fd: { impl sealed::UseFixed },
buf: { *mut u8 },
len: { u32 },
buf_index: { u16 },
;;
ioprio: u16 = 0,
offset: u64 = 0,
rw_flags: types::RwFlags = types::RwFlags::empty()
}
pub const CODE = sys::IoringOp::ReadFixed;
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 = ioprio;
sqe.addr_or_splice_off_in.addr.ptr = buf as _;
sqe.len.len = len;
sqe.off_or_addr2.off = offset as _;
sqe.op_flags.rw_flags = rw_flags;
sqe.buf.buf_index = buf_index;
Entry(sqe)
}
}
opcode! {
#[derive(Debug)]
pub struct WriteFixed {
fd: { impl sealed::UseFixed },
buf: { *const u8 },
len: { u32 },
buf_index: { u16 },
;;
ioprio: u16 = 0,
offset: u64 = 0,
rw_flags: types::RwFlags = types::RwFlags::empty()
}
pub const CODE = sys::IoringOp::WriteFixed;
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 = ioprio;
sqe.addr_or_splice_off_in.addr.ptr = buf as _;
sqe.len.len = len;
sqe.off_or_addr2.off = offset as _;
sqe.op_flags.rw_flags = rw_flags;
sqe.buf.buf_index = buf_index;
Entry(sqe)
}
}
opcode! {
#[derive(Debug)]
pub struct PollAdd {
fd: { impl sealed::UseFixed },
flags: { u32 },
;;
multi: bool = false
}
pub const CODE = sys::IoringOp::PollAdd;
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.poll_flags = sys::IoringPollFlags::ADD_MULTI;
}
#[cfg(target_endian = "little")] {
sqe.op_flags.poll32_events = flags;
}
#[cfg(target_endian = "big")] {
let x = flags << 16;
let y = flags >> 16;
let flags = x | y;
sqe.op_flags.poll32_events = flags;
}
Entry(sqe)
}
}
opcode! {
#[derive(Debug)]
pub struct PollRemove {
user_data: { sys::io_uring_user_data }
;;
}
pub const CODE = sys::IoringOp::PollRemove;
pub fn build(self) -> Entry {
let PollRemove { user_data } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.fd = -1;
sqe.addr_or_splice_off_in.user_data = user_data;
Entry(sqe)
}
}
opcode! {
#[derive(Debug)]
pub struct SyncFileRange {
fd: { impl sealed::UseFixed },
len: { u32 },
;;
offset: u64 = 0,
flags: u32 = 0
}
pub const CODE = sys::IoringOp::SyncFileRange;
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 = len as _;
sqe.off_or_addr2.off = offset as _;
sqe.op_flags.sync_range_flags = flags;
Entry(sqe)
}
}
opcode! {
#[derive(Debug)]
pub struct SendMsg {
fd: { impl sealed::UseFixed },
msg: { *const sys::MsgHdr },
;;
ioprio: u16 = 0,
flags: sys::SendFlags = sys::SendFlags::empty()
}
pub const CODE = sys::IoringOp::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 = ioprio;
sqe.addr_or_splice_off_in.addr.ptr = msg as _;
sqe.len.len = 1;
sqe.op_flags.send_flags = flags;
Entry(sqe)
}
}
opcode! {
#[derive(Debug)]
pub struct RecvMsg {
fd: { impl sealed::UseFixed },
msg: { *mut sys::MsgHdr },
;;
ioprio: u16 = 0,
flags: sys::RecvFlags = sys::RecvFlags::empty(),
buf_group: u16 = 0
}
pub const CODE = sys::IoringOp::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 = ioprio;
sqe.addr_or_splice_off_in.addr.ptr = msg as _;
sqe.len.len = 1;
sqe.op_flags.recv_flags = flags;
sqe.buf.buf_group = buf_group;
Entry(sqe)
}
}
opcode! {
#[derive(Debug)]
pub struct RecvMsgMulti {
fd: { impl sealed::UseFixed },
msg: { *const sys::MsgHdr },
buf_group: { u16 },
;;
ioprio: sys::IoringRecvFlags = sys::IoringRecvFlags::empty(),
flags: sys::RecvFlags = sys::RecvFlags::empty()
}
pub const CODE = sys::IoringOp::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.addr_or_splice_off_in.addr.ptr = msg as _;
sqe.len.len = 1;
sqe.op_flags.recv_flags = flags;
sqe.buf.buf_group = buf_group;
sqe.flags |= sys::IoringSqeFlags::BUFFER_SELECT;
sqe.ioprio.recv_flags = ioprio | sys::IoringRecvFlags::MULTISHOT;
Entry(sqe)
}
}
opcode! {
#[derive(Debug)]
pub struct Timeout {
timespec: { *const types::Timespec },
;;
count: u32 = 0,
flags: types::TimeoutFlags = types::TimeoutFlags::empty()
}
pub const CODE = sys::IoringOp::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.addr_or_splice_off_in.addr.ptr = timespec as _;
sqe.len.len = 1;
sqe.off_or_addr2.off = count as _;
sqe.op_flags.timeout_flags = sys::IoringTimeoutFlags::from_bits_retain(flags.bits());
Entry(sqe)
}
}
opcode! {
pub struct TimeoutRemove {
user_data: { sys::io_uring_user_data }
;;
}
pub const CODE = sys::IoringOp::TimeoutRemove;
pub fn build(self) -> Entry {
let TimeoutRemove { user_data } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.fd = -1;
sqe.addr_or_splice_off_in.user_data = user_data;
Entry(sqe)
}
}
opcode! {
pub struct TimeoutUpdate {
user_data: { sys::io_uring_user_data },
timespec: { *const types::Timespec },
;;
flags: types::TimeoutFlags = types::TimeoutFlags::empty()
}
pub const CODE = sys::IoringOp::TimeoutRemove;
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.off_or_addr2.addr2.ptr = timespec as _;
sqe.addr_or_splice_off_in.user_data = user_data;
sqe.op_flags.timeout_flags = sys::IoringTimeoutFlags::from_bits_retain(flags.bits()) | sys::IoringTimeoutFlags::UPDATE;
Entry(sqe)
}
}
opcode! {
pub struct Accept {
fd: { impl sealed::UseFixed },
addr: { *mut sys::SocketAddrOpaque },
addrlen: { *mut sys::SocketAddrLen },
;;
file_index: Option<types::DestinationSlot> = None,
flags: sys::SocketFlags = sys::SocketFlags::empty()
}
pub const CODE = sys::IoringOp::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.addr_or_splice_off_in.addr.ptr = addr as _;
sqe.off_or_addr2.addr2.ptr = addrlen as _;
sqe.op_flags.accept_flags = flags;
if let Some(dest) = file_index {
sqe.splice_fd_in_or_file_index_or_addr_len.file_index = dest.kernel_index_arg();
}
Entry(sqe)
}
}
opcode! {
pub struct AsyncCancel {
user_data: { sys::io_uring_user_data }
;;
}
pub const CODE = sys::IoringOp::AsyncCancel;
pub fn build(self) -> Entry {
let AsyncCancel { user_data } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.fd = -1;
sqe.addr_or_splice_off_in.user_data = user_data;
Entry(sqe)
}
}
opcode! {
pub struct LinkTimeout {
timespec: { *const types::Timespec },
;;
flags: types::TimeoutFlags = types::TimeoutFlags::empty()
}
pub const CODE = sys::IoringOp::LinkTimeout;
pub fn build(self) -> Entry {
let LinkTimeout { timespec, flags } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.fd = -1;
sqe.addr_or_splice_off_in.addr.ptr = timespec as _;
sqe.len.len = 1;
sqe.op_flags.timeout_flags = sys::IoringTimeoutFlags::from_bits_retain(flags.bits());
Entry(sqe)
}
}
opcode! {
pub struct Connect {
fd: { impl sealed::UseFixed },
addr: { *const sys::SocketAddrOpaque },
addrlen: { sys::SocketAddrLen }
;;
}
pub const CODE = sys::IoringOp::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.addr_or_splice_off_in.addr.ptr = addr as _;
sqe.off_or_addr2.off = addrlen as _;
Entry(sqe)
}
}
opcode! {
pub struct Fallocate {
fd: { impl sealed::UseFixed },
len: { u64 },
;;
offset: u64 = 0,
mode: i32 = 0
}
pub const CODE = sys::IoringOp::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.addr_or_splice_off_in.addr.ptr = len as _;
sqe.len.len = mode as _;
sqe.off_or_addr2.off = offset as _;
Entry(sqe)
}
}
opcode! {
pub struct OpenAt {
dirfd: { impl sealed::UseFd },
pathname: { *const sys::c_char },
;;
file_index: Option<types::DestinationSlot> = None,
flags: sys::OFlags = sys::OFlags::empty(),
mode: sys::Mode = sys::Mode::empty()
}
pub const CODE = sys::IoringOp::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.addr_or_splice_off_in.addr.ptr = pathname as _;
sqe.len.len = mode.bits();
sqe.op_flags.open_flags = flags;
if let Some(dest) = file_index {
sqe.splice_fd_in_or_file_index_or_addr_len.file_index = dest.kernel_index_arg();
}
Entry(sqe)
}
}
opcode! {
pub struct Close {
fd: { impl sealed::UseFixed },
;;
}
pub const CODE = sys::IoringOp::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.splice_fd_in_or_file_index_or_addr_len.file_index = idx + 1;
}
}
Entry(sqe)
}
}
opcode! {
pub struct FilesUpdate {
fds: { *const RawFd },
len: { u32 },
;;
offset: i32 = 0
}
pub const CODE = sys::IoringOp::FilesUpdate;
pub fn build(self) -> Entry {
let FilesUpdate { fds, len, offset } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.fd = -1;
sqe.addr_or_splice_off_in.addr.ptr = fds as _;
sqe.len.len = len;
sqe.off_or_addr2.off = offset as _;
Entry(sqe)
}
}
opcode! {
pub struct Statx {
dirfd: { impl sealed::UseFd },
pathname: { *const sys::c_char },
statxbuf: { *mut types::Statx },
;;
flags: sys::AtFlags = sys::AtFlags::empty(),
mask: sys::StatxFlags = sys::StatxFlags::empty()
}
pub const CODE = sys::IoringOp::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.addr_or_splice_off_in.addr.ptr = pathname as _;
sqe.len.len = mask.bits();
sqe.off_or_addr2.addr2.ptr = statxbuf as _;
sqe.op_flags.statx_flags = flags;
Entry(sqe)
}
}
opcode! {
pub struct Read {
fd: { impl sealed::UseFixed },
buf: { *mut u8 },
len: { u32 },
;;
offset: u64 = 0,
ioprio: u16 = 0,
rw_flags: types::RwFlags = types::RwFlags::empty(),
buf_group: u16 = 0
}
pub const CODE = sys::IoringOp::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 = ioprio;
sqe.addr_or_splice_off_in.addr.ptr = buf as _;
sqe.len.len = len;
sqe.off_or_addr2.off = offset as _;
sqe.op_flags.rw_flags = rw_flags;
sqe.buf.buf_group = buf_group;
Entry(sqe)
}
}
opcode! {
pub struct Write {
fd: { impl sealed::UseFixed },
buf: { *const u8 },
len: { u32 },
;;
offset: u64 = 0,
ioprio: u16 = 0,
rw_flags: types::RwFlags = types::RwFlags::empty()
}
pub const CODE = sys::IoringOp::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 = ioprio;
sqe.addr_or_splice_off_in.addr.ptr = buf as _;
sqe.len.len = len;
sqe.off_or_addr2.off = offset as _;
sqe.op_flags.rw_flags = rw_flags;
Entry(sqe)
}
}
opcode! {
pub struct Fadvise {
fd: { impl sealed::UseFixed },
len: { u32 },
advice: { sys::Advice },
;;
offset: u64 = 0,
}
pub const CODE = sys::IoringOp::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 = len;
sqe.off_or_addr2.off = offset as _;
sqe.op_flags.fadvise_advice = advice;
Entry(sqe)
}
}
opcode! {
pub struct Madvise {
addr: { *const core::ffi::c_void },
len: { u32 },
advice: { sys::Advice },
;;
}
pub const CODE = sys::IoringOp::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.addr_or_splice_off_in.addr.ptr = addr as _;
sqe.len.len = len;
sqe.op_flags.fadvise_advice = advice as _;
Entry(sqe)
}
}
opcode! {
pub struct Send {
fd: { impl sealed::UseFixed },
buf: { *const u8 },
len: { u32 },
;;
flags: sys::SendFlags = sys::SendFlags::empty(),
dest_addr: *const rustix::net::addr::SocketAddrStorage = core::ptr::null(),
dest_addr_len: rustix::net::addr::SocketAddrLen = 0,
}
pub const CODE = sys::IoringOp::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.addr_or_splice_off_in.addr.ptr = buf as _;
sqe.off_or_addr2.addr2.ptr = dest_addr as _;
sqe.splice_fd_in_or_file_index_or_addr_len.addr_len.addr_len = dest_addr_len as _;
sqe.len.len = len;
sqe.op_flags.send_flags = flags;
Entry(sqe)
}
}
opcode! {
pub struct Recv {
fd: { impl sealed::UseFixed },
buf: { *mut u8 },
len: { u32 },
;;
flags: sys::RecvFlags = sys::RecvFlags::empty(),
buf_group: u16 = 0
}
pub const CODE = sys::IoringOp::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.addr_or_splice_off_in.addr.ptr = buf as _;
sqe.len.len = len;
sqe.op_flags.recv_flags = flags;
sqe.buf.buf_group = buf_group;
Entry(sqe)
}
}
opcode! {
pub struct RecvMulti {
fd: { impl sealed::UseFixed },
buf_group: { u16 },
;;
flags: sys::RecvFlags = sys::RecvFlags::empty(),
}
pub const CODE = sys::IoringOp::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.op_flags.recv_flags = flags;
sqe.buf.buf_group = buf_group;
sqe.flags |= sys::IoringSqeFlags::BUFFER_SELECT;
sqe.ioprio.recv_flags = sys::IoringRecvFlags::MULTISHOT;
Entry(sqe)
}
}
opcode! {
pub struct OpenAt2 {
dirfd: { impl sealed::UseFd },
pathname: { *const sys::c_char },
how: { *const types::OpenHow }
;;
file_index: Option<types::DestinationSlot> = None,
}
pub const CODE = sys::IoringOp::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.addr_or_splice_off_in.addr.ptr = pathname as _;
sqe.len.len = mem::size_of::<sys::open_how>() as _;
sqe.off_or_addr2.addr2.ptr = how as _;
if let Some(dest) = file_index {
sqe.splice_fd_in_or_file_index_or_addr_len.file_index = dest.kernel_index_arg();
}
Entry(sqe)
}
}
opcode! {
pub struct EpollCtl {
epfd: { impl sealed::UseFixed },
fd: { impl sealed::UseFd },
op: { i32 },
ev: { *const types::EpollEvent },
;;
}
pub const CODE = sys::IoringOp::EpollCtl;
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.addr_or_splice_off_in.addr.ptr = ev as _;
sqe.len.len = op as _;
sqe.off_or_addr2.off = fd as _;
Entry(sqe)
}
}
opcode! {
pub struct Splice {
fd_in: { impl sealed::UseFixed },
off_in: { i64 },
fd_out: { impl sealed::UseFixed },
off_out: { i64 },
len: { u32 },
;;
flags: sys::SpliceFlags = sys::SpliceFlags::empty()
}
pub const CODE = sys::IoringOp::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 = len;
sqe.off_or_addr2.off = off_out as _;
sqe.splice_fd_in_or_file_index_or_addr_len.splice_fd_in = match fd_in {
sealed::Target::Fd(fd) => fd,
sealed::Target::Fixed(idx) => {
flags |= sys::SpliceFlags::FD_IN_FIXED;
idx as _
}
};
sqe.addr_or_splice_off_in.splice_off_in = off_in as _;
sqe.op_flags.splice_flags = flags;
Entry(sqe)
}
}
opcode! {
pub struct ProvideBuffers {
addr: { *mut u8 },
len: { i32 },
nbufs: { u16 },
bgid: { u16 },
bid: { u16 }
;;
}
pub const CODE = sys::IoringOp::ProvideBuffers;
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.addr_or_splice_off_in.addr.ptr = addr as _;
sqe.len.len = len as _;
sqe.off_or_addr2.off = bid as _;
sqe.buf.buf_group = bgid;
Entry(sqe)
}
}
opcode! {
pub struct RemoveBuffers {
nbufs: { u16 },
bgid: { u16 }
;;
}
pub const CODE = sys::IoringOp::RemoveBuffers;
pub fn build(self) -> Entry {
let RemoveBuffers { nbufs, bgid } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.fd = nbufs as _;
sqe.buf.buf_group = bgid;
Entry(sqe)
}
}
opcode! {
pub struct Tee {
fd_in: { impl sealed::UseFixed },
fd_out: { impl sealed::UseFixed },
len: { u32 }
;;
flags: sys::SpliceFlags = sys::SpliceFlags::empty()
}
pub const CODE = sys::IoringOp::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 = len;
sqe.splice_fd_in_or_file_index_or_addr_len.splice_fd_in = match fd_in {
sealed::Target::Fd(fd) => fd,
sealed::Target::Fixed(idx) => {
flags |= sys::SpliceFlags::FD_IN_FIXED;
idx as _
}
};
sqe.op_flags.splice_flags = flags;
Entry(sqe)
}
}
opcode! {
pub struct Shutdown {
fd: { impl sealed::UseFixed },
how: { i32 },
;;
}
pub const CODE = sys::IoringOp::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.len = how as _;
Entry(sqe)
}
}
opcode! {
pub struct RenameAt {
olddirfd: { impl sealed::UseFd },
oldpath: { *const sys::c_char },
newdirfd: { impl sealed::UseFd },
newpath: { *const sys::c_char },
;;
flags: sys::RenameFlags = sys::RenameFlags::empty()
}
pub const CODE = sys::IoringOp::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.addr_or_splice_off_in.addr.ptr = oldpath as _;
sqe.len.len = newdirfd as _;
sqe.off_or_addr2.addr2.ptr = newpath as _;
sqe.op_flags.rename_flags = flags;
Entry(sqe)
}
}
opcode! {
pub struct UnlinkAt {
dirfd: { impl sealed::UseFd },
pathname: { *const sys::c_char },
;;
flags: sys::AtFlags = sys::AtFlags::empty()
}
pub const CODE = sys::IoringOp::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.addr_or_splice_off_in.addr.ptr = pathname as _;
sqe.op_flags.unlink_flags = flags;
Entry(sqe)
}
}
opcode! {
pub struct MkDirAt {
dirfd: { impl sealed::UseFd },
pathname: { *const sys::c_char },
;;
mode: sys::Mode = sys::Mode::empty()
}
pub const CODE = sys::IoringOp::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.addr_or_splice_off_in.addr.ptr = pathname as _;
sqe.len.len = mode.into();
Entry(sqe)
}
}
opcode! {
pub struct SymlinkAt {
newdirfd: { impl sealed::UseFd },
target: { *const sys::c_char },
linkpath: { *const sys::c_char },
;;
}
pub const CODE = sys::IoringOp::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.addr_or_splice_off_in.addr.ptr = target as _;
sqe.off_or_addr2.addr2.ptr = linkpath as _;
Entry(sqe)
}
}
opcode! {
pub struct LinkAt {
olddirfd: { impl sealed::UseFd },
oldpath: { *const sys::c_char },
newdirfd: { impl sealed::UseFd },
newpath: { *const sys::c_char },
;;
flags: sys::AtFlags = sys::AtFlags::empty()
}
pub const CODE = sys::IoringOp::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.addr_or_splice_off_in.addr.ptr = oldpath as _;
sqe.len.len = newdirfd as _;
sqe.off_or_addr2.addr2.ptr = newpath as _;
sqe.op_flags.hardlink_flags = flags;
Entry(sqe)
}
}
opcode! {
pub struct MsgRingData {
ring_fd: { impl sealed::UseFd },
result: { i32 },
user_data: { sys::io_uring_user_data },
user_flags: { Option<u32> },
;;
opcode_flags: sys::IoringMsgringFlags = sys::IoringMsgringFlags::empty()
}
pub const CODE = sys::IoringOp::MsgRing;
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.addr_or_splice_off_in.msgring_cmd = sys::IoringMsgringCmds::Data;
sqe.fd = ring_fd;
sqe.len.len = result as u32;
sqe.off_or_addr2.user_data = user_data;
sqe.op_flags.msg_ring_flags = opcode_flags;
if let Some(flags) = user_flags {
sqe.splice_fd_in_or_file_index_or_addr_len.file_index = flags;
unsafe {sqe.op_flags.msg_ring_flags |= sys::IoringMsgringFlags::FLAGS_PASS};
}
Entry(sqe)
}
}
opcode! {
pub struct AsyncCancel2 {
builder: { types::CancelBuilder }
;;
}
pub const CODE = sys::IoringOp::AsyncCancel;
pub fn build(self) -> Entry {
let AsyncCancel2 { builder } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.fd = builder.to_fd();
sqe.addr_or_splice_off_in.user_data = builder.user_data;
sqe.op_flags.cancel_flags = sys::IoringAsyncCancelFlags::from_bits_retain(builder.flags.bits());
Entry(sqe)
}
}
opcode! {
pub struct UringCmd16 {
fd: { impl sealed::UseFixed },
cmd_op: { u32 },
;;
buf_index: Option<u16> = None,
cmd: [u8; 16] = [0u8; 16]
}
pub const CODE = sys::IoringOp::UringCmd;
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.off_or_addr2.cmd_op.cmd_op = cmd_op;
unsafe { *sqe.addr3_or_cmd.cmd.as_mut().as_mut_ptr().cast::<[u8; 16]>() = cmd };
if let Some(buf_index) = buf_index {
sqe.buf.buf_index = buf_index;
unsafe {
sqe.op_flags.uring_cmd_flags |= sys::IoringUringCmdFlags::FIXED;
}
}
Entry(sqe)
}
}
opcode! {
pub struct UringCmd80 {
fd: { impl sealed::UseFixed },
cmd_op: { u32 },
;;
buf_index: Option<u16> = None,
cmd: [u8; 80] = [0u8; 80]
}
pub const CODE = sys::IoringOp::UringCmd;
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.off_or_addr2.cmd_op.cmd_op = cmd_op;
unsafe { *sqe.addr3_or_cmd.cmd.as_mut().as_mut_ptr().cast::<[u8; 16]>() = cmd1 };
if let Some(buf_index) = buf_index {
sqe.buf.buf_index = buf_index;
unsafe {
sqe.op_flags.uring_cmd_flags |= sys::IoringUringCmdFlags::FIXED;
}
}
Entry128(Entry(sqe), cmd2)
}
}
opcode! {
pub struct Socket {
domain: { i32 },
socket_type: { i32 },
protocol: { i32 },
;;
file_index: Option<types::DestinationSlot> = None,
flags: types::RwFlags = types::RwFlags::empty(),
}
pub const CODE = sys::IoringOp::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.off_or_addr2.off = socket_type as _;
sqe.len.len = protocol as _;
sqe.op_flags.rw_flags = flags;
if let Some(dest) = file_index {
sqe.splice_fd_in_or_file_index_or_addr_len.file_index = dest.kernel_index_arg();
}
Entry(sqe)
}
}
opcode! {
pub struct AcceptMulti {
fd: { impl sealed::UseFixed },
;;
allocate_file_index: bool = false,
flags: sys::SocketFlags = sys::SocketFlags::empty()
}
pub const CODE = sys::IoringOp::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.accept_flags = sys::IoringAcceptFlags::MULTISHOT;
sqe.op_flags.accept_flags = flags;
if allocate_file_index {
sqe.splice_fd_in_or_file_index_or_addr_len.file_index = sys::IORING_FILE_INDEX_ALLOC as u32;
}
Entry(sqe)
}
}
opcode! {
pub struct MsgRingSendFd {
ring_fd: { impl sealed::UseFd },
fixed_slot_src: { types::Fixed },
dest_slot_index: { types::DestinationSlot },
user_data: { sys::io_uring_user_data }
;;
opcode_flags: sys::IoringMsgringFlags = sys::IoringMsgringFlags::empty()
}
pub const CODE = sys::IoringOp::MsgRing;
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.addr_or_splice_off_in.msgring_cmd = sys::IoringMsgringCmds::SendFd;
sqe.fd = ring_fd;
sqe.off_or_addr2.user_data = user_data;
sqe.addr3_or_cmd.addr3.addr3 = fixed_slot_src.0 as u64;
sqe.splice_fd_in_or_file_index_or_addr_len.file_index = dest_slot_index.kernel_index_arg();
sqe.op_flags.msg_ring_flags = opcode_flags;
Entry(sqe)
}
}
opcode! {
pub struct SendZc {
fd: { impl sealed::UseFixed },
buf: { *const u8 },
len: { u32 },
;;
buf_index: Option<u16> = None,
dest_addr: *const sys::SocketAddrStorage = core::ptr::null(),
dest_addr_len: sys::SocketAddrLen = 0,
flags: sys::SendFlags = sys::SendFlags::empty(),
zc_flags: sys::IoringSendFlags = sys::IoringSendFlags::empty(),
}
pub const CODE = sys::IoringOp::SendZc;
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.addr_or_splice_off_in.addr.ptr = buf as _;
sqe.len.len = len;
sqe.op_flags.send_flags = flags;
sqe.ioprio.send_flags = zc_flags;
if let Some(buf_index) = buf_index {
sqe.buf.buf_index = buf_index;
unsafe { sqe.ioprio.send_flags |= sys::IoringSendFlags::FIXED_BUF; }
}
sqe.off_or_addr2.addr2.ptr = dest_addr as _;
sqe.splice_fd_in_or_file_index_or_addr_len.addr_len.addr_len = dest_addr_len as _;
Entry(sqe)
}
}
opcode! {
#[derive(Debug)]
pub struct SendMsgZc {
fd: { impl sealed::UseFixed },
msg: { *const sys::MsgHdr },
;;
ioprio: u16 = 0,
flags: sys::SendFlags = sys::SendFlags::empty()
}
pub const CODE = sys::IoringOp::SendmsgZc;
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 = ioprio;
sqe.addr_or_splice_off_in.addr.ptr = msg as _;
sqe.len.len = 1;
sqe.op_flags.send_flags = flags;
Entry(sqe)
}
}
opcode! {
#[derive(Debug)]
pub struct FutexWait {
futex: { *const u32 },
val: { u64 },
mask: { u64 },
futex_flags: { sys::FutexWaitFlags },
;;
flags: sys::FutexWaitvFlags = sys::FutexWaitvFlags::empty()
}
pub const CODE = sys::IoringOp::FutexWait;
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.bits() as _;
sqe.addr_or_splice_off_in.addr.ptr = futex as _;
sqe.off_or_addr2.off = val;
sqe.addr3_or_cmd.addr3.addr3 = mask;
sqe.op_flags.futex_flags = flags;
Entry(sqe)
}
}
opcode! {
#[derive(Debug)]
pub struct FutexWake {
futex: { *const u32 },
val: { u64 },
mask: { u64 },
futex_flags: { sys::FutexWaitFlags },
;;
flags: sys::FutexWaitvFlags = sys::FutexWaitvFlags::empty(),
}
pub const CODE = sys::IoringOp::FutexWake;
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.bits() as _;
sqe.addr_or_splice_off_in.addr.ptr = futex as _;
sqe.off_or_addr2.off = val;
sqe.addr3_or_cmd.addr3.addr3 = mask;
sqe.op_flags.futex_flags = flags;
Entry(sqe)
}
}
opcode! {
#[derive(Debug)]
pub struct FutexWaitV {
futexv: { *const types::FutexWaitV },
nr_futex: { u32 },
;;
flags: sys::FutexWaitvFlags = sys::FutexWaitvFlags::empty(),
}
pub const CODE = sys::IoringOp::FutexWaitv;
pub fn build(self) -> Entry {
let FutexWaitV { futexv, nr_futex, flags } = self;
let mut sqe = sqe_zeroed();
sqe.opcode = Self::CODE;
sqe.addr_or_splice_off_in.addr.ptr = futexv as _;
sqe.len.len = nr_futex;
sqe.op_flags.futex_flags = flags;
Entry(sqe)
}
}
opcode! {
#[derive(Debug)]
pub struct FixedFdInstall {
fd: { types::Fixed },
file_flags: { sys::IoringFixedFdFlags },
;;
}
pub const CODE = sys::IoringOp::FixedFdInstall;
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 = sys::IoringSqeFlags::FIXED_FILE;
sqe.op_flags.install_fd_flags = file_flags;
Entry(sqe)
}
}
opcode! {
#[derive(Debug)]
pub struct Ftruncate {
fd: { impl sealed::UseFixed },
len: { u64 },
;;
}
pub const CODE = sys::IoringOp::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.off_or_addr2.off = len;
Entry(sqe)
}
}
opcode! {
pub struct SendBundle {
fd: { impl sealed::UseFixed },
buf_group: { u16 },
;;
flags: sys::SendFlags = sys::SendFlags::empty(),
len: u32 = 0
}
pub const CODE = sys::IoringOp::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 = len;
sqe.op_flags.send_flags = flags;
sqe.ioprio.send_flags = sys::IoringSendFlags::BUNDLE;
sqe.flags |= sys::IoringSqeFlags::BUFFER_SELECT;
sqe.buf.buf_group = buf_group;
Entry(sqe)
}
}
opcode! {
pub struct RecvBundle {
fd: { impl sealed::UseFixed },
buf_group: { u16 },
;;
flags: sys::RecvFlags = sys::RecvFlags::empty(),
}
pub const CODE = sys::IoringOp::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.op_flags.recv_flags = flags;
sqe.buf.buf_group = buf_group;
sqe.flags |= sys::IoringSqeFlags::BUFFER_SELECT;
sqe.ioprio.recv_flags = sys::IoringRecvFlags::BUNDLE;
Entry(sqe)
}
}
opcode! {
pub struct RecvMultiBundle {
fd: { impl sealed::UseFixed },
buf_group: { u16 },
;;
flags: sys::RecvFlags = sys::RecvFlags::empty(),
}
pub const CODE = sys::IoringOp::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.op_flags.recv_flags = flags;
sqe.buf.buf_group = buf_group;
sqe.flags |= sys::IoringSqeFlags::BUFFER_SELECT;
sqe.ioprio.recv_flags = sys::IoringRecvFlags::MULTISHOT | sys::IoringRecvFlags::BUNDLE;
Entry(sqe)
}
}