use core::cmp;
use core::ffi::c_void;
use core::mem::size_of;
use core::ops::{Deref, DerefMut};
use core::ptr;
use core::sync::atomic::{AtomicU32, Ordering};
use rustix::fd::BorrowedFd;
use rustix::io_uring::{
io_cqring_offsets, io_sqring_offsets, io_uring_params, IoringSqFlags,
IORING_OFF_CQ_RING, IORING_OFF_SQES, IORING_OFF_SQ_RING,
};
use rustix::{io, mm};
use std::os::fd::AsFd;
use crate::entry::*;
const _: () = assert!(usize::BITS >= 32);
const SQE_SIZE: u32 = size_of::<Sqe>() as u32;
const CQE_SIZE: u32 = size_of::<Cqe>() as u32;
const _: () = {
assert!(size_of::<io_uring_params>() == 120);
assert!(SQE_SIZE == 64);
assert!(CQE_SIZE == 16);
assert!(IORING_OFF_SQ_RING == 0);
assert!(IORING_OFF_CQ_RING == 0x8000000);
assert!(IORING_OFF_SQES == 0x10000000);
};
#[derive(Debug)]
struct Mmap {
ptr: *mut c_void,
len: usize,
}
impl Mmap {
fn new(
size: usize,
fd: BorrowedFd,
flags: mm::MapFlags,
offset: u64,
) -> rustix::io::Result<Self> {
let prot_flags = mm::ProtFlags::READ | mm::ProtFlags::WRITE;
let ptr = unsafe {
mm::mmap(ptr::null_mut(), size, prot_flags, flags, fd, offset)?
};
Ok(Self { ptr, len: size })
}
unsafe fn mut_ptr_at<T>(&mut self, byte_offset: u32) -> *mut T {
self.ptr.cast::<u8>().add(byte_offset as usize).cast()
}
unsafe fn ptr_at<T>(&self, byte_offset: u32) -> *const T {
(self.ptr as *const u8).add(byte_offset as usize).cast()
}
unsafe fn u32_at(&self, byte_offset: u32) -> u32 {
*self.ptr_at(byte_offset)
}
unsafe fn raw_slice_at<T>(&self, byte_offset: u32, len: u32) -> *mut [T] {
let ptr = self.ptr.cast::<u8>().add(byte_offset as usize).cast();
ptr::slice_from_raw_parts_mut(ptr, len as usize)
}
}
impl Drop for Mmap {
fn drop(&mut self) {
unsafe {
mm::munmap(self.ptr, self.len).expect("munmap failed");
}
}
}
#[derive(Debug)]
pub struct Sqes(Mmap);
impl Sqes {
pub fn new(
io_ring_fd: BorrowedFd,
p: &io_uring_params,
) -> io::Result<Self> {
let size_sqes = (p.sq_entries * SQE_SIZE) as usize;
let mmap = Mmap::new(
size_sqes,
io_ring_fd.as_fd(),
mm::MapFlags::SHARED | mm::MapFlags::POPULATE,
IORING_OFF_SQES,
)?;
Ok(Self(mmap))
}
}
impl Deref for Sqes {
type Target = [Sqe];
fn deref(&self) -> &Self::Target {
unsafe { &*ptr::slice_from_raw_parts(self.0.ptr.cast(), self.0.len) }
}
}
impl DerefMut for Sqes {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe {
&mut *ptr::slice_from_raw_parts_mut(self.0.ptr.cast(), self.0.len)
}
}
}
#[derive(Debug)]
pub struct RingMasks {
pub sq: u32,
pub cq: u32,
}
#[derive(Debug)]
pub struct Ioring {
mmap: Mmap,
sq_off: io_sqring_offsets,
cq_off: io_cqring_offsets,
sq_entries: u32,
cq_entries: u32,
}
impl Ioring {
pub fn new(
ring_fd: BorrowedFd,
p: &io_uring_params,
) -> rustix::io::Result<(Self, RingMasks)> {
let io_uring_params { sq_entries, cq_entries, sq_off, cq_off, .. } = *p;
let size = cmp::max(
sq_off.array + sq_entries * (size_of::<u32>() as u32),
cq_off.cqes + cq_entries * CQE_SIZE,
) as usize;
let mmap = Mmap::new(
size,
ring_fd,
mm::MapFlags::SHARED | mm::MapFlags::POPULATE,
IORING_OFF_SQ_RING,
)?;
let masks = unsafe {
RingMasks {
sq: mmap.u32_at(sq_off.ring_mask),
cq: mmap.u32_at(cq_off.ring_mask),
}
};
unsafe {
assert_eq!(mmap.u32_at(sq_off.head), 0);
assert_eq!(mmap.u32_at(sq_off.tail), 0);
assert_eq!(masks.sq, sq_entries - 1);
assert_eq!(mmap.u32_at(p.sq_off.dropped), 0);
assert_eq!(mmap.u32_at(cq_off.head), 0);
assert_eq!(mmap.u32_at(cq_off.tail), 0);
assert_eq!(masks.cq, cq_entries - 1);
assert_eq!(mmap.u32_at(cq_off.overflow), 0);
assert_eq!(mmap.u32_at(p.sq_off.ring_entries), p.sq_entries);
}
let ioring = Self { mmap, sq_off, cq_off, sq_entries, cq_entries };
Ok((ioring, masks))
}
}
impl Ioring {
pub fn sq_head(&self) -> u32 {
unsafe { self.atomic_load_u32_at(self.sq_off.head, Ordering::Acquire) }
}
pub fn sq_flag_contains(&self, flag: IoringSqFlags) -> bool {
let bits = unsafe {
self.atomic_load_u32_at(self.sq_off.flags, Ordering::Relaxed)
};
let flags = IoringSqFlags::from_bits_retain(bits);
flags.contains(flag)
}
pub fn sq_tail(&self) -> u32 {
unsafe { self.mmap.u32_at(self.sq_off.tail) }
}
pub fn sq_tail_write(&mut self, new_tail: u32) {
let tail = unsafe { self.atomic_u32_at(self.sq_off.tail) };
tail.store(new_tail, Ordering::Release);
}
pub fn sqe_indices(&mut self) -> &mut [u32] {
unsafe {
&mut *self.mmap.raw_slice_at(self.sq_off.array, self.sq_entries)
}
}
pub fn cqes(&self) -> &[Cqe] {
unsafe { &*self.mmap.raw_slice_at(self.cq_off.cqes, self.cq_entries) }
}
pub fn cq_head(&self) -> u32 {
unsafe { self.mmap.u32_at(self.cq_off.head) }
}
pub fn cq_tail(&self) -> u32 {
unsafe { self.atomic_load_u32_at(self.cq_off.tail, Ordering::Acquire) }
}
pub fn cq_advance(&mut self, count: u32) {
if count > 0 {
let atomic_head = unsafe { self.atomic_u32_at(self.cq_off.head) };
atomic_head.fetch_add(count, Ordering::Release);
}
}
unsafe fn atomic_load_u32_at(
&self,
byte_offset: u32,
ordering: Ordering,
) -> u32 {
let ptr = self.mmap.ptr_at::<u32>(byte_offset).cast_mut();
AtomicU32::from_ptr(ptr).load(ordering)
}
unsafe fn atomic_u32_at(&mut self, byte_offset: u32) -> &AtomicU32 {
AtomicU32::from_ptr(self.mmap.mut_ptr_at(byte_offset))
}
}