use core::ffi::{c_int, c_long, c_void};
use core::ptr;
use core::sync::atomic::{AtomicU16, Ordering};
use std::io;
use crate::ffi::{
self, IO_URING_BUF_SIZE, IO_URING_BUF_TAIL_OFF, IORING_REGISTER_PBUF_RING,
IORING_UNREGISTER_PBUF_RING, MAP_ANONYMOUS, MAP_PRIVATE, PROT_READ, PROT_WRITE,
SYS_IO_URING_REGISTER,
};
use crate::layout::IoUringBufReg;
pub struct ProvidedBufRing {
pub(crate) ring_fd: c_int,
pub(crate) ring: *mut u8,
pub(crate) ring_len: usize,
pub(crate) slab: Vec<u8>,
pub(crate) mask: u16,
pub(crate) buf_size: u32,
pub(crate) bgid: u16,
pub(crate) tail: u16,
}
unsafe impl Send for ProvidedBufRing {}
impl ProvidedBufRing {
pub(crate) fn new(
ring_fd: c_int,
entries: u16,
buf_size: u32,
bgid: u16,
) -> io::Result<Self> {
assert!(entries.is_power_of_two(), "buf ring entries must be power of two");
let (ring, ring_len) = Self::mmap_buf_ring(entries)?;
if let Err(e) = Self::register_with_kernel(ring_fd, ring, entries, bgid) {
unsafe { ffi::munmap(ring as *mut c_void, ring_len) };
return Err(e);
}
let mut pbr = ProvidedBufRing {
ring_fd,
ring,
ring_len,
slab: vec![0u8; entries as usize * buf_size as usize],
mask: entries - 1,
buf_size,
bgid,
tail: 0,
};
for bid in 0..entries {
pbr.stage(bid);
}
pbr.commit();
Ok(pbr)
}
fn mmap_buf_ring(entries: u16) -> io::Result<(*mut u8, usize)> {
let ring_len = entries as usize * IO_URING_BUF_SIZE;
let ring = unsafe {
ffi::mmap(
ptr::null_mut(),
ring_len,
PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE,
-1,
0,
)
};
if ring as isize == -1 {
return Err(io::Error::last_os_error());
}
Ok((ring as *mut u8, ring_len))
}
fn register_with_kernel(ring_fd: c_int, ring: *mut u8, entries: u16, bgid: u16) -> io::Result<()> {
let reg = IoUringBufReg {
ring_addr: ring as u64,
ring_entries: entries as u32,
bgid,
pad: 0,
resv: [0; 3],
};
let ret = unsafe {
ffi::syscall(
SYS_IO_URING_REGISTER,
ring_fd as c_long,
IORING_REGISTER_PBUF_RING as c_long,
® as *const IoUringBufReg as c_long,
1 as c_long,
)
};
if ret < 0 {
return Err(io::Error::last_os_error());
}
Ok(())
}
pub fn group(&self) -> u16 {
self.bgid
}
pub fn bytes(&self, bid: u16, n: usize) -> &[u8] {
let start = bid as usize * self.buf_size as usize;
&self.slab[start..start + n.min(self.buf_size as usize)]
}
pub(crate) fn stage(&mut self, bid: u16) {
let idx = (self.tail & self.mask) as usize;
let base = unsafe { self.ring.add(idx * IO_URING_BUF_SIZE) };
let addr = unsafe { self.slab.as_ptr().add(bid as usize * self.buf_size as usize) } as u64;
unsafe {
ptr::write_unaligned(base as *mut u64, addr);
ptr::write_unaligned(base.add(8) as *mut u32, self.buf_size);
ptr::write_unaligned(base.add(12) as *mut u16, bid);
}
self.tail = self.tail.wrapping_add(1);
}
pub(crate) fn commit(&self) {
let tail = unsafe { &*(self.ring.add(IO_URING_BUF_TAIL_OFF) as *const AtomicU16) };
tail.store(self.tail, Ordering::Release);
}
pub fn recycle(&mut self, bid: u16) {
self.stage(bid);
self.commit();
}
}
impl Drop for ProvidedBufRing {
fn drop(&mut self) {
let reg = IoUringBufReg {
ring_addr: 0,
ring_entries: 0,
bgid: self.bgid,
pad: 0,
resv: [0; 3],
};
unsafe {
ffi::syscall(
SYS_IO_URING_REGISTER,
self.ring_fd as c_long,
IORING_UNREGISTER_PBUF_RING as c_long,
® as *const IoUringBufReg as c_long,
1 as c_long,
);
ffi::munmap(self.ring as *mut c_void, self.ring_len);
}
}
}