#![cfg(feature = "sys")]
#![allow(elided_lifetimes_in_paths)]
#![allow(mismatched_lifetime_syntaxes)]
use std::marker::PhantomData;
use std::ops::Deref;
use std::ptr;
use std::slice;
use crate::error::Error;
use crate::ffi;
use crate::frame::Frame;
pub struct Ring<'a> {
ring: *mut ffi::netmap_ring,
fd: i32,
index: usize,
direction: RingDirection,
_marker: PhantomData<&'a mut ffi::netmap_ring>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RingDirection {
Tx,
Rx,
}
unsafe impl<'a> Send for Ring<'a> {}
pub struct TxRing<'a>(Ring<'a>);
pub struct RxRing<'a>(Ring<'a>);
impl<'a> Deref for TxRing<'a> {
type Target = Ring<'a>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<'a> Deref for RxRing<'a> {
type Target = Ring<'a>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<'a> Ring<'a> {
pub(crate) fn new(ring: *mut ffi::netmap_ring, fd: i32, index: usize) -> Self {
Self {
ring,
fd,
index,
direction: RingDirection::Tx,
_marker: PhantomData,
}
}
pub fn index(&self) -> usize {
self.index
}
pub fn head(&self) -> u32 {
unsafe { (*self.ring).head }
}
pub fn tail(&self) -> u32 {
unsafe { (*self.ring).tail }
}
pub fn dir(&self) -> u16 {
unsafe { (*self.ring).dir }
}
pub fn direction(&self) -> RingDirection {
self.direction
}
pub fn num_slots(&self) -> usize {
unsafe { (*self.ring).num_slots as usize }
}
pub fn has_free_slots(&self) -> bool {
unsafe {
let ring = self.ring;
let head = (*ring).head;
let tail = (*ring).tail;
let num_slots = (*ring).num_slots;
(head + 1) % num_slots != tail
}
}
pub fn sync(&self) {
unsafe {
let cmd = match self.direction {
RingDirection::Tx => ffi::NIOCTXSYNC,
RingDirection::Rx => ffi::NIOCRXSYNC,
};
libc::ioctl(self.fd, cmd, 0);
}
}
}
impl<'a> TxRing<'a> {
pub(crate) fn new(ring: *mut ffi::netmap_ring, fd: i32, index: usize) -> Self {
let mut r = Ring::new(ring, fd, index);
r.direction = RingDirection::Tx;
Self(r)
}
pub fn send(&mut self, buf: &[u8]) -> Result<(), Error> {
if buf.len() > self.max_payload_size() {
return Err(Error::PacketTooLarge(buf.len()));
}
unsafe {
let ring = self.0.ring;
let cur = (*ring).cur;
let slot = (*ring).slot.as_mut_ptr().add(cur as usize);
let dst = ffi::NETMAP_BUF(ring, (*slot).buf_idx) as *mut u8;
ptr::copy_nonoverlapping(buf.as_ptr(), dst, buf.len());
(*slot).len = buf.len() as u16;
(*ring).head = (*ring).cur.wrapping_add(1);
(*ring).cur = (*ring).head;
Ok(())
}
}
pub fn max_payload_size(&self) -> usize {
unsafe { (*self.0.ring).nr_buf_size as usize }
}
pub fn reserve_batch(&mut self, count: usize) -> Result<BatchReservation<'a>, Error> {
unsafe {
let ring_ptr = self.0.ring;
let head = (*ring_ptr).head;
let tail = (*ring_ptr).tail;
let num_slots = (*ring_ptr).num_slots;
let current_used_slots = (head.wrapping_sub(tail).wrapping_add(num_slots)) % num_slots;
let available_slots = (num_slots - 1).saturating_sub(current_used_slots) as usize;
if available_slots < count {
return Err(Error::InsufficientSpace);
}
}
Ok(BatchReservation {
ring: self.0.ring,
start: unsafe { (*self.0.ring).head },
count,
_marker: PhantomData,
})
}
}
pub struct BatchReservation<'a> {
ring: *mut ffi::netmap_ring,
start: u32,
count: usize,
_marker: PhantomData<&'a mut ffi::netmap_ring>,
}
impl<'a> BatchReservation<'a> {
pub fn packet(&mut self, index: usize, len: usize) -> Result<&mut [u8], Error> {
if index >= self.count {
return Err(Error::InvalidRingIndex(index));
}
unsafe {
let slot_idx = (self.start + index as u32) % (*self.ring).num_slots;
let slot = (*self.ring).slot.as_mut_ptr().add(slot_idx as usize);
(*slot).len = len as u16;
let src = ffi::NETMAP_BUF(self.ring, (*slot).buf_idx) as *mut u8;
Ok(slice::from_raw_parts_mut(src, len))
}
}
pub fn commit(self) {
unsafe {
(*self.ring).head = self.start + self.count as u32;
(*self.ring).cur = (*self.ring).head;
}
}
}
impl<'a> RxRing<'a> {
pub(crate) fn new(ring: *mut ffi::netmap_ring, fd: i32, index: usize) -> Self {
let mut r = Ring::new(ring, fd, index);
r.direction = RingDirection::Rx;
Self(r)
}
pub fn recv(&mut self) -> Option<Frame> {
unsafe {
let ring = self.0.ring;
if (*ring).head == (*ring).tail {
return None;
}
let slot_idx = (*ring).head % (*ring).num_slots;
let slot = (*ring).slot.as_mut_ptr().add(slot_idx as usize);
let src = ffi::NETMAP_BUF(ring, (*slot).buf_idx) as *const u8;
let buf = slice::from_raw_parts(src, (*slot).len as usize);
(*ring).head = (*ring).head.wrapping_add(1);
(*ring).cur = (*ring).head;
Some(Frame::new(buf))
}
}
pub fn recv_batch(&mut self, batch: &mut [Frame]) -> usize {
unsafe {
let ring = self.0.ring;
let avail = (*ring).tail.wrapping_sub((*ring).head) as usize;
let count = avail.min(batch.len());
for (i, frame) in batch.iter_mut().take(count).enumerate() {
let slot_idx = ((*ring).head + i as u32) % (*ring).num_slots;
let slot = (*ring).slot.as_mut_ptr().add(slot_idx as usize);
let src = ffi::NETMAP_BUF(ring, (*slot).buf_idx) as *const u8;
let buf = slice::from_raw_parts(src, (*slot).len as usize);
*frame = Frame::new(buf);
}
(*ring).head = (*ring).head.wrapping_add(count as u32);
(*ring).cur = (*ring).head;
count
}
}
}