#[macro_use]
mod util;
pub mod cqueue;
pub mod opcode;
pub mod register;
pub mod squeue;
mod submit;
mod sys;
pub mod types;
use std::marker::PhantomData;
use std::mem::ManuallyDrop;
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
use std::{cmp, io, mem};
#[cfg(feature = "io_safety")]
use std::os::unix::io::{AsFd, BorrowedFd};
pub use cqueue::CompletionQueue;
pub use register::Probe;
pub use squeue::SubmissionQueue;
pub use submit::EnterFlags;
pub use submit::Submitter;
use util::{Mmap, OwnedFd};
pub struct IoUring<S = squeue::Entry, C = cqueue::Entry>
where
S: squeue::EntryMarker,
C: cqueue::EntryMarker,
{
sq: squeue::Inner<S>,
cq: cqueue::Inner<C>,
fd: OwnedFd,
params: Parameters,
memory: ManuallyDrop<MemoryMap>,
}
#[allow(dead_code)]
struct MemoryMap {
sq_mmap: Mmap,
sqe_mmap: Mmap,
cq_mmap: Option<Mmap>,
}
#[derive(Clone, Default)]
pub struct Builder<S = squeue::Entry, C = cqueue::Entry>
where
S: squeue::EntryMarker,
C: cqueue::EntryMarker,
{
dontfork: bool,
params: sys::io_uring_params,
phantom: PhantomData<(S, C)>,
}
#[derive(Clone)]
#[repr(transparent)]
pub struct Parameters(sys::io_uring_params);
unsafe impl<S: squeue::EntryMarker, C: cqueue::EntryMarker> Send for IoUring<S, C> {}
unsafe impl<S: squeue::EntryMarker, C: cqueue::EntryMarker> Sync for IoUring<S, C> {}
impl IoUring<squeue::Entry, cqueue::Entry> {
pub fn new(entries: u32) -> io::Result<Self> {
Self::builder().build(entries)
}
pub unsafe fn from_fd(fd: RawFd, params: Parameters) -> io::Result<Self> {
Self::with_fd_and_params(OwnedFd::from_raw_fd(fd), params.0)
}
}
impl<S: squeue::EntryMarker, C: cqueue::EntryMarker> IoUring<S, C> {
#[must_use]
pub fn builder() -> Builder<S, C> {
Builder {
dontfork: false,
params: sys::io_uring_params {
flags: S::BUILD_FLAGS | C::BUILD_FLAGS,
..Default::default()
},
phantom: PhantomData,
}
}
fn with_params(entries: u32, mut p: sys::io_uring_params) -> io::Result<Self> {
let fd: OwnedFd = unsafe { OwnedFd::from_raw_fd(sys::io_uring_setup(entries, &mut p)?) };
unsafe { Self::with_fd_and_params(fd, p) }
}
unsafe fn with_fd_and_params(fd: OwnedFd, p: sys::io_uring_params) -> io::Result<Self> {
#[inline]
unsafe fn setup_queue<S: squeue::EntryMarker, C: cqueue::EntryMarker>(
fd: &OwnedFd,
p: &sys::io_uring_params,
) -> io::Result<(MemoryMap, squeue::Inner<S>, cqueue::Inner<C>)> {
let sq_len = p.sq_off.array as usize + p.sq_entries as usize * mem::size_of::<u32>();
let cq_len = p.cq_off.cqes as usize + p.cq_entries as usize * mem::size_of::<C>();
let sqe_len = p.sq_entries as usize * mem::size_of::<S>();
let sqe_mmap = Mmap::new(fd, sys::IORING_OFF_SQES as _, sqe_len)?;
if p.features & sys::IORING_FEAT_SINGLE_MMAP != 0 {
let scq_mmap =
Mmap::new(fd, sys::IORING_OFF_SQ_RING as _, cmp::max(sq_len, cq_len))?;
let sq = squeue::Inner::new(&scq_mmap, &sqe_mmap, p);
let cq = cqueue::Inner::new(&scq_mmap, p);
let mm = MemoryMap {
sq_mmap: scq_mmap,
cq_mmap: None,
sqe_mmap,
};
Ok((mm, sq, cq))
} else {
let sq_mmap = Mmap::new(fd, sys::IORING_OFF_SQ_RING as _, sq_len)?;
let cq_mmap = Mmap::new(fd, sys::IORING_OFF_CQ_RING as _, cq_len)?;
let sq = squeue::Inner::new(&sq_mmap, &sqe_mmap, p);
let cq = cqueue::Inner::new(&cq_mmap, p);
let mm = MemoryMap {
cq_mmap: Some(cq_mmap),
sq_mmap,
sqe_mmap,
};
Ok((mm, sq, cq))
}
}
let (mm, sq, cq) = unsafe { setup_queue(&fd, &p)? };
Ok(IoUring {
sq,
cq,
fd,
params: Parameters(p),
memory: ManuallyDrop::new(mm),
})
}
#[inline]
pub fn submitter(&self) -> Submitter<'_> {
Submitter::new(
&self.fd,
&self.params,
self.sq.head,
self.sq.tail,
self.sq.flags,
)
}
#[inline]
pub fn params(&self) -> &Parameters {
&self.params
}
#[inline]
pub fn submit(&self) -> io::Result<usize> {
self.submitter().submit()
}
#[inline]
pub fn submit_and_wait(&self, want: usize) -> io::Result<usize> {
self.submitter().submit_and_wait(want)
}
#[inline]
pub fn split(
&mut self,
) -> (
Submitter<'_>,
SubmissionQueue<'_, S>,
CompletionQueue<'_, C>,
) {
let submit = Submitter::new(
&self.fd,
&self.params,
self.sq.head,
self.sq.tail,
self.sq.flags,
);
(submit, self.sq.borrow(), self.cq.borrow())
}
#[inline]
pub fn submission(&mut self) -> SubmissionQueue<'_, S> {
self.sq.borrow()
}
#[inline]
pub unsafe fn submission_shared(&self) -> SubmissionQueue<'_, S> {
self.sq.borrow_shared()
}
#[inline]
pub fn completion(&mut self) -> CompletionQueue<'_, C> {
self.cq.borrow()
}
#[inline]
pub unsafe fn completion_shared(&self) -> CompletionQueue<'_, C> {
self.cq.borrow_shared()
}
}
impl<S: squeue::EntryMarker, C: cqueue::EntryMarker> Drop for IoUring<S, C> {
fn drop(&mut self) {
unsafe {
ManuallyDrop::drop(&mut self.memory);
}
}
}
impl<S: squeue::EntryMarker, C: cqueue::EntryMarker> Builder<S, C> {
pub fn dontfork(&mut self) -> &mut Self {
self.dontfork = true;
self
}
pub fn setup_iopoll(&mut self) -> &mut Self {
self.params.flags |= sys::IORING_SETUP_IOPOLL;
self
}
pub fn setup_sqpoll(&mut self, idle: u32) -> &mut Self {
self.params.flags |= sys::IORING_SETUP_SQPOLL;
self.params.sq_thread_idle = idle;
self
}
pub fn setup_sqpoll_cpu(&mut self, cpu: u32) -> &mut Self {
self.params.flags |= sys::IORING_SETUP_SQ_AFF;
self.params.sq_thread_cpu = cpu;
self
}
pub fn setup_cqsize(&mut self, entries: u32) -> &mut Self {
self.params.flags |= sys::IORING_SETUP_CQSIZE;
self.params.cq_entries = entries;
self
}
pub fn setup_clamp(&mut self) -> &mut Self {
self.params.flags |= sys::IORING_SETUP_CLAMP;
self
}
pub fn setup_attach_wq(&mut self, fd: RawFd) -> &mut Self {
self.params.flags |= sys::IORING_SETUP_ATTACH_WQ;
self.params.wq_fd = fd as _;
self
}
pub fn setup_r_disabled(&mut self) -> &mut Self {
self.params.flags |= sys::IORING_SETUP_R_DISABLED;
self
}
pub fn setup_submit_all(&mut self) -> &mut Self {
self.params.flags |= sys::IORING_SETUP_SUBMIT_ALL;
self
}
pub fn setup_coop_taskrun(&mut self) -> &mut Self {
self.params.flags |= sys::IORING_SETUP_COOP_TASKRUN;
self
}
pub fn setup_taskrun_flag(&mut self) -> &mut Self {
self.params.flags |= sys::IORING_SETUP_TASKRUN_FLAG;
self
}
pub fn setup_defer_taskrun(&mut self) -> &mut Self {
self.params.flags |= sys::IORING_SETUP_DEFER_TASKRUN;
self
}
pub fn setup_single_issuer(&mut self) -> &mut Self {
self.params.flags |= sys::IORING_SETUP_SINGLE_ISSUER;
self
}
pub fn build(&self, entries: u32) -> io::Result<IoUring<S, C>> {
let ring = IoUring::with_params(entries, self.params)?;
if self.dontfork {
ring.memory.sq_mmap.dontfork()?;
ring.memory.sqe_mmap.dontfork()?;
if let Some(cq_mmap) = ring.memory.cq_mmap.as_ref() {
cq_mmap.dontfork()?;
}
}
Ok(ring)
}
}
impl Parameters {
pub fn is_setup_sqpoll(&self) -> bool {
self.0.flags & sys::IORING_SETUP_SQPOLL != 0
}
pub fn is_setup_iopoll(&self) -> bool {
self.0.flags & sys::IORING_SETUP_IOPOLL != 0
}
pub fn is_setup_single_issuer(&self) -> bool {
self.0.flags & sys::IORING_SETUP_SINGLE_ISSUER != 0
}
pub fn is_feature_single_mmap(&self) -> bool {
self.0.features & sys::IORING_FEAT_SINGLE_MMAP != 0
}
pub fn is_feature_nodrop(&self) -> bool {
self.0.features & sys::IORING_FEAT_NODROP != 0
}
pub fn is_feature_submit_stable(&self) -> bool {
self.0.features & sys::IORING_FEAT_SUBMIT_STABLE != 0
}
pub fn is_feature_rw_cur_pos(&self) -> bool {
self.0.features & sys::IORING_FEAT_RW_CUR_POS != 0
}
pub fn is_feature_cur_personality(&self) -> bool {
self.0.features & sys::IORING_FEAT_CUR_PERSONALITY != 0
}
pub fn is_feature_fast_poll(&self) -> bool {
self.0.features & sys::IORING_FEAT_FAST_POLL != 0
}
pub fn is_feature_poll_32bits(&self) -> bool {
self.0.features & sys::IORING_FEAT_POLL_32BITS != 0
}
pub fn is_feature_sqpoll_nonfixed(&self) -> bool {
self.0.features & sys::IORING_FEAT_SQPOLL_NONFIXED != 0
}
pub fn is_feature_ext_arg(&self) -> bool {
self.0.features & sys::IORING_FEAT_EXT_ARG != 0
}
pub fn is_feature_native_workers(&self) -> bool {
self.0.features & sys::IORING_FEAT_NATIVE_WORKERS != 0
}
pub fn is_feature_resource_tagging(&self) -> bool {
self.0.features & sys::IORING_FEAT_RSRC_TAGS != 0
}
pub fn is_feature_skip_cqe_on_success(&self) -> bool {
self.0.features & sys::IORING_FEAT_CQE_SKIP != 0
}
pub fn is_feature_linked_file(&self) -> bool {
self.0.features & sys::IORING_FEAT_LINKED_FILE != 0
}
pub fn is_feature_recvsend_bundle(&self) -> bool {
self.0.features & sys::IORING_FEAT_RECVSEND_BUNDLE != 0
}
pub fn is_feature_min_timeout(&self) -> bool {
self.0.features & sys::IORING_FEAT_MIN_TIMEOUT != 0
}
pub fn sq_entries(&self) -> u32 {
self.0.sq_entries
}
pub fn sq_thread_idle(&self) -> u32 {
self.0.sq_thread_idle
}
pub fn cq_entries(&self) -> u32 {
self.0.cq_entries
}
}
impl std::fmt::Debug for Parameters {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Parameters")
.field("is_setup_sqpoll", &self.is_setup_sqpoll())
.field("is_setup_iopoll", &self.is_setup_iopoll())
.field("is_setup_single_issuer", &self.is_setup_single_issuer())
.field("is_feature_single_mmap", &self.is_feature_single_mmap())
.field("is_feature_nodrop", &self.is_feature_nodrop())
.field("is_feature_submit_stable", &self.is_feature_submit_stable())
.field("is_feature_rw_cur_pos", &self.is_feature_rw_cur_pos())
.field(
"is_feature_cur_personality",
&self.is_feature_cur_personality(),
)
.field("is_feature_poll_32bits", &self.is_feature_poll_32bits())
.field("sq_entries", &self.0.sq_entries)
.field("cq_entries", &self.0.cq_entries)
.finish()
}
}
impl<S: squeue::EntryMarker, C: cqueue::EntryMarker> AsRawFd for IoUring<S, C> {
fn as_raw_fd(&self) -> RawFd {
self.fd.as_raw_fd()
}
}
#[cfg(feature = "io_safety")]
impl<S: squeue::EntryMarker, C: cqueue::EntryMarker> AsFd for IoUring<S, C> {
fn as_fd(&self) -> BorrowedFd<'_> {
self.fd.as_fd()
}
}