use std::io;
use std::ptr;
use std::sync::atomic::{self, AtomicU16};
pub struct ProvidedBufRing {
ring_ptr: *mut u8,
ring_mmap_len: usize,
buf_backing: Vec<u8>,
bgid: u16,
ring_size: u16,
buf_size: u32,
tail: u16,
mask: u16,
}
#[repr(C)]
struct BufRingEntry {
addr: u64,
len: u32,
bid: u16,
resv: u16,
}
impl ProvidedBufRing {
const ENTRY_SIZE: usize = std::mem::size_of::<BufRingEntry>();
pub fn new(bgid: u16, ring_size: u16, buf_size: u32) -> io::Result<Self> {
assert!(ring_size.is_power_of_two(), "ring_size must be power of 2");
let ring_mmap_len = ring_size as usize * Self::ENTRY_SIZE;
let buf_backing = vec![0u8; ring_size as usize * buf_size as usize];
let ring_ptr = unsafe {
libc::mmap(
ptr::null_mut(),
ring_mmap_len,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_ANONYMOUS | libc::MAP_SHARED,
-1,
0,
)
};
if ring_ptr == libc::MAP_FAILED {
return Err(io::Error::last_os_error());
}
let mut ring = ProvidedBufRing {
ring_ptr: ring_ptr as *mut u8,
ring_mmap_len,
buf_backing,
bgid,
ring_size,
buf_size,
tail: 0,
mask: ring_size - 1,
};
for i in 0..ring_size {
ring.push_entry(i);
}
ring.commit_tail();
Ok(ring)
}
pub fn ring_addr(&self) -> u64 {
self.ring_ptr as u64
}
pub fn bgid(&self) -> u16 {
self.bgid
}
pub fn ring_entries(&self) -> u32 {
self.ring_size as u32
}
pub fn get_buffer(&self, bid: u16) -> (*const u8, u32) {
let offset = bid as usize * self.buf_size as usize;
let ptr = unsafe { self.buf_backing.as_ptr().add(offset) };
(ptr, self.buf_size)
}
pub fn replenish_batch(&mut self, bids: &[u16]) {
for &bid in bids {
self.push_entry(bid);
}
if !bids.is_empty() {
self.commit_tail();
}
}
fn push_entry(&mut self, bid: u16) {
let ring_idx = (self.tail & self.mask) as usize;
let entry_ptr = unsafe {
self.ring_ptr
.add(ring_idx * Self::ENTRY_SIZE)
.cast::<BufRingEntry>()
};
let buf_offset = bid as usize * self.buf_size as usize;
let buf_addr = unsafe { self.buf_backing.as_ptr().add(buf_offset) };
unsafe {
ptr::write(
entry_ptr,
BufRingEntry {
addr: buf_addr as u64,
len: self.buf_size,
bid,
resv: 0,
},
);
}
self.tail = self.tail.wrapping_add(1);
}
fn commit_tail(&self) {
let tail_ptr = unsafe { self.ring_ptr.add(14).cast::<AtomicU16>() };
unsafe {
(*tail_ptr).store(self.tail, atomic::Ordering::Release);
}
}
}
impl Drop for ProvidedBufRing {
fn drop(&mut self) {
if !self.ring_ptr.is_null() {
unsafe {
libc::munmap(self.ring_ptr as *mut _, self.ring_mmap_len);
}
}
}
}
unsafe impl Send for ProvidedBufRing {}