//! Linux [io_uring]. //! //! This API is very low-level. The main adaptations it makes from the raw //! Linux io_uring API are the use of appropriately-sized `bitflags`, `enum`, //! `Result`, `OwnedFd`, `AsFd`, `RawFd`, and `*mut c_void` in place of plain //! integers. //! //! For a higher-level API built on top of this, see the [rustix-uring] crate. //! //! # Safety //! //! io_uring operates on raw pointers and raw file descriptors. Rustix does not //! attempt to provide a safe API for these, because the abstraction level is //! too low for this to be practical. Safety should be introduced in //! higher-level abstraction layers. //! //! # References //! - [Linux] //! - [io_uring header] //! //! [Linux]: https://www.man7.org/linux/man-pages/man7/io_uring.7.html //! [io_uring]: https://en.wikipedia.org/wiki/Io_uring //! [io_uring header]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/io_uring.h?h=v6.13 //! [rustix-uring]: https://crates.io/crates/rustix-uring #![allow(unsafe_code)]
mod bindgen_types;
usecrate::fd::{AsFd, BorrowedFd, OwnedFd, RawFd}; usecrate::utils::option_as_ptr; usecrate::{backend, io}; use bindgen_types::*; use core::cmp::Ordering; use core::ffi::c_void; use core::hash::{Hash, Hasher}; use core::mem::size_of; use core::ptr::null_mut; use linux_raw_sys::net;
// Export types used in io_uring APIs. pubusecrate::clockid::ClockId; pubusecrate::event::epoll::{
Event as EpollEvent, EventData as EpollEventData, EventFlags as EpollEventFlags,
}; pubusecrate::ffi::c_char; pubusecrate::fs::{
Advice, AtFlags, Mode, OFlags, RenameFlags, ResolveFlags, Statx, StatxFlags, XattrFlags,
}; pubusecrate::io::ReadWriteFlags; pubusecrate::kernel_sigset::KernelSigSet; pubusecrate::net::addr::{SocketAddrLen, SocketAddrOpaque, SocketAddrStorage}; pubusecrate::net::{RecvFlags, SendFlags, SocketFlags}; pubusecrate::signal::Signal; pubusecrate::thread::futex::{
Wait as FutexWait, WaitFlags as FutexWaitFlags, WaitPtr as FutexWaitPtr,
WaitvFlags as FutexWaitvFlags,
}; pubusecrate::timespec::{Nsecs, Secs, Timespec};
mod sys { pub(super) use linux_raw_sys::io_uring::*; #[cfg(test)] pub(super) use { crate::backend::c::iovec, linux_raw_sys::general::open_how, linux_raw_sys::net::msghdr,
};
}
/// `io_uring_setup(entries, params)`—Setup a context for performing /// asynchronous I/O. /// /// # Safety /// /// If [`IoringSetupFlags::ATTACH_WQ`] is set, the `wq_fd` field of /// `io_uring_params` must be an open file descriptor. /// /// # References /// - [Linux] /// /// [Linux]: https://www.man7.org/linux/man-pages/man2/io_uring_setup.2.html #[inline] pubunsafefn io_uring_setup(entries: u32, params: &mut io_uring_params) -> io::Result<OwnedFd> {
backend::io_uring::syscalls::io_uring_setup(entries, params)
}
/// `io_uring_register(fd, opcode, arg, nr_args)`—Register files or user /// buffers for asynchronous I/O. /// /// To pass flags, use [`io_uring_register_with`]. /// /// # Safety /// /// io_uring operates on raw pointers and raw file descriptors. Users are /// responsible for ensuring that memory and resources are only accessed in /// valid ways. /// /// If `opcode` is `IoringRegisterOp::RegisterRingFds`, `arg` must point to /// mutable memory, despite being `*const`. /// /// # References /// - [Linux] /// /// [Linux]: https://www.man7.org/linux/man-pages/man2/io_uring_register.2.html #[inline] pubunsafefn io_uring_register<Fd: AsFd>(
fd: Fd,
opcode: IoringRegisterOp,
arg: *const c_void,
nr_args: u32,
) -> io::Result<u32> {
backend::io_uring::syscalls::io_uring_register(fd.as_fd(), opcode, arg, nr_args)
}
/// `io_uring_register_with(fd, opcode, flags, arg, nr_args)`—Register files or /// user buffers for asynchronous I/O. /// /// # Safety /// /// io_uring operates on raw pointers and raw file descriptors. Users are /// responsible for ensuring that memory and resources are only accessed in /// valid ways. /// /// If `opcode` is `IoringRegisterOp::RegisterRingFds`, `arg` must point to /// mutable memory, despite being `*const`. /// /// # References /// - [Linux] /// /// [Linux]: https://www.man7.org/linux/man-pages/man2/io_uring_register.2.html #[inline] pubunsafefn io_uring_register_with<Fd: AsFd>(
fd: Fd,
opcode: IoringRegisterOp,
flags: IoringRegisterFlags,
arg: *const c_void,
nr_args: u32,
) -> io::Result<u32> {
backend::io_uring::syscalls::io_uring_register_with(fd.as_fd(), opcode, flags, arg, nr_args)
}
/// `io_uring_enter(fd, to_submit, min_complete, flags, 0, 0)`—Initiate /// and/or complete asynchronous I/O. /// /// This version has no `arg` argument. To pass: /// - a signal mask, use [`io_uring_enter_sigmask`]. /// - an [`io_uring_getevents_arg`], use [`io_uring_enter_arg`] (aka /// `io_uring_enter2`). /// /// # Safety /// /// io_uring operates on raw pointers and raw file descriptors. Users are /// responsible for ensuring that memory and resources are only accessed in /// valid ways. /// /// And, `flags` must not have [`IoringEnterFlags::EXT_ARG`] or /// [`IoringEnterFlags::EXT_ARG_REG`] set. /// /// # References /// - [Linux] /// /// [Linux]: https://www.man7.org/linux/man-pages/man2/io_uring_enter.2.html #[doc(alias = "io_uring_enter2")] #[inline] pubunsafefn io_uring_enter<Fd: AsFd>(
fd: Fd,
to_submit: u32,
min_complete: u32,
flags: IoringEnterFlags,
) -> io::Result<u32> {
debug_assert!(!flags.contains(IoringEnterFlags::EXT_ARG));
debug_assert!(!flags.contains(IoringEnterFlags::EXT_ARG_REG));
/// `io_uring_enter(fd, to_submit, min_complete, flags, sigmask, /// sizeof(*sigmask))`— Initiate and/or complete asynchronous I/O, with a /// signal mask. /// /// # Safety /// /// io_uring operates on raw pointers and raw file descriptors. Users are /// responsible for ensuring that memory and resources are only accessed in /// valid ways. /// /// And, `flags` must not have [`IoringEnterFlags::EXT_ARG`] or /// [`IoringEnterFlags::EXT_ARG_REG`] set. /// /// And, the `KernelSigSet` referred to by `arg` must not contain any signal /// numbers reserved by libc. /// /// # References /// - [Linux] /// /// [Linux]: https://www.man7.org/linux/man-pages/man2/io_uring_enter.2.html #[doc(alias = "io_uring_enter")] #[inline] pubunsafefn io_uring_enter_sigmask<Fd: AsFd>(
fd: Fd,
to_submit: u32,
min_complete: u32,
flags: IoringEnterFlags,
sigmask: Option<&KernelSigSet>,
) -> io::Result<u32> {
debug_assert!(!flags.contains(IoringEnterFlags::EXT_ARG));
debug_assert!(!flags.contains(IoringEnterFlags::EXT_ARG_REG));
/// `io_uring_enter2(fd, to_submit, min_complete, flags, arg, sizeof(*arg))`— /// Initiate and/or complete asynchronous I/O, with a signal mask and a /// timeout. /// /// # Safety /// /// io_uring operates on raw pointers and raw file descriptors. Users are /// responsible for ensuring that memory and resources are only accessed in /// valid ways. /// /// And, `flags` must have [`IoringEnterFlags::EXT_ARG`] set, and must not have /// [`IoringEnterFlags::EXT_ARG_REG`] set. /// /// And, the `KernelSigSet` pointed to by the `io_uring_getenvets_arg` referred /// to by `arg` must not contain any signal numbers reserved by libc. /// /// # References /// - [Linux] /// /// [Linux]: https://www.man7.org/linux/man-pages/man2/io_uring_enter.2.html #[doc(alias = "io_uring_enter")] #[doc(alias = "io_uring_enter2")] #[inline] pubunsafefn io_uring_enter_arg<Fd: AsFd>(
fd: Fd,
to_submit: u32,
min_complete: u32,
flags: IoringEnterFlags,
arg: Option<&io_uring_getevents_arg>,
) -> io::Result<u32> {
debug_assert!(flags.contains(IoringEnterFlags::EXT_ARG));
debug_assert!(!flags.contains(IoringEnterFlags::EXT_ARG_REG));
// TODO: Uncomment this when we support `IoringRegisterOp::CQWAIT_REG`. /* /// `io_uring_enter2(fd, to_submit, min_complete, flags, offset, /// sizeof(io_uring_reg_wait))`— Initiate and/or complete asynchronous I/O, /// using a previously registered `io_uring_reg_wait`. /// /// `offset` is an offset into an area of wait regions previously registered /// with [`io_uring_register`] using the [`IoringRegisterOp::CQWAIT_REG`] /// operation. /// /// # Safety /// /// io_uring operates on raw pointers and raw file descriptors. Users are /// responsible for ensuring that memory and resources are only accessed in /// valid ways. /// /// And, `flags` must have [`IoringEnterFlags::EXT_ARG_REG`] set, and must not /// have [`IoringEnterFlags::EXT_ARG`] set. /// /// # References /// - [Linux] /// /// [Linux]: https://www.man7.org/linux/man-pages/man2/io_uring_enter.2.html #[doc(alias="io_uring_enter")] #[doc(alias="io_uring_enter2")] #[inline] pubunsafefnio_uring_enter_reg_wait<Fd:AsFd>( fd:Fd, to_submit:u32, min_complete:u32, flags:IoringEnterFlags, reg_wait:usize, )->io::Result<u32>{ debug_assert!(!flags.contains(IoringEnterFlags::EXT_ARG)); debug_assert!(flags.contains(IoringEnterFlags::EXT_ARG_REG));
/// `IORING_MSG_*` constants which represent commands for use with /// [`IoringOp::MsgRing`], (`seq.addr`) #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] #[repr(u64)] #[non_exhaustive] pubenum IoringMsgringCmds { /// `IORING_MSG_DATA`
Data = sys::io_uring_msg_ring_flags::IORING_MSG_DATA as _,
/// `IORING_MSG_SEND_FD`
SendFd = sys::io_uring_msg_ring_flags::IORING_MSG_SEND_FD as _,
}
bitflags::bitflags! { /// `IORING_SETUP_*` flags for use with [`io_uring_params`]. #[repr(transparent)] #[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)] pubstruct IoringSetupFlags: u32 { /// `IORING_SETUP_ATTACH_WQ` const ATTACH_WQ = sys::IORING_SETUP_ATTACH_WQ;
#[allow(missing_docs)] pubconst IORING_CQE_BUFFER_SHIFT: u32 = sys::IORING_CQE_BUFFER_SHIFT as _; #[allow(missing_docs)] pubconst IORING_FILE_INDEX_ALLOC: i32 = sys::IORING_FILE_INDEX_ALLOC as _;
// Re-export these as `u64`, which is the `offset` type in `rustix::io::mmap`. #[allow(missing_docs)] pubconst IORING_OFF_SQ_RING: u64 = sys::IORING_OFF_SQ_RING as _; #[allow(missing_docs)] pubconst IORING_OFF_CQ_RING: u64 = sys::IORING_OFF_CQ_RING as _; #[allow(missing_docs)] pubconst IORING_OFF_SQES: u64 = sys::IORING_OFF_SQES as _;
/// `IORING_REGISTER_FILES_SKIP` // SAFETY: `IORING_REGISTER_FILES_SKIP` is a reserved value that is never // dynamically allocated, so it'll remain valid for the duration of // `'static`. pubconst IORING_REGISTER_FILES_SKIP: BorrowedFd<'static> = unsafe { BorrowedFd::<'static>::borrow_raw(sys::IORING_REGISTER_FILES_SKIP as RawFd) };
/// `IORING_NOTIF_USAGE_ZC_COPIED` (since Linux 6.2) pubconst IORING_NOTIF_USAGE_ZC_COPIED: i32 = sys::IORING_NOTIF_USAGE_ZC_COPIED as _;
/// A pointer in the io_uring API. /// /// `io_uring`'s native API represents pointers as `u64` values. In order to /// preserve strict-provenance, use a `*mut c_void`. On platforms where /// pointers are narrower than 64 bits, this requires additional padding. #[repr(C)] #[cfg_attr(any(target_arch = "arm", target_arch = "powerpc"), repr(align(8)))] #[derive(Copy, Clone)] #[non_exhaustive] pubstruct io_uring_ptr { #[cfg(all(target_pointer_width = "32", target_endian = "big"))] #[doc(hidden)] pub __pad32: u32, #[cfg(all(target_pointer_width = "16", target_endian = "big"))] #[doc(hidden)] pub __pad16: u16,
/// User data in the io_uring API. /// /// `io_uring`'s native API represents `user_data` fields as `u64` values. In /// order to preserve strict-provenance, use a union which allows users to /// optionally store pointers. #[repr(C)] #[derive(Copy, Clone)] pub union io_uring_user_data { /// An arbitrary `u64`. pub u64_: u64,
/// A pointer. pub ptr: io_uring_ptr,
}
impl io_uring_user_data { /// Create a zero-initialized `Self`. pubconstfn zeroed() -> Self { // Initialize the `u64_` field, which is the size of the full union. // This can use `core::mem::zeroed` in Rust 1.75. Self { u64_: 0 }
}
/// Return the `u64` value. #[inline] pubconstfn u64_(self) -> u64 { // SAFETY: All the fields have the same underlying representation. unsafe { self.u64_ }
}
/// Create a `Self` from a `u64` value. #[inline] pubconstfn from_u64(u64_: u64) -> Self { Self { u64_ }
}
/// Return the `ptr` pointer value. #[inline] pubconstfn ptr(self) -> *mut c_void { // SAFETY: All the fields have the same underlying representation. unsafe { self.ptr }.ptr
}
/// Create a `Self` from a pointer value. #[inline] pubconstfn from_ptr(ptr: *mut c_void) -> Self { Self {
ptr: io_uring_ptr::new(ptr),
}
}
}
impl PartialEq for io_uring_user_data { #[inline] fn eq(&self, other: &Self) -> bool { // SAFETY: `io_uring_ptr` and `u64` have the same layout. unsafe { self.u64_.eq(&other.u64_) }
}
}
impl Eq for io_uring_user_data {}
#[allow(clippy::non_canonical_partial_ord_impl)] impl PartialOrd for io_uring_user_data { #[inline] fn partial_cmp(&self, other: &Self) -> Option<Ordering> { // SAFETY: `io_uring_ptr` and `u64` have the same layout. unsafe { self.u64_.partial_cmp(&other.u64_) }
}
}
impl Ord for io_uring_user_data { #[inline] fn cmp(&self, other: &Self) -> Ordering { // SAFETY: `io_uring_ptr` and `u64` have the same layout. unsafe { self.u64_.cmp(&other.u64_) }
}
}
impl Hash for io_uring_user_data { #[inline] fn hash<H: Hasher>(&self, state: &mut H) { // SAFETY: `io_uring_ptr` and `u64` have the same layout. unsafe { self.u64_.hash(state) }
}
}
impl core::fmt::Debug for io_uring_user_data { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { // SAFETY: Just format as a `u64`, since formatting doesn't preserve // provenance, and we don't have a discriminant. unsafe { self.u64_.fmt(f) }
}
}
/// An io_uring Completion Queue Entry. /// /// This does not derive `Copy` or `Clone` because the `big_cqe` field is not /// automatically copyable. #[allow(missing_docs)] #[repr(C)] #[derive(Debug, Default)] pubstruct io_uring_cqe { pub user_data: io_uring_user_data, pub res: i32, pub flags: IoringCqeFlags, pub big_cqe: IncompleteArrayField<u64>,
}
#[cfg(test)] mod tests { usesuper::*; usecrate::fd::AsRawFd as _;
/// Check that our custom structs and unions have the same layout as the /// kernel's versions. #[test] fn io_uring_layouts() { use sys as c;
// `io_uring_ptr` is a replacement for `u64`.
assert_eq_size!(io_uring_ptr, u64);
assert_eq_align!(io_uring_ptr, u64);
// Test that pointers are stored in `io_uring_ptr` in the way that // io_uring stores them in a `u64`. unsafe { const MAGIC: u64 = !0x0123_4567_89ab_cdef; let ptr = io_uring_ptr::new(MAGIC as usize as *mut c_void);
assert_eq!(ptr.ptr, MAGIC as usize as *mut c_void); #[cfg(target_pointer_width = "16")]
assert_eq!(ptr.__pad16, 0); #[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))]
assert_eq!(ptr.__pad32, 0); let int = core::mem::transmute::<io_uring_ptr, u64>(ptr);
assert_eq!(int, MAGIC as usize as u64);
}
// `io_uring_user_data` is a replacement for `u64`.
assert_eq_size!(io_uring_user_data, u64);
assert_eq_align!(io_uring_user_data, u64);
// Test that `u64`s and pointers are properly stored in // `io_uring_user_data`. unsafe { const MAGIC: u64 = !0x0123_4567_89ab_cdef; let user_data = io_uring_user_data::from_u64(MAGIC);
assert_eq!(user_data.u64_(), MAGIC);
assert_eq!(
core::mem::transmute::<io_uring_user_data, u64>(user_data),
MAGIC
); let user_data = io_uring_user_data::from_ptr(MAGIC as usize as *mut c_void);
assert_eq!(user_data.ptr(), MAGIC as usize as *mut c_void);
assert_eq!(
core::mem::transmute::<io_uring_user_data, u64>(user_data),
MAGIC as usize as u64
);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.