use core::fmt;
use core::marker::PhantomData;
use core::sync::atomic::{Ordering, fence};
use bytemuck::Zeroable;
use smallvec::SmallVec;
use thiserror::Error;
use super::desc::{DescFlags, DescTable, Descriptor};
use super::event::{EventFlags, EventSuppression};
use super::{Layout, MemOps};
#[derive(Debug, Copy, Clone, Zeroable)]
pub struct BufferElement {
pub addr: u64,
pub len: u32,
pub writable: bool,
}
#[derive(Debug, Copy, Clone)]
pub struct UsedBuffer {
pub id: u16,
pub len: u32,
}
#[derive(Debug, Copy, Clone)]
pub struct SubmitResult {
pub id: u16,
pub notify: bool,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum MemOp {
ReadDesc,
WriteDesc,
ReadEvent,
WriteEvent,
}
impl fmt::Display for MemOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::ReadDesc => f.write_str("reading descriptor"),
Self::WriteDesc => f.write_str("writing descriptor"),
Self::ReadEvent => f.write_str("reading event suppression"),
Self::WriteEvent => f.write_str("writing event suppression"),
}
}
}
#[derive(Error, Debug)]
pub enum RingError {
#[error("Buffer chain is empty")]
EmptyChain,
#[error("Buffer chain is malformed")]
BadChain,
#[error("Operation would block")]
WouldBlock,
#[error("Out of memory")]
OutOfMemory,
#[error("Invalid state")]
InvalidState,
#[error("Invalid memory layout")]
InvalidLayout,
#[error("Backend memory error while {op} at address 0x{addr:x}, len {len}")]
MemError {
op: MemOp,
addr: u64,
len: usize,
},
}
impl RingError {
#[inline]
fn mem_err(op: MemOp, addr: u64) -> Self {
let len = match op {
MemOp::ReadDesc | MemOp::WriteDesc => Descriptor::SIZE,
MemOp::ReadEvent | MemOp::WriteEvent => EventSuppression::SIZE,
};
Self::MemError { op, addr, len }
}
}
pub struct Readable;
pub struct Writable;
#[derive(Debug, Default)]
pub struct BufferChainBuilder<T> {
elems: SmallVec<[BufferElement; 16]>,
split: usize,
marker: PhantomData<T>,
}
impl BufferChainBuilder<Readable> {
pub fn new() -> Self {
Self {
elems: Default::default(),
split: 0,
marker: PhantomData,
}
}
pub fn readable(mut self, addr: u64, len: u32) -> Self {
self.elems.push(BufferElement {
addr,
len,
writable: false,
});
self.split += 1;
self
}
pub fn readables(
mut self,
elements: impl IntoIterator<Item = impl Into<BufferElement>>,
) -> Self {
for elem in elements {
let mut elem = elem.into();
elem.writable = false;
self.elems.push(elem);
self.split += 1;
}
self
}
pub fn writable(mut self, addr: u64, len: u32) -> BufferChainBuilder<Writable> {
self.elems.push(BufferElement {
addr,
len,
writable: true,
});
BufferChainBuilder {
elems: self.elems,
split: self.split,
marker: PhantomData,
}
}
pub fn writables(
mut self,
elements: impl IntoIterator<Item = impl Into<BufferElement>>,
) -> BufferChainBuilder<Writable> {
for elem in elements {
let mut elem = elem.into();
elem.writable = true;
self.elems.push(elem);
}
BufferChainBuilder {
elems: self.elems,
split: self.split,
marker: PhantomData,
}
}
pub fn build(self) -> Result<BufferChain, RingError> {
if self.elems.is_empty() {
return Err(RingError::EmptyChain);
}
Ok(BufferChain {
elems: self.elems,
split: self.split,
})
}
}
impl BufferChainBuilder<Writable> {
pub fn writable(mut self, addr: u64, len: u32) -> Self {
self.elems.push(BufferElement {
addr,
len,
writable: true,
});
self
}
pub fn writables(
mut self,
elements: impl IntoIterator<Item = impl Into<BufferElement>>,
) -> Self {
for elem in elements {
let mut elem = elem.into();
elem.writable = true;
self.elems.push(elem);
}
self
}
pub fn build(self) -> Result<BufferChain, RingError> {
if self.elems.is_empty() {
return Err(RingError::EmptyChain);
}
Ok(BufferChain {
elems: self.elems,
split: self.split,
})
}
}
#[derive(Debug, Clone)]
pub struct BufferChain {
elems: SmallVec<[BufferElement; 16]>,
split: usize,
}
impl BufferChain {
pub fn elems(&self) -> &[BufferElement] {
self.elems.as_slice()
}
pub fn readables(&self) -> &[BufferElement] {
&self.elems[..self.split]
}
pub(crate) fn readables_mut(&mut self) -> &mut [BufferElement] {
&mut self.elems[..self.split]
}
pub fn writables(&self) -> &[BufferElement] {
&self.elems[self.split..]
}
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
self.elems.len()
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct RingCursor {
head: u16,
size: u16,
wrap: bool,
}
impl RingCursor {
pub(crate) fn new(size: usize) -> Self {
Self {
head: 0,
size: size as u16,
wrap: true,
}
}
#[inline]
fn advance(&mut self) {
debug_assert!(self.head.checked_add(1).is_some());
self.head += 1;
if self.head >= self.size {
self.head = 0;
self.wrap = !self.wrap;
}
}
#[inline]
fn advance_by(&mut self, n: u16) {
debug_assert!(self.head.checked_add(n).is_some());
let new = self.head + n;
let wraps = new / self.size;
self.head = new % self.size;
if !wraps.is_multiple_of(2) {
self.wrap = !self.wrap;
}
}
#[inline]
pub fn head(&self) -> u16 {
self.head
}
#[inline]
pub fn wrap(&self) -> bool {
self.wrap
}
#[inline]
pub fn reset(&mut self) {
self.head = 0;
self.wrap = true;
}
}
#[derive(Debug)]
pub struct RingProducer<M> {
mem: M,
avail_cursor: RingCursor,
used_cursor: RingCursor,
num_free: usize,
desc_table: DescTable,
event_flags_shadow: EventFlags,
drv_evt_addr: u64,
dev_evt_addr: u64,
id_free: SmallVec<[u16; DescTable::DEFAULT_LEN]>,
id_num: SmallVec<[u16; DescTable::DEFAULT_LEN]>,
}
impl<M: MemOps> RingProducer<M> {
pub fn new(layout: Layout, mem: M) -> Self {
let size = layout.desc_table_len() as usize;
let raw = layout.desc_table_addr();
let table = unsafe { DescTable::from_raw_parts(raw, size) };
let cursor = RingCursor::new(size);
const DEFAULT_LEN: usize = DescTable::default_len();
let id_free = (0..size as u16).collect::<SmallVec<[_; DEFAULT_LEN]>>();
let id_num = SmallVec::<[_; DEFAULT_LEN]>::from_elem(0, size);
let event_flags_shadow = EventFlags::ENABLE;
Self {
mem,
avail_cursor: cursor,
used_cursor: cursor,
num_free: size,
desc_table: table,
id_free,
id_num,
event_flags_shadow,
drv_evt_addr: layout.drv_evt_addr(),
dev_evt_addr: layout.dev_evt_addr(),
}
}
pub fn submit_one(&mut self, addr: u64, len: u32, writable: bool) -> Result<u16, RingError> {
if self.num_free < 1 {
return Err(RingError::WouldBlock);
}
let id = self.id_free.pop().ok_or(RingError::OutOfMemory)?;
if self.id_num[id as usize] != 0 {
return Err(RingError::InvalidState);
}
self.id_num[id as usize] = 1;
let head_idx = self.avail_cursor.head();
let head_wrap = self.avail_cursor.wrap();
let mut flags = DescFlags::empty();
flags.set(DescFlags::WRITE, writable);
let mut desc = Descriptor::new(addr, len, id, flags);
desc.mark_avail(head_wrap);
let addr = self
.desc_table
.desc_addr(head_idx)
.ok_or(RingError::InvalidState)?;
desc.write_release(&self.mem, addr)
.map_err(|_| RingError::mem_err(MemOp::WriteDesc, addr))?;
self.avail_cursor.advance();
self.num_free -= 1;
Ok(id)
}
pub fn submit_available_with_notify(
&mut self,
chain: &BufferChain,
) -> Result<SubmitResult, RingError> {
let old = self.avail_cursor;
let id = self.submit_available(chain)?;
let new = self.avail_cursor;
let notify = self.should_notify_device(old, new)?;
Ok(SubmitResult { id, notify })
}
pub fn submit_one_with_notify(
&mut self,
addr: u64,
len: u32,
writable: bool,
) -> Result<SubmitResult, RingError> {
let old = self.avail_cursor;
let id = self.submit_one(addr, len, writable)?;
let new = self.avail_cursor;
let notify = self.should_notify_device(old, new)?;
Ok(SubmitResult { id, notify })
}
pub fn submit_available(&mut self, chain: &BufferChain) -> Result<u16, RingError> {
let total_descs = chain.len();
if total_descs == 0 {
return Err(RingError::EmptyChain);
}
if self.num_free < total_descs {
return Err(RingError::WouldBlock);
}
if total_descs == 1 {
let elem = chain.elems()[0];
return self.submit_one(elem.addr, elem.len, elem.writable);
}
let head_idx = self.avail_cursor.head();
let head_wrap = self.avail_cursor.wrap();
let id = self.id_free.pop().ok_or(RingError::OutOfMemory)?;
if self.id_num[id as usize] != 0 {
return Err(RingError::InvalidState);
}
self.id_num[id as usize] = total_descs as u16;
let mut pos = self.avail_cursor;
pos.advance();
for (i, elem) in chain.elems().iter().enumerate().skip(1) {
let is_next = i + 1 < total_descs;
let mut flags = DescFlags::empty();
flags.set(DescFlags::NEXT, is_next);
flags.set(DescFlags::WRITE, elem.writable);
let mut desc = Descriptor::new(elem.addr, elem.len, id, flags);
desc.mark_avail(pos.wrap());
let addr = self
.desc_table
.desc_addr(pos.head())
.ok_or(RingError::InvalidState)?;
self.mem
.write_val(addr, desc)
.map_err(|_| RingError::mem_err(MemOp::WriteDesc, addr))?;
pos.advance();
}
let head_elem = chain.elems()[0];
let mut head_flags = DescFlags::empty();
head_flags.set(DescFlags::NEXT, total_descs > 1);
head_flags.set(DescFlags::WRITE, head_elem.writable);
let mut head_desc = Descriptor::new(head_elem.addr, head_elem.len, id, head_flags);
head_desc.mark_avail(head_wrap);
let head_addr = self
.desc_table
.desc_addr(head_idx)
.ok_or(RingError::InvalidState)?;
head_desc
.write_release(&self.mem, head_addr)
.map_err(|_| RingError::mem_err(MemOp::WriteDesc, head_addr))?;
self.num_free -= total_descs;
self.avail_cursor = pos;
Ok(id)
}
pub fn poll_used(&mut self) -> Result<UsedBuffer, RingError> {
let idx = self.used_cursor.head();
let wrap = self.used_cursor.wrap();
let addr = self
.desc_table
.desc_addr(idx)
.ok_or(RingError::InvalidState)?;
let flags = Descriptor::read_flags_acquire(&self.mem, addr)
.map_err(|_| RingError::mem_err(MemOp::ReadDesc, addr))?;
if !flags.is_used(wrap) {
return Err(RingError::WouldBlock);
}
let desc = Descriptor::read_body(&self.mem, addr, flags)
.map_err(|_| RingError::mem_err(MemOp::ReadDesc, addr))?;
let id = desc.id;
let count = *self
.id_num
.get(id as usize)
.ok_or(RingError::InvalidState)?;
if count == 0 {
return Err(RingError::InvalidState);
}
self.used_cursor.advance_by(count);
self.num_free += count as usize;
self.id_num[id as usize] = 0;
self.id_free.push(id);
Ok(UsedBuffer { id, len: desc.len })
}
#[inline]
pub fn num_free(&self) -> usize {
self.num_free
}
#[inline]
pub fn num_inflight(&self) -> usize {
self.desc_table.len() - self.num_free
}
#[inline]
pub fn is_full(&self) -> bool {
self.num_free == 0
}
#[inline]
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
self.desc_table.len()
}
#[inline]
pub fn mem(&self) -> &M {
&self.mem
}
#[inline]
pub fn desc_table(&self) -> &DescTable {
&self.desc_table
}
#[inline]
pub fn avail_cursor(&self) -> RingCursor {
self.avail_cursor
}
#[inline]
pub fn used_cursor(&self) -> RingCursor {
self.used_cursor
}
pub fn should_notify_since(&self, old: RingCursor) -> Result<bool, RingError> {
self.should_notify_device(old, self.avail_cursor)
}
pub fn disable_used_notifications(&mut self) -> Result<(), RingError> {
if self.event_flags_shadow == EventFlags::DISABLE {
return Ok(());
}
let mut evt = self
.mem
.read_val::<EventSuppression>(self.drv_evt_addr)
.map_err(|_| RingError::mem_err(MemOp::ReadEvent, self.drv_evt_addr))?;
evt.set_flags(EventFlags::DISABLE);
evt.write_release(&self.mem, self.drv_evt_addr)
.map_err(|_| RingError::mem_err(MemOp::WriteEvent, self.drv_evt_addr))?;
self.event_flags_shadow = EventFlags::DISABLE;
Ok(())
}
pub fn enable_used_notifications(&mut self) -> Result<(), RingError> {
if self.event_flags_shadow == EventFlags::ENABLE {
return Ok(());
}
let mut evt = self
.mem
.read_val::<EventSuppression>(self.drv_evt_addr)
.map_err(|_| RingError::mem_err(MemOp::ReadEvent, self.drv_evt_addr))?;
evt.set_flags(EventFlags::ENABLE);
evt.write_release(&self.mem, self.drv_evt_addr)
.map_err(|_| RingError::mem_err(MemOp::WriteEvent, self.drv_evt_addr))?;
self.event_flags_shadow = EventFlags::ENABLE;
Ok(())
}
pub fn enable_used_notifications_desc(
&mut self,
off: u16,
wrap: bool,
) -> Result<(), RingError> {
let mut evt = self
.mem
.read_val::<EventSuppression>(self.drv_evt_addr)
.map_err(|_| RingError::mem_err(MemOp::ReadEvent, self.drv_evt_addr))?;
evt.set_desc_event(off, wrap);
evt.set_flags(EventFlags::DESC);
evt.write_release(&self.mem, self.drv_evt_addr)
.map_err(|_| RingError::mem_err(MemOp::WriteEvent, self.drv_evt_addr))?;
self.event_flags_shadow = EventFlags::DESC;
Ok(())
}
pub fn enable_used_notifications_for_next(&mut self) -> Result<(), RingError> {
let off = self.used_cursor.head();
let wrap = self.used_cursor.wrap();
self.enable_used_notifications_desc(off, wrap)
}
fn should_notify_device(&self, old: RingCursor, new: RingCursor) -> Result<bool, RingError> {
fence(Ordering::SeqCst);
should_notify_evt(&self.mem, self.dev_evt_addr, self.len() as u16, old, new)
}
pub fn reset(&mut self) {
let size = self.desc_table.len();
self.avail_cursor.reset();
self.used_cursor.reset();
self.num_free = size;
self.id_free.clear();
self.id_free.extend(0..size as u16);
self.id_num.iter_mut().for_each(|n| *n = 0);
self.event_flags_shadow = EventFlags::ENABLE;
}
pub fn reset_prefilled(&mut self, ids: &[u16]) {
let size = self.desc_table.len();
let count = ids.len();
assert!(count <= size);
let wrapped = count >= size;
self.avail_cursor.head = if wrapped { 0 } else { count as u16 };
self.avail_cursor.wrap = !wrapped;
self.used_cursor.head = 0;
self.used_cursor.wrap = true;
self.id_num.iter_mut().for_each(|n| *n = 0);
for &id in ids {
assert!((id as usize) < size);
assert_eq!(self.id_num[id as usize], 0);
self.id_num[id as usize] = 1;
}
self.num_free = size - count;
self.id_free.clear();
self.id_free
.extend((0..size as u16).filter(|id| self.id_num[*id as usize] == 0));
}
}
#[derive(Debug)]
pub struct RingConsumer<M> {
mem: M,
avail_cursor: RingCursor,
used_cursor: RingCursor,
desc_table: DescTable,
id_num: SmallVec<[u16; DescTable::DEFAULT_LEN]>,
num_inflight: usize,
event_flags_shadow: EventFlags,
drv_evt_addr: u64,
dev_evt_addr: u64,
}
impl<M: MemOps> RingConsumer<M> {
pub fn new(layout: Layout, mem: M) -> Self {
let size = layout.desc_table_len() as usize;
let raw = layout.desc_table_addr();
let table = unsafe { DescTable::from_raw_parts(raw, size) };
let cursor = RingCursor::new(size);
let id_chain_len = SmallVec::<[u16; DescTable::DEFAULT_LEN]>::from_elem(0, size);
let event_flags_shadow = EventFlags::ENABLE;
Self {
mem,
avail_cursor: cursor,
used_cursor: cursor,
desc_table: table,
id_num: id_chain_len,
num_inflight: 0,
event_flags_shadow,
drv_evt_addr: layout.drv_evt_addr(),
dev_evt_addr: layout.dev_evt_addr(),
}
}
pub fn poll_available(&mut self) -> Result<(u16, BufferChain), RingError> {
let idx = self.avail_cursor.head();
let wrap = self.avail_cursor.wrap();
let head_addr = self
.desc_table
.desc_addr(idx)
.ok_or(RingError::InvalidState)?;
let flags = Descriptor::read_flags_acquire(&self.mem, head_addr)
.map_err(|_| RingError::mem_err(MemOp::ReadDesc, head_addr))?;
if !flags.is_avail(wrap) {
return Err(RingError::WouldBlock);
}
let head_desc = Descriptor::read_body(&self.mem, head_addr, flags)
.map_err(|_| RingError::mem_err(MemOp::ReadDesc, head_addr))?;
let mut elements = SmallVec::<[BufferElement; 16]>::new();
let mut pos = self.avail_cursor;
let mut chain_len: u16 = 1;
let mut steps = 1;
let mut has_next = head_desc.is_next();
let max_steps = self.desc_table.len();
let head_elem = BufferElement::from(&head_desc);
let mut seen_writable = head_elem.writable;
let mut writables: usize = if seen_writable { 1 } else { 0 };
elements.push(head_elem);
pos.advance();
while has_next && steps < max_steps {
let addr = self
.desc_table
.desc_addr(pos.head())
.ok_or(RingError::InvalidState)?;
let desc: Descriptor = self
.mem
.read_val(addr)
.map_err(|_| RingError::mem_err(MemOp::ReadDesc, addr))?;
let elem = BufferElement::from(&desc);
if elem.writable {
seen_writable = true;
writables += 1;
} else if seen_writable {
return Err(RingError::BadChain);
}
elements.push(elem);
chain_len += 1;
steps += 1;
has_next = desc.is_next();
pos.advance();
}
if steps >= max_steps && has_next {
return Err(RingError::BadChain);
}
if self.num_inflight + chain_len as usize > self.desc_table.len() {
return Err(RingError::InvalidState);
}
let readables = elements.len() - writables;
let id = head_desc.id;
let id_num = self
.id_num
.get_mut(id as usize)
.ok_or(RingError::InvalidState)?;
if *id_num != 0 {
return Err(RingError::InvalidState);
}
*id_num = chain_len;
self.avail_cursor = pos;
self.num_inflight += chain_len as usize;
Ok((
id,
BufferChain {
elems: elements,
split: readables,
},
))
}
pub fn submit_used(&mut self, id: u16, written_len: u32) -> Result<(), RingError> {
let chain_len = *self
.id_num
.get(id as usize)
.ok_or(RingError::InvalidState)?;
if chain_len == 0 || chain_len > self.desc_table.len() as u16 {
return Err(RingError::InvalidState);
}
let idx = self.used_cursor.head();
let wrap = self.used_cursor.wrap();
let mut used_desc = Descriptor::new(0, written_len, id, DescFlags::empty());
used_desc.mark_used(wrap);
let addr = self
.desc_table
.desc_addr(idx)
.ok_or(RingError::InvalidState)?;
used_desc
.write_release(&self.mem, addr)
.map_err(|_| RingError::mem_err(MemOp::WriteDesc, addr))?;
self.used_cursor.advance_by(chain_len);
self.id_num[id as usize] = 0;
self.num_inflight -= chain_len as usize;
Ok(())
}
pub fn peek_available(&self) -> Result<bool, RingError> {
let Some(addr) = self.desc_table.desc_addr(self.avail_cursor.head()) else {
return Err(RingError::InvalidState);
};
let flags = Descriptor::read_flags_acquire(&self.mem, addr)
.map_err(|_| RingError::mem_err(MemOp::ReadDesc, addr))?;
Ok(flags.is_avail(self.avail_cursor.wrap()))
}
pub fn submit_used_with_notify(
&mut self,
id: u16,
written_len: u32,
) -> Result<bool, RingError> {
let old = self.used_cursor;
self.submit_used(id, written_len)?;
let new = self.used_cursor;
self.should_notify_driver(old, new)
}
pub fn num_free(&self) -> usize {
self.desc_table.len() - self.num_inflight
}
pub fn num_inflight(&self) -> usize {
self.num_inflight
}
pub fn is_full(&self) -> bool {
self.num_inflight == self.desc_table.len()
}
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
self.desc_table.len()
}
pub fn mem(&self) -> &M {
&self.mem
}
#[inline]
pub fn avail_cursor(&self) -> RingCursor {
self.avail_cursor
}
#[inline]
pub fn used_cursor(&self) -> RingCursor {
self.used_cursor
}
pub fn disable_avail_notifications(&mut self) -> Result<(), RingError> {
if self.event_flags_shadow == EventFlags::DISABLE {
return Ok(());
}
let mut evt = self
.mem
.read_val::<EventSuppression>(self.dev_evt_addr)
.map_err(|_| RingError::mem_err(MemOp::ReadEvent, self.dev_evt_addr))?;
evt.set_flags(EventFlags::DISABLE);
evt.write_release(&self.mem, self.dev_evt_addr)
.map_err(|_| RingError::mem_err(MemOp::WriteEvent, self.dev_evt_addr))?;
self.event_flags_shadow = EventFlags::DISABLE;
Ok(())
}
pub fn enable_avail_notifications(&mut self) -> Result<(), RingError> {
if self.event_flags_shadow == EventFlags::ENABLE {
return Ok(());
}
let mut evt = self
.mem
.read_val::<EventSuppression>(self.dev_evt_addr)
.map_err(|_| RingError::mem_err(MemOp::ReadEvent, self.dev_evt_addr))?;
evt.set_flags(EventFlags::ENABLE);
evt.write_release(&self.mem, self.dev_evt_addr)
.map_err(|_| RingError::mem_err(MemOp::WriteEvent, self.dev_evt_addr))?;
self.event_flags_shadow = EventFlags::ENABLE;
Ok(())
}
pub fn enable_avail_notifications_desc(
&mut self,
off: u16,
wrap: bool,
) -> Result<(), RingError> {
let mut evt = self
.mem
.read_val::<EventSuppression>(self.dev_evt_addr)
.map_err(|_| RingError::mem_err(MemOp::ReadEvent, self.dev_evt_addr))?;
evt.set_desc_event(off, wrap);
evt.set_flags(EventFlags::DESC);
evt.write_release(&self.mem, self.dev_evt_addr)
.map_err(|_| RingError::mem_err(MemOp::WriteEvent, self.dev_evt_addr))?;
self.event_flags_shadow = EventFlags::DESC;
Ok(())
}
pub fn enable_avail_notifications_for_next(&mut self) -> Result<(), RingError> {
let off = self.avail_cursor.head();
let wrap = self.avail_cursor.wrap();
self.enable_avail_notifications_desc(off, wrap)
}
fn should_notify_driver(&self, old: RingCursor, new: RingCursor) -> Result<bool, RingError> {
fence(Ordering::SeqCst);
should_notify_evt(&self.mem, self.drv_evt_addr, self.len() as u16, old, new)
}
pub fn reset(&mut self) {
self.avail_cursor.reset();
self.used_cursor.reset();
self.id_num.iter_mut().for_each(|n| *n = 0);
self.num_inflight = 0;
self.event_flags_shadow = EventFlags::ENABLE;
}
}
fn should_notify_evt<M: MemOps>(
mem: &M,
evt_addr: u64,
ring_len: u16,
old: RingCursor,
new: RingCursor,
) -> Result<bool, RingError> {
let flags = EventSuppression::read_flags_acquire(mem, evt_addr)
.map_err(|_| RingError::mem_err(MemOp::ReadEvent, evt_addr))?;
let evt = if flags == EventFlags::DESC {
EventSuppression::read_body(mem, evt_addr, flags)
.map_err(|_| RingError::mem_err(MemOp::ReadEvent, evt_addr))?
} else {
EventSuppression::new(0, flags)
};
Ok(should_notify(evt, ring_len, old, new))
}
#[inline]
fn should_notify(evt: EventSuppression, ring_len: u16, old: RingCursor, new: RingCursor) -> bool {
match evt.flags() {
EventFlags::DISABLE => false,
EventFlags::ENABLE => true,
EventFlags::DESC => {
let mut off = evt.desc_event_off();
let wrap = evt.desc_event_wrap();
if wrap != new.wrap() {
off = off.wrapping_sub(ring_len);
}
ring_need_event(off, new.head(), old.head())
}
_ => false,
}
}
#[inline(always)]
fn ring_need_event(event_idx: u16, new: u16, old: u16) -> bool {
new.wrapping_sub(event_idx).wrapping_sub(1) < new.wrapping_sub(old)
}
impl From<&Descriptor> for BufferElement {
fn from(desc: &Descriptor) -> Self {
BufferElement {
addr: desc.addr,
len: desc.len,
writable: desc.is_writable(),
}
}
}
#[cfg(test)]
pub(crate) mod tests {
use alloc::sync::Arc;
use core::cell::UnsafeCell;
use core::num::NonZeroU16;
use core::ptr;
use core::sync::atomic::{AtomicU16, Ordering};
use bytemuck::{Pod, Zeroable};
use super::super::align_up;
use super::*;
use crate::virtq::event::EventSuppression;
#[derive(Clone)]
pub struct TestMem {
inner: Arc<TestMemInner>,
}
struct TestMemInner {
storage: UnsafeCell<Vec<u8>>,
base_addr: u64,
}
unsafe impl Send for TestMemInner {}
unsafe impl Sync for TestMemInner {}
impl TestMem {
pub fn new(size: usize) -> Self {
let storage = vec![0u8; size];
let base_addr = storage.as_ptr() as u64;
Self {
inner: Arc::new(TestMemInner {
storage: UnsafeCell::new(storage),
base_addr,
}),
}
}
fn ptr_for_addr(&self, addr: u64) -> *mut u8 {
let storage = unsafe { &mut *self.inner.storage.get() };
let base_ptr = storage.as_mut_ptr();
let offset = (addr - self.inner.base_addr) as usize;
base_ptr.wrapping_add(offset)
}
pub fn base_addr(&self) -> u64 {
self.inner.base_addr
}
}
unsafe impl MemOps for TestMem {
type Error = core::convert::Infallible;
fn read(&self, addr: u64, dst: &mut [u8]) -> Result<(), Self::Error> {
let src = self.ptr_for_addr(addr);
unsafe {
ptr::copy_nonoverlapping(src, dst.as_mut_ptr(), dst.len());
}
Ok(())
}
fn write(&self, addr: u64, src: &[u8]) -> Result<(), Self::Error> {
let dst = self.ptr_for_addr(addr);
unsafe {
ptr::copy_nonoverlapping(src.as_ptr(), dst, src.len());
}
Ok(())
}
fn read_val<T: Pod>(&self, addr: u64) -> Result<T, Self::Error> {
let ptr = self.ptr_for_addr(addr).cast::<T>();
Ok(unsafe { ptr::read_volatile(ptr) })
}
fn write_val<T: Pod>(&self, addr: u64, val: T) -> Result<(), Self::Error> {
let ptr = self.ptr_for_addr(addr).cast::<T>();
unsafe { ptr::write_volatile(ptr, val) };
Ok(())
}
fn load_acquire(&self, addr: u64) -> Result<u16, Self::Error> {
let ptr = self.ptr_for_addr(addr).cast::<AtomicU16>();
Ok(unsafe { (*ptr).load(Ordering::Acquire) })
}
fn store_release(&self, addr: u64, val: u16) -> Result<(), Self::Error> {
let ptr = self.ptr_for_addr(addr).cast::<AtomicU16>();
unsafe { (*ptr).store(val, Ordering::Release) };
Ok(())
}
unsafe fn as_slice(&self, addr: u64, len: usize) -> Result<&[u8], Self::Error> {
let ptr = self.ptr_for_addr(addr);
Ok(unsafe { core::slice::from_raw_parts(ptr, len) })
}
unsafe fn as_mut_slice(&self, addr: u64, len: usize) -> Result<&mut [u8], Self::Error> {
let ptr = self.ptr_for_addr(addr);
Ok(unsafe { core::slice::from_raw_parts_mut(ptr, len) })
}
}
pub struct OwnedRing {
mem: TestMem,
layout: Layout,
}
impl OwnedRing {
pub fn new(size: usize) -> Self {
let num_descs = NonZeroU16::new(size as u16).unwrap();
let needed = Layout::query_size(size);
let padding = Descriptor::ALIGN;
let pool_headroom = 0x100 + 0x8000;
let mem = TestMem::new(needed + padding + pool_headroom);
let aligned_base = align_up(mem.base_addr() as usize, Descriptor::ALIGN) as u64;
let layout = unsafe { Layout::from_base(aligned_base, num_descs).unwrap() };
Self { mem, layout }
}
pub fn layout(&self) -> Layout {
self.layout
}
pub fn mem(&self) -> TestMem {
self.mem.clone()
}
pub fn desc_addr(&self, idx: u16) -> u64 {
self.layout.desc_table_addr() + (idx as u64 * Descriptor::SIZE as u64)
}
pub fn read_desc(&self, idx: u16) -> Descriptor {
self.mem.read_val(self.desc_addr(idx)).unwrap()
}
pub fn write_desc(&self, idx: u16, desc: Descriptor) {
self.mem.write_val(self.desc_addr(idx), desc).unwrap()
}
pub fn read_driver_event(&self) -> EventSuppression {
self.mem.read_val(self.layout.drv_evt_addr()).unwrap()
}
pub fn read_device_event(&self) -> EventSuppression {
self.mem.read_val(self.layout.dev_evt_addr()).unwrap()
}
pub fn len(&self) -> usize {
self.layout.desc_table_len() as usize
}
}
pub(crate) fn make_ring(size: usize) -> OwnedRing {
OwnedRing::new(size)
}
pub(crate) fn make_producer(ring: &OwnedRing) -> RingProducer<TestMem> {
RingProducer::new(ring.layout(), ring.mem())
}
pub(crate) fn make_consumer(ring: &OwnedRing) -> RingConsumer<TestMem> {
RingConsumer::new(ring.layout(), ring.mem())
}
fn assert_invariants(ring: &OwnedRing, prod: &RingProducer<TestMem>) {
let outstanding: u16 = prod.id_num.iter().copied().sum();
assert_eq!(outstanding as usize + prod.num_free, ring.len());
for id in prod.id_free.iter() {
assert_eq!(prod.id_num[*id as usize], 0);
}
for (id, &n) in prod.id_num.iter().enumerate() {
if n > 0 {
assert!(!prod.id_free.contains(&(id as u16)));
}
}
}
#[test]
fn test_initialization() {
let ring = make_ring(8);
let producer = make_producer(&ring);
for i in 0..8u16 {
let desc = ring.read_desc(i);
assert_eq!(desc, Descriptor::zeroed());
assert_eq!(desc.flags, 0);
assert_eq!(desc.addr, 0);
assert_eq!(desc.len, 0);
assert_eq!(desc.id, 0);
}
assert_eq!(producer.avail_cursor.head(), 0);
assert!(producer.avail_cursor.wrap());
assert_eq!(producer.used_cursor.head(), 0);
assert!(producer.used_cursor.wrap());
assert_eq!(producer.id_free.len(), 8);
assert_eq!(producer.num_free, 8);
for i in 0..8 {
assert_eq!(producer.id_num[i], 0);
}
}
#[test]
fn test_buffer_chain_builder_normalizes_element_direction() {
let readable_as_writable = BufferElement {
addr: 0x1000,
len: 16,
writable: true,
};
let writable_as_readable = BufferElement {
addr: 0x2000,
len: 32,
writable: false,
};
let second_writable_as_readable = BufferElement {
addr: 0x3000,
len: 64,
writable: false,
};
let chain = BufferChainBuilder::new()
.readables([readable_as_writable])
.writables([writable_as_readable])
.writables([second_writable_as_readable])
.build()
.unwrap();
assert!(!chain.readables()[0].writable);
assert!(chain.writables().iter().all(|elem| elem.writable));
}
#[test]
fn test_submit_one_descriptor() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
let addr = 0x1000;
let len = 512;
let writable = false;
let id = producer.submit_one(addr, len, writable).unwrap();
let desc = ring.read_desc(0);
assert_eq!(desc.addr, addr);
assert_eq!(desc.len, len);
assert_eq!(desc.id, id);
let flags = desc.flags();
assert!(flags.contains(DescFlags::AVAIL));
assert!(!flags.contains(DescFlags::USED));
assert!(!flags.contains(DescFlags::WRITE));
assert!(!flags.contains(DescFlags::NEXT));
assert_eq!(producer.num_free, 7);
assert_eq!(producer.avail_cursor.head(), 1);
assert!(producer.avail_cursor.wrap());
assert_eq!(producer.id_num[id as usize], 1);
assert_eq!(producer.id_free.len(), 7);
}
#[test]
fn test_single_descriptor_wrap_toggle() {
let ring = make_ring(4);
let mut producer = make_producer(&ring);
producer.avail_cursor.head = 3;
producer.avail_cursor.wrap = true;
producer.num_free = 1;
producer.id_free.clear();
producer.id_free.push(0);
let _id = producer.submit_one(0x1000, 512, false).unwrap();
assert_eq!(producer.avail_cursor.head(), 0);
assert!(!producer.avail_cursor.wrap());
let desc = ring.read_desc(3);
let flags = desc.flags();
assert!(flags.contains(DescFlags::AVAIL));
assert!(!flags.contains(DescFlags::USED));
}
#[test]
fn test_multi_descriptor_no_wrap() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
let chain = BufferChainBuilder::new()
.readable(0x1000, 256)
.readable(0x2000, 256)
.writable(0x3000, 512)
.build()
.unwrap();
let id = producer.submit_available(&chain).unwrap();
let head_desc = ring.read_desc(0);
assert_eq!(head_desc.addr, 0x1000);
assert_eq!(head_desc.len, 256);
assert_eq!(head_desc.id, id);
let head_flags = head_desc.flags();
assert!(head_flags.contains(DescFlags::NEXT));
assert!(!head_flags.contains(DescFlags::WRITE));
assert!(head_flags.contains(DescFlags::AVAIL));
assert!(!head_flags.contains(DescFlags::USED));
let mid_desc = ring.read_desc(1);
assert_eq!(mid_desc.addr, 0x2000);
assert_eq!(mid_desc.len, 256);
assert_eq!(mid_desc.id, id);
let mid_flags = mid_desc.flags();
assert!(mid_flags.contains(DescFlags::NEXT));
assert!(!mid_flags.contains(DescFlags::WRITE));
let tail_desc = ring.read_desc(2);
assert_eq!(tail_desc.addr, 0x3000);
assert_eq!(tail_desc.len, 512);
assert_eq!(tail_desc.id, id);
let tail_flags = tail_desc.flags();
assert!(!tail_flags.contains(DescFlags::NEXT));
assert!(tail_flags.contains(DescFlags::WRITE));
assert_eq!(head_desc.id, mid_desc.id);
assert_eq!(mid_desc.id, tail_desc.id);
assert_eq!(producer.num_free, 5);
assert_eq!(producer.avail_cursor.head(), 3);
assert_eq!(producer.id_num[id as usize], 3);
}
#[test]
fn test_multi_descriptor_with_wrap() {
let ring = make_ring(4);
let mut producer = make_producer(&ring);
producer.avail_cursor.head = 2;
producer.avail_cursor.wrap = true;
let chain = BufferChainBuilder::new()
.readable(0x1000, 256)
.readable(0x2000, 256)
.readable(0x3000, 256)
.build()
.unwrap();
let _id = producer.submit_available(&chain).unwrap();
let head_desc = ring.read_desc(2);
let head_flags = head_desc.flags();
assert!(head_flags.contains(DescFlags::AVAIL));
assert!(!head_flags.contains(DescFlags::USED));
let mid_desc = ring.read_desc(3);
let mid_flags = mid_desc.flags();
assert!(mid_flags.contains(DescFlags::AVAIL));
assert!(!mid_flags.contains(DescFlags::USED));
let tail_desc = ring.read_desc(0);
let tail_flags = tail_desc.flags();
assert!(!tail_flags.contains(DescFlags::AVAIL));
assert!(tail_flags.contains(DescFlags::USED));
assert_eq!(producer.avail_cursor.head(), 1);
assert!(!producer.avail_cursor.wrap());
}
#[test]
fn test_ring_full() {
let ring = make_ring(4);
let mut producer = make_producer(&ring);
for _ in 0..4 {
producer.submit_one(0x1000, 256, false).unwrap();
}
assert_eq!(producer.num_free, 0);
let result = producer.submit_one(0x5000, 256, false);
assert!(matches!(result, Err(RingError::WouldBlock)));
}
#[test]
fn test_poll_and_reclaim() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
let id = producer.submit_one(0x1000, 512, false).unwrap();
let mut desc = ring.read_desc(0);
desc.mark_used(true);
desc.len = 256;
ring.write_desc(0, desc);
let used = producer.poll_used().unwrap();
assert_eq!(used.id, id);
assert_eq!(used.len, 256);
assert_eq!(producer.num_free, 8);
assert_eq!(producer.used_cursor.head(), 1);
assert_eq!(producer.id_num[id as usize], 0);
assert!(producer.id_free.contains(&id));
}
#[test]
fn test_poll_multi_descriptor_chain() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
let chain = BufferChainBuilder::new()
.readable(0x1000, 256)
.readable(0x2000, 256)
.writable(0x3000, 512)
.build()
.unwrap();
let id = producer.submit_available(&chain).unwrap();
let mut head_desc = ring.read_desc(0);
head_desc.mark_used(true);
head_desc.len = 512;
ring.write_desc(0, head_desc);
let used = producer.poll_used().unwrap();
assert_eq!(used.id, id);
assert_eq!(used.len, 512);
assert_eq!(producer.used_cursor.head(), 3);
assert_eq!(producer.num_free, 8);
}
#[test]
fn test_id_reuse() {
let ring = make_ring(4);
let mut producer = make_producer(&ring);
let id1 = producer.submit_one(0x1000, 256, false).unwrap();
let mut desc = ring.read_desc(0);
desc.mark_used(true);
ring.write_desc(0, desc);
producer.poll_used().unwrap();
let id2 = producer.submit_one(0x2000, 256, false).unwrap();
assert_eq!(id2, id1);
assert_eq!(producer.id_num[id2 as usize], 1);
}
#[test]
fn test_available_descriptor_flags() {
let ring = make_ring(4);
let mut producer = make_producer(&ring);
producer.submit_one(0x1000, 256, false).unwrap();
let desc = ring.read_desc(0);
let flags = desc.flags();
assert_ne!(
flags.contains(DescFlags::AVAIL),
flags.contains(DescFlags::USED)
);
assert!(flags.contains(DescFlags::AVAIL));
assert!(!flags.contains(DescFlags::USED));
}
#[test]
fn test_used_descriptor_flags() {
let ring = make_ring(4);
let mut producer = make_producer(&ring);
producer.submit_one(0x1000, 256, false).unwrap();
let mut desc = ring.read_desc(0);
desc.mark_used(true);
ring.write_desc(0, desc);
let desc = ring.read_desc(0);
let flags = desc.flags();
assert_eq!(
flags.contains(DescFlags::AVAIL),
flags.contains(DescFlags::USED)
);
}
#[test]
fn test_poll_empty_ring() {
let ring = make_ring(4);
let mut producer = make_producer(&ring);
assert!(matches!(producer.poll_used(), Err(RingError::WouldBlock)));
}
#[test]
fn test_submit_when_full() {
let ring = make_ring(2);
let mut producer = make_producer(&ring);
producer.submit_one(0x1000, 256, false).unwrap();
producer.submit_one(0x2000, 256, false).unwrap();
assert!(matches!(
producer.submit_one(0x3000, 256, false),
Err(RingError::WouldBlock)
));
}
#[test]
fn test_wrap_stress() {
let ring = make_ring(4);
let mut producer = make_producer(&ring);
let mut consumer = make_consumer(&ring);
for lap in 0..3 {
let expected_wrap = lap % 2 == 0;
for _ in 0..4 {
let id = producer.submit_one(0x1000, 256, false).unwrap();
let (dev_id, _) = consumer.poll_available().unwrap();
assert_eq!(dev_id, id);
consumer.submit_used(dev_id, 256).unwrap();
producer.poll_used().unwrap();
}
assert_eq!(producer.avail_cursor.wrap(), !expected_wrap);
}
assert_invariants(&ring, &producer);
}
#[test]
fn test_next_flag_termination() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
let chain = BufferChainBuilder::new()
.readable(0x1000, 256)
.readable(0x2000, 256)
.readable(0x3000, 256)
.build()
.unwrap();
producer.submit_available(&chain).unwrap();
for i in 0..2 {
let desc = ring.read_desc(i);
assert!(desc.flags().contains(DescFlags::NEXT));
}
let tail_desc = ring.read_desc(2);
assert!(!tail_desc.flags().contains(DescFlags::NEXT));
}
#[test]
fn test_consumer_initialization() {
let ring = make_ring(8);
let consumer = make_consumer(&ring);
assert_eq!(consumer.avail_cursor.head(), 0);
assert!(consumer.avail_cursor.wrap());
assert_eq!(consumer.used_cursor.head(), 0);
assert!(consumer.used_cursor.wrap());
for i in 0..8 {
assert_eq!(consumer.id_num[i], 0);
}
}
#[test]
fn test_consumer_poll_available_single() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
let mut consumer = make_consumer(&ring);
let id = producer.submit_one(0x1000, 512, false).unwrap();
let (polled_id, chain) = consumer.poll_available().unwrap();
assert_eq!(polled_id, id);
assert_eq!(chain.len(), 1);
assert_eq!(chain.elems()[0].addr, 0x1000);
assert_eq!(chain.elems()[0].len, 512);
assert!(!chain.elems()[0].writable);
assert_eq!(consumer.id_num[id as usize], 1);
assert_eq!(consumer.avail_cursor.head(), 1);
}
#[test]
fn test_consumer_poll_available_chain() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
let mut consumer = make_consumer(&ring);
let chain = BufferChainBuilder::new()
.readable(0x1000, 256)
.readable(0x2000, 256)
.writable(0x3000, 512)
.build()
.unwrap();
let id = producer.submit_available(&chain).unwrap();
let (polled_id, polled_chain) = consumer.poll_available().unwrap();
assert_eq!(polled_id, id);
assert_eq!(polled_chain.len(), 3);
assert_eq!(polled_chain.elems()[0].addr, 0x1000);
assert!(!polled_chain.elems()[0].writable);
assert_eq!(polled_chain.elems()[1].addr, 0x2000);
assert!(!polled_chain.elems()[1].writable);
assert_eq!(polled_chain.elems()[2].addr, 0x3000);
assert!(polled_chain.elems()[2].writable);
assert_eq!(consumer.id_num[id as usize], 3);
}
#[test]
fn test_consumer_rejects_duplicate_inflight_id() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
let mut consumer = make_consumer(&ring);
let id = producer.submit_one(0x1000, 512, false).unwrap();
let (polled_id, _) = consumer.poll_available().unwrap();
assert_eq!(polled_id, id);
let mut desc = Descriptor::new(0x2000, 256, id, DescFlags::empty());
desc.mark_avail(consumer.avail_cursor.wrap());
ring.write_desc(consumer.avail_cursor.head(), desc);
assert!(matches!(
consumer.poll_available(),
Err(RingError::InvalidState)
));
}
#[test]
fn test_consumer_submit_used() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
let mut consumer = make_consumer(&ring);
let id = producer.submit_one(0x1000, 512, true).unwrap();
let (polled_id, _) = consumer.poll_available().unwrap();
consumer.submit_used(polled_id, 256).unwrap();
let desc = ring.read_desc(0);
assert_eq!(desc.id, id);
assert_eq!(desc.len, 256);
assert!(desc.is_used(true));
assert_eq!(consumer.used_cursor.head(), 1);
assert_eq!(consumer.id_num[id as usize], 0);
}
#[test]
fn test_consumer_submit_used_multi_descriptor() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
let mut consumer = make_consumer(&ring);
let chain = BufferChainBuilder::new()
.readable(0x1000, 256)
.writable(0x2000, 512)
.writable(0x3000, 512)
.build()
.unwrap();
producer.submit_available(&chain).unwrap();
let (id, _) = consumer.poll_available().unwrap();
consumer.submit_used(id, 1024).unwrap();
let head_desc = ring.read_desc(0);
assert!(head_desc.is_used(true));
assert_eq!(head_desc.len, 1024);
assert_eq!(consumer.used_cursor.head(), 3);
assert_eq!(consumer.id_num[id as usize], 0);
}
#[test]
fn test_consumer_poll_empty() {
let ring = make_ring(4);
let mut consumer = make_consumer(&ring);
assert!(matches!(
consumer.poll_available(),
Err(RingError::WouldBlock)
));
}
#[test]
fn test_consumer_peek() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
let consumer = make_consumer(&ring);
producer.submit_one(0x1000, 512, false).unwrap();
assert!(consumer.peek_available().unwrap());
let empty_ring = make_ring(4);
let empty_consumer = make_consumer(&empty_ring);
assert!(!empty_consumer.peek_available().unwrap());
}
#[test]
fn test_full_roundtrip() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
let mut consumer = make_consumer(&ring);
let chain = BufferChainBuilder::new()
.readable(0x1000, 256)
.writable(0x2000, 512)
.build()
.unwrap();
let id = producer.submit_available(&chain).unwrap();
let (consumer_id, consumer_chain) = consumer.poll_available().unwrap();
assert_eq!(consumer_id, id);
assert_eq!(consumer_chain.len(), 2);
consumer.submit_used(consumer_id, 512).unwrap();
let used = producer.poll_used().unwrap();
assert_eq!(used.id, id);
assert_eq!(used.len, 512);
}
#[test]
fn ring_initial_poll_used_blocks() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
for _ in 0..8 {
assert!(matches!(producer.poll_used(), Err(RingError::WouldBlock)));
}
assert_eq!(producer.num_free, ring.len());
}
#[test]
fn ring_consumer_blocks_until_submit() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
let mut consumer = make_consumer(&ring);
assert!(matches!(
consumer.poll_available(),
Err(RingError::WouldBlock)
));
let chain = BufferChainBuilder::new()
.readable(0x1000, 32)
.readable(0x2000, 16)
.build()
.unwrap();
let id = producer.submit_available(&chain).unwrap();
let (cid, polled) = consumer.poll_available().unwrap();
assert_eq!(cid, id);
assert_eq!(polled.len(), chain.len());
}
#[test]
fn test_out_of_order_completion_stream() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
let mut consumer = make_consumer(&ring);
let id_a = producer.submit_one(0x1000, 256, true).unwrap();
let id_b = producer.submit_one(0x2000, 256, true).unwrap();
let (dev_id_a, chain_a) = consumer.poll_available().unwrap();
assert_eq!(dev_id_a, id_a);
assert_eq!(chain_a.len(), 1);
let (dev_id_b, chain_b) = consumer.poll_available().unwrap();
assert_eq!(dev_id_b, id_b);
assert_eq!(chain_b.len(), 1);
consumer.submit_used(dev_id_b, 128).unwrap();
consumer.submit_used(dev_id_a, 256).unwrap();
let used_b = producer.poll_used().unwrap();
assert_eq!(used_b.id, id_b);
assert_eq!(used_b.len, 128);
let used_a = producer.poll_used().unwrap();
assert_eq!(used_a.id, id_a);
assert_eq!(used_a.len, 256);
assert!(producer.id_free.contains(&id_a));
assert!(producer.id_free.contains(&id_b));
}
#[test]
fn test_mixed_chain_sizes_out_of_order_completion() {
let ring = make_ring(16);
let mut producer = make_producer(&ring);
let mut consumer = make_consumer(&ring);
let chains = vec![
BufferChainBuilder::new()
.readable(0x1000, 10)
.writable(0x2000, 5)
.build()
.unwrap(),
BufferChainBuilder::new()
.readable(0x3000, 8)
.readable(0x3010, 8)
.writable(0x3020, 16)
.build()
.unwrap(),
BufferChainBuilder::new()
.readable(0x4000, 4)
.build()
.unwrap(),
BufferChainBuilder::new()
.readable(0x5000, 4)
.readable(0x5010, 4)
.readable(0x5020, 4)
.writable(0x5030, 4)
.build()
.unwrap(),
];
for c in &chains {
producer.submit_available(c).unwrap();
}
let mut dev_chain_lens = Vec::new();
for _ in &chains {
let (id, chain) = consumer.poll_available().unwrap();
dev_chain_lens.push((id, chain.len() as u32));
}
let order = [1, 3, 0, 2];
let mut completion = Vec::new();
for &idx in &order {
let (id, len) = dev_chain_lens[idx];
consumer.submit_used(id, len).unwrap();
completion.push((id, len));
}
for (expected_id, expected_len) in &completion {
let used = producer.poll_used().unwrap();
assert_eq!(used.id, *expected_id);
assert_eq!(used.len, *expected_len);
assert_eq!(producer.id_num[*expected_id as usize], 0);
assert!(producer.id_free.contains(expected_id));
}
assert_invariants(&ring, &producer);
}
#[test]
fn test_used_stream_wrap_crossing() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
let mut consumer = make_consumer(&ring);
let mut ids = Vec::new();
for i in 0..8 {
ids.push(producer.submit_one(0x1000 + i as u64, 1, false).unwrap());
}
for _ in 0..8 {
consumer.poll_available().unwrap();
}
for &id in ids.iter().rev() {
consumer.submit_used(id, 1).unwrap();
}
for _ in 0..8 {
producer.poll_used().unwrap();
}
assert_eq!(producer.used_cursor.head(), 0);
assert!(!producer.used_cursor.wrap()); assert_invariants(&ring, &producer);
}
#[test]
fn test_interleaved_submit_completion() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
let mut consumer = make_consumer(&ring);
let chain_a = BufferChainBuilder::new()
.readable(0x1000, 8)
.writable(0x2000, 8)
.build()
.unwrap();
let id_a = producer.submit_available(&chain_a).unwrap();
let (dev_id_a, _) = consumer.poll_available().unwrap();
assert_eq!(dev_id_a, id_a);
consumer.submit_used(dev_id_a, 8).unwrap();
let chain_b = BufferChainBuilder::new()
.readable(0x3000, 4)
.readable(0x3010, 4)
.writable(0x3020, 4)
.build()
.unwrap();
let id_b = producer.submit_available(&chain_b).unwrap();
let (dev_id_b, _) = consumer.poll_available().unwrap();
assert_eq!(dev_id_b, id_b);
let used_a = producer.poll_used().unwrap();
assert_eq!(used_a.id, id_a);
consumer.submit_used(dev_id_b, 12).unwrap();
let used_b = producer.poll_used().unwrap();
assert_eq!(used_b.id, id_b);
assert_invariants(&ring, &producer);
}
#[test]
fn test_partial_publish_safety() {
let ring = make_ring(8);
let mut consumer = make_consumer(&ring);
let mut producer = make_producer(&ring);
let chain = BufferChainBuilder::new()
.readable(0x1000, 4)
.readable(0x2000, 4)
.writable(0x3000, 4)
.build()
.unwrap();
let id = producer.id_free.pop().unwrap();
producer.id_num[id as usize] = chain.len() as u16;
let head_idx = producer.avail_cursor.head();
let wrap_start = producer.avail_cursor.wrap();
let mut pos = producer.avail_cursor;
pos.advance();
for (i, elem) in chain.elems().iter().enumerate().skip(1) {
let is_next = i + 1 < chain.len();
let mut flags = DescFlags::empty();
flags.set(DescFlags::NEXT, is_next);
flags.set(DescFlags::WRITE, elem.writable);
let mut d = Descriptor::new(elem.addr, elem.len, id, flags);
d.mark_avail(pos.wrap());
ring.write_desc(pos.head(), d);
pos.advance();
}
assert!(matches!(
consumer.poll_available(),
Err(RingError::WouldBlock)
));
let head_elem = chain.elems()[0];
let mut head_flags = DescFlags::empty();
head_flags.set(DescFlags::NEXT, true);
head_flags.set(DescFlags::WRITE, head_elem.writable);
let mut head_desc = Descriptor::new(head_elem.addr, head_elem.len, id, head_flags);
head_desc.mark_avail(wrap_start);
ring.write_desc(head_idx, head_desc);
producer.avail_cursor = pos;
producer.num_free -= chain.len();
let (dev_id, dev_chain) = consumer.poll_available().unwrap();
assert_eq!(dev_id, id);
assert_eq!(dev_chain.len(), chain.len());
assert_invariants(&ring, &producer);
}
#[test]
fn test_tail_marked_used_ignored() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
let chain = BufferChainBuilder::new()
.readable(0x1000, 4)
.readable(0x2000, 4)
.build()
.unwrap();
let id = producer.submit_available(&chain).unwrap();
let mut tail_desc = ring.read_desc(1);
tail_desc.mark_used(producer.used_cursor.wrap());
ring.write_desc(1, tail_desc);
assert!(matches!(producer.poll_used(), Err(RingError::WouldBlock)));
let mut head_desc = ring.read_desc(0);
head_desc.mark_used(producer.used_cursor.wrap());
ring.write_desc(0, head_desc);
let used = producer.poll_used().unwrap();
assert_eq!(used.id, id);
assert_invariants(&ring, &producer);
}
#[test]
fn test_max_chain_len_rejected() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
let elems = (0..9).map(|i| BufferElement {
addr: 0x1000 + i as u64,
len: 42,
writable: false,
});
let chain = BufferChainBuilder::new().readables(elems).build().unwrap();
assert!(matches!(
producer.submit_available(&chain),
Err(RingError::WouldBlock)
));
}
#[test]
fn test_descriptor_state_monotonicity() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
let mut consumer = make_consumer(&ring);
let mut states = vec![0u8; 8];
for _ in 0..5 {
for state in states.iter_mut() {
let id = producer.submit_one(0x1000, 4, false).unwrap();
*state = (*state).max(1);
let (dev_id, _) = consumer.poll_available().unwrap();
consumer.submit_used(dev_id, 4).unwrap();
*state = (*state).max(2);
let used = producer.poll_used().unwrap();
assert_eq!(used.id, id);
*state = (*state).max(3);
}
assert_invariants(&ring, &producer);
}
for s in states {
assert!(s >= 3);
}
}
#[test]
fn test_random_stress_small() {
use rand::RngExt;
use rand::seq::SliceRandom;
let ring = make_ring(16);
let mut producer = make_producer(&ring);
let mut consumer = make_consumer(&ring);
let mut rng = rand::rng();
let mut active_ids = Vec::new();
for _ in 0..8 {
let len = rng.random_range(1..=4);
let mut b = BufferChainBuilder::new().readable(0x1000, 4);
for i in 1..len {
b = b.readable(0x1000 + i as u64 * 0x10, 4);
}
let chain = b.build().unwrap();
if let Ok(id) = producer.submit_available(&chain) {
active_ids.push(id);
}
}
let mut dev_ids = Vec::new();
while let Ok((id, _)) = consumer.poll_available() {
dev_ids.push(id);
}
dev_ids.shuffle(&mut rng);
for id in &dev_ids {
let chain_len = consumer.id_num[*id as usize];
consumer.submit_used(*id, chain_len as u32 * 4).unwrap();
}
for _ in &dev_ids {
if producer.poll_used().is_ok() {}
}
assert_invariants(&ring, &producer);
}
#[test]
fn test_out_of_order_multi_length() {
let ring = make_ring(16);
let mut producer = make_producer(&ring);
let mut consumer = make_consumer(&ring);
let chain_a = BufferChainBuilder::new()
.readable(0x1000, 4)
.writable(0x2000, 4)
.build()
.unwrap();
let chain_b = BufferChainBuilder::new()
.readable(0x3000, 4)
.readable(0x3010, 4)
.writable(0x3020, 4)
.build()
.unwrap();
let chain_c = BufferChainBuilder::new()
.readable(0x4000, 4)
.build()
.unwrap();
let id_a = producer.submit_available(&chain_a).unwrap();
let id_b = producer.submit_available(&chain_b).unwrap();
let id_c = producer.submit_available(&chain_c).unwrap();
let (d_a, _) = consumer.poll_available().unwrap();
let (d_b, _) = consumer.poll_available().unwrap();
let (d_c, _) = consumer.poll_available().unwrap();
assert_eq!(d_a, id_a);
assert_eq!(d_b, id_b);
assert_eq!(d_c, id_c);
consumer.submit_used(d_b, 12).unwrap();
consumer.submit_used(d_c, 4).unwrap();
consumer.submit_used(d_a, 8).unwrap();
let u_b = producer.poll_used().unwrap();
assert_eq!(u_b.id, id_b);
let u_c = producer.poll_used().unwrap();
assert_eq!(u_c.id, id_c);
let u_a = producer.poll_used().unwrap();
assert_eq!(u_a.id, id_a);
assert_invariants(&ring, &producer);
}
#[test]
fn interleave_submit_and_completion() {
let ring = make_ring(16);
let mut producer = make_producer(&ring);
let mut consumer = make_consumer(&ring);
let chain_a = BufferChainBuilder::new()
.readable(0x1000, 4)
.writable(0x2000, 4)
.build()
.unwrap();
let id_a = producer.submit_available(&chain_a).unwrap();
let (d_a, _) = consumer.poll_available().unwrap();
assert_eq!(d_a, id_a);
consumer.submit_used(d_a, 8).unwrap();
let chain_b = BufferChainBuilder::new()
.readable(0x3000, 4)
.readable(0x3010, 4)
.writable(0x3020, 4)
.build()
.unwrap();
let id_b = producer.submit_available(&chain_b).unwrap();
let u_a = producer.poll_used().unwrap();
assert_eq!(u_a.id, id_a);
assert_eq!(u_a.len, 8);
let (d_b, _) = consumer.poll_available().unwrap();
assert_eq!(d_b, id_b);
consumer.submit_used(d_b, 12).unwrap();
let id_c = producer.submit_one(0x4000, 4, false).unwrap();
let (d_c, _) = consumer.poll_available().unwrap();
assert_eq!(d_c, id_c);
consumer.submit_used(d_c, 4).unwrap();
let u_b = producer.poll_used().unwrap();
assert_eq!(u_b.id, id_b);
assert_eq!(u_b.len, 12);
let u_c = producer.poll_used().unwrap();
assert_eq!(u_c.id, id_c);
assert_eq!(u_c.len, 4);
assert_invariants(&ring, &producer);
}
#[test]
fn producer_disable_used_notifications_writes_driver_disable() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
assert_eq!(ring.read_driver_event().flags(), EventFlags::ENABLE);
producer.disable_used_notifications().unwrap();
assert_eq!(ring.read_driver_event().flags(), EventFlags::DISABLE);
}
#[test]
fn producer_enable_used_notifications_writes_driver_enable() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
producer.disable_used_notifications().unwrap();
assert_eq!(ring.read_driver_event().flags(), EventFlags::DISABLE);
producer.enable_used_notifications().unwrap();
assert_eq!(ring.read_driver_event().flags(), EventFlags::ENABLE);
}
#[test]
fn producer_enable_used_notifications_desc_sets_off_wrap_and_flags() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
producer.enable_used_notifications_desc(5, true).unwrap();
let evt = ring.read_driver_event();
assert_eq!(evt.flags(), EventFlags::DESC);
assert_eq!(evt.desc_event_off(), 5);
assert!(evt.desc_event_wrap());
}
#[test]
fn producer_enable_used_notifications_for_next_programs_used_cursor() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
producer.enable_used_notifications_for_next().unwrap();
let evt = ring.read_driver_event();
assert_eq!(evt.flags(), EventFlags::DESC);
assert_eq!(evt.desc_event_off(), 0);
assert!(evt.desc_event_wrap());
}
#[test]
fn consumer_disable_avail_notifications_writes_device_disable() {
let ring = make_ring(8);
let mut consumer = make_consumer(&ring);
assert_eq!(ring.read_device_event().flags(), EventFlags::ENABLE);
consumer.disable_avail_notifications().unwrap();
assert_eq!(ring.read_device_event().flags(), EventFlags::DISABLE);
}
#[test]
fn consumer_enable_avail_notifications_writes_device_enable() {
let ring = make_ring(8);
let mut consumer = make_consumer(&ring);
consumer.disable_avail_notifications().unwrap();
assert_eq!(ring.read_device_event().flags(), EventFlags::DISABLE);
consumer.enable_avail_notifications().unwrap();
assert_eq!(ring.read_device_event().flags(), EventFlags::ENABLE);
}
#[test]
fn consumer_enable_avail_notifications_desc_sets_off_wrap_and_flags() {
let ring = make_ring(8);
let mut consumer = make_consumer(&ring);
consumer.enable_avail_notifications_desc(7, false).unwrap();
let evt = ring.read_device_event();
assert_eq!(evt.flags(), EventFlags::DESC);
assert_eq!(evt.desc_event_off(), 7);
assert!(!evt.desc_event_wrap());
}
#[test]
fn consumer_enable_avail_notifications_for_next_programs_avail_cursor() {
let ring = make_ring(8);
let mut consumer = make_consumer(&ring);
consumer.enable_avail_notifications_for_next().unwrap();
let evt = ring.read_device_event();
assert_eq!(evt.flags(), EventFlags::DESC);
assert_eq!(evt.desc_event_off(), 0);
assert!(evt.desc_event_wrap());
}
#[test]
fn producer_does_not_write_device_event_when_toggling_used_notifications() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
let dev_before = ring.read_device_event();
producer.disable_used_notifications().unwrap();
let dev_after = ring.read_device_event();
assert_eq!(dev_after, dev_before);
}
#[test]
fn consumer_does_not_write_driver_event_when_toggling_avail_notifications() {
let ring = make_ring(8);
let mut consumer = make_consumer(&ring);
let drv_before = ring.read_driver_event();
consumer.disable_avail_notifications().unwrap();
let drv_after = ring.read_driver_event();
assert_eq!(drv_after, drv_before);
}
#[test]
fn should_notify_flags_enable_disable() {
let ring_len = 8;
let old = RingCursor {
head: 0,
size: ring_len,
wrap: true,
};
let new = RingCursor {
head: 1,
size: ring_len,
wrap: true,
};
let evt = EventSuppression::new(0, EventFlags::DISABLE);
assert!(!should_notify(evt, ring_len, old, new));
let evt = EventSuppression::new(0, EventFlags::ENABLE);
assert!(should_notify(evt, ring_len, old, new));
}
#[test]
fn should_notify_desc_no_crossing() {
let ring_len = 8;
let old = RingCursor {
head: 2,
size: ring_len,
wrap: true,
};
let new = RingCursor {
head: 3,
size: ring_len,
wrap: true,
};
let mut evt = EventSuppression::zeroed();
evt.set_desc_event(6, true);
evt.set_flags(EventFlags::DESC);
assert!(!should_notify(evt, ring_len, old, new));
}
#[test]
fn should_notify_desc_wrap_mismatch_adjusts_event_idx() {
let ring_len = 8;
let old = RingCursor {
head: 7,
size: ring_len,
wrap: true,
};
let new = RingCursor {
head: 1,
size: ring_len,
wrap: false,
};
let mut evt = EventSuppression::zeroed();
evt.set_desc_event(7, true);
evt.set_flags(EventFlags::DESC);
assert!(should_notify(evt, ring_len, old, new));
}
#[test]
fn ring_need_event_basic_cases() {
assert!(ring_need_event(4, 5, 2));
assert!(!ring_need_event(4, 5, 5));
let old = 0xFFFE;
let new = 1;
assert!(ring_need_event(0xFFFF, new, old));
}
#[test]
fn bad_device_marks_tail_used() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
let chain = BufferChainBuilder::new()
.readable(0x1000, 4)
.readable(0x2000, 4)
.build()
.unwrap();
let id = producer.submit_available(&chain).unwrap();
let mut tail = ring.read_desc(1);
tail.mark_used(producer.used_cursor.wrap());
ring.write_desc(1, tail);
assert!(matches!(producer.poll_used(), Err(RingError::WouldBlock)));
let mut head = ring.read_desc(0);
head.mark_used(producer.used_cursor.wrap());
ring.write_desc(0, head);
let used = producer.poll_used().unwrap();
assert_eq!(used.id, id);
}
#[test]
fn bad_device_wrong_used_bits() {
let ring = make_ring(4);
let mut producer = make_producer(&ring);
let id = producer.submit_one(0x1000, 8, true).unwrap();
let mut d = ring.read_desc(0);
d.mark_avail(producer.used_cursor.wrap());
d.len = 8;
ring.write_desc(0, d);
assert!(matches!(producer.poll_used(), Err(RingError::WouldBlock)));
let mut d2 = ring.read_desc(0);
d2.mark_used(producer.used_cursor.wrap());
ring.write_desc(0, d2);
let u = producer.poll_used().unwrap();
assert_eq!(u.id, id);
}
#[test]
fn bad_driver_next_never_clears() {
let ring = make_ring(8);
let mut consumer = make_consumer(&ring);
let mut producer = make_producer(&ring);
let id = producer.id_free.pop().unwrap();
producer.id_num[id as usize] = 8;
let mut pos = producer.avail_cursor;
let wrap_start = pos.wrap();
for _ in 0..8 {
let idx = pos.head();
let mut flags = DescFlags::empty();
flags.set(DescFlags::NEXT, true); let mut desc = Descriptor::new(0x1000 + idx as u64 * 0x10, 4, id, flags);
desc.mark_avail(pos.wrap());
ring.write_desc(idx, desc);
pos.advance();
}
let head_idx = producer.avail_cursor.head();
let mut head_flags = DescFlags::empty();
head_flags.set(DescFlags::NEXT, true);
let mut head_desc = Descriptor::new(0x42, 4, id, head_flags);
head_desc.mark_avail(wrap_start);
ring.write_desc(head_idx, head_desc);
assert!(matches!(
consumer.poll_available(),
Err(RingError::BadChain)
));
}
#[test]
fn bad_driver_interleaved_readables_and_writables() {
let ring = make_ring(8);
let mut consumer = make_consumer(&ring);
let mut producer = make_producer(&ring);
let chain = BufferChainBuilder::new()
.readable(0x1000, 4)
.readable(0x2000, 4)
.writable(0x2000, 4)
.build()
.unwrap();
let _id = producer.submit_available(&chain).unwrap();
let mut first = ring.read_desc(0);
first.flags |= DescFlags::WRITE.bits();
ring.write_desc(0, first);
assert!(matches!(
consumer.poll_available(),
Err(RingError::BadChain)
));
}
#[test]
fn bad_device_marks_multiple_used_in_chain() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
let chain = BufferChainBuilder::new()
.readable(0x1000, 4)
.readable(0x2000, 4)
.build()
.unwrap();
let id = producer.submit_available(&chain).unwrap();
let mut head = ring.read_desc(0);
head.mark_used(producer.used_cursor.wrap());
ring.write_desc(0, head);
let mut tail = ring.read_desc(1);
tail.mark_used(producer.used_cursor.wrap());
ring.write_desc(1, tail);
let u = producer.poll_used().unwrap();
assert_eq!(u.id, id);
assert!(matches!(producer.poll_used(), Err(RingError::WouldBlock)));
}
#[test]
fn bad_device_writes_used_at_wrong_slot() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
let _id = producer.submit_one(0x1000, 4, true).unwrap();
let mut d = ring.read_desc(3);
d.mark_used(producer.used_cursor.wrap());
ring.write_desc(3, d);
assert!(matches!(producer.poll_used(), Err(RingError::WouldBlock)));
let mut d0 = ring.read_desc(0);
d0.mark_used(producer.used_cursor.wrap());
ring.write_desc(0, d0);
let _u = producer.poll_used().unwrap();
}
#[test]
fn bad_driver_reuses_id_while_outstanding() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
let id = producer.submit_one(0x1000, 4, false).unwrap();
assert_eq!(producer.id_num[id as usize], 1);
producer.id_free.push(id);
let res = producer.submit_one(0x2000, 4, false);
assert!(matches!(res, Err(RingError::InvalidState)));
}
#[test]
fn test_avail_cursor_accessor() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
let cursor = producer.avail_cursor();
assert_eq!(cursor.head(), 0);
assert!(cursor.wrap());
producer.submit_one(0x1000, 512, false).unwrap();
let cursor = producer.avail_cursor();
assert_eq!(cursor.head(), 1);
assert!(cursor.wrap());
}
#[test]
fn test_should_notify_since() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
let before = producer.avail_cursor();
producer.submit_one(0x1000, 512, false).unwrap();
let should_notify = producer.should_notify_since(before).unwrap();
assert!(should_notify);
}
#[test]
fn test_batch_notification_single_check() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
let mut consumer = make_consumer(&ring);
let before = producer.avail_cursor();
producer.submit_one(0x1000, 512, false).unwrap();
producer.submit_one(0x2000, 512, false).unwrap();
producer.submit_one(0x3000, 512, false).unwrap();
let should_notify = producer.should_notify_since(before).unwrap();
assert!(should_notify);
for _ in 0..3 {
let (_, _) = consumer.poll_available().unwrap();
}
}
#[test]
fn test_ring_cursor_reset() {
let mut cursor = RingCursor::new(16);
cursor.advance_by(5);
assert_eq!(cursor.head(), 5);
cursor.reset();
assert_eq!(cursor, RingCursor::new(16));
assert_eq!(cursor.head(), 0);
assert!(cursor.wrap());
}
#[test]
fn test_ring_cursor_reset_after_wrap() {
let mut cursor = RingCursor::new(4);
cursor.advance_by(5);
assert_eq!(cursor.head(), 1);
assert!(!cursor.wrap());
cursor.reset();
assert_eq!(cursor.head(), 0);
assert!(cursor.wrap());
}
#[test]
fn test_ring_producer_reset_matches_new() {
let ring = make_ring(8);
let fresh = make_producer(&ring);
let mut used = make_producer(&ring);
used.submit_one(0x1000, 64, false).unwrap();
used.submit_one(0x2000, 128, true).unwrap();
used.reset();
assert_eq!(used.avail_cursor, fresh.avail_cursor);
assert_eq!(used.used_cursor, fresh.used_cursor);
assert_eq!(used.num_free, fresh.num_free);
assert_eq!(used.id_free.len(), fresh.id_free.len());
assert_eq!(used.id_num.as_slice(), fresh.id_num.as_slice());
assert_eq!(used.event_flags_shadow, fresh.event_flags_shadow);
}
#[test]
fn test_ring_producer_reset_id_free_complete() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
for i in 0..4u64 {
producer.submit_one(0x1000 + i * 0x100, 64, false).unwrap();
}
assert_eq!(producer.num_free, 4);
producer.reset();
assert_eq!(producer.num_free, 8);
assert_eq!(producer.id_free.len(), 8);
for id in 0..8u16 {
assert!(producer.id_free.contains(&id));
}
}
#[test]
fn test_ring_consumer_reset_matches_new() {
let ring = make_ring(8);
let fresh = make_consumer(&ring);
let mut used = make_consumer(&ring);
let mut producer = make_producer(&ring);
producer.submit_one(0x1000, 64, false).unwrap();
let (id, _chain) = used.poll_available().unwrap();
used.submit_used(id, 64).unwrap();
used.reset();
assert_eq!(used.avail_cursor, fresh.avail_cursor);
assert_eq!(used.used_cursor, fresh.used_cursor);
assert_eq!(used.id_num.as_slice(), fresh.id_num.as_slice());
assert_eq!(used.num_inflight, fresh.num_inflight);
assert_eq!(used.event_flags_shadow, fresh.event_flags_shadow);
}
#[test]
fn test_ring_consumer_reset_clears_inflight() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
let mut consumer = make_consumer(&ring);
producer.submit_one(0x1000, 64, false).unwrap();
producer.submit_one(0x2000, 64, false).unwrap();
let _ = consumer.poll_available().unwrap();
let _ = consumer.poll_available().unwrap();
assert_eq!(consumer.num_inflight, 2);
consumer.reset();
assert_eq!(consumer.num_inflight, 0);
}
#[test]
fn test_reset_prefilled_sets_cursors() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
let ids: Vec<u16> = (0..8).collect();
producer.reset_prefilled(&ids);
assert_eq!(producer.avail_cursor.head(), 0);
assert!(!producer.avail_cursor.wrap());
assert_eq!(producer.used_cursor.head(), 0);
assert!(producer.used_cursor.wrap());
}
#[test]
fn test_reset_prefilled_all_ids_inflight() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
let ids: Vec<u16> = (0..8).collect();
producer.reset_prefilled(&ids);
assert_eq!(producer.num_free, 0);
assert!(producer.id_free.is_empty());
assert!(producer.id_num.iter().all(|&n| n == 1));
}
#[test]
fn test_reset_prefilled_partial() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
producer.reset_prefilled(&[5, 6, 7, 3]);
assert_eq!(producer.avail_cursor.head(), 4);
assert!(producer.avail_cursor.wrap());
assert_eq!(producer.used_cursor.head(), 0);
assert!(producer.used_cursor.wrap());
assert_eq!(producer.num_free, 4);
assert_eq!(producer.id_free.len(), 4);
for &id in &[0, 1, 2, 4] {
assert!(producer.id_free.contains(&id));
}
for &id in &[5, 6, 7, 3] {
assert_eq!(producer.id_num[id as usize], 1);
}
for &id in &[0, 1, 2, 4] {
assert_eq!(producer.id_num[id as usize], 0);
}
}
#[test]
fn test_reset_prefilled_partial_then_submit() {
let ring = make_ring(8);
let mut producer = make_producer(&ring);
producer.reset_prefilled(&[4, 5, 6, 7]);
let id = producer.submit_one(0x8000, 128, false).unwrap();
assert!([0, 1, 2, 3].contains(&id));
assert_eq!(producer.num_free, 3);
assert_eq!(producer.id_num[id as usize], 1);
}
#[test]
fn test_reset_prefilled_then_poll_used() {
let ring = make_ring(4);
let mut producer = make_producer(&ring);
for i in 0..4u64 {
producer.submit_one(0x1000 + i * 4096, 4096, true).unwrap();
}
let mut consumer = make_consumer(&ring);
let (id, _chain) = consumer.poll_available().unwrap();
consumer.submit_used(id, 64).unwrap();
let mut restored = make_producer(&ring);
restored.reset_prefilled(&[0, 1, 2, 3]);
let used = restored.poll_used().unwrap();
assert_eq!(used.id, id);
}
#[test]
fn test_desc_table_read_after_submit() {
let ring = make_ring(8);
let mut writer = make_producer(&ring);
writer.submit_one(0x1000, 4096, true).unwrap();
let reader = make_producer(&ring);
let addr = reader.desc_table().desc_addr(0).unwrap();
let flags = Descriptor::read_flags_acquire(reader.mem(), addr).unwrap();
let desc = Descriptor::read_body(reader.mem(), addr, flags).unwrap();
assert_eq!(desc.addr, 0x1000);
assert_eq!(desc.len, 4096);
assert!(desc.is_writable());
assert!(desc.is_avail(true));
assert!(!desc.is_used(true));
}
#[test]
fn test_desc_table_out_of_bounds() {
let ring = make_ring(8);
let reader = make_producer(&ring);
assert!(reader.desc_table().desc_addr(8).is_none());
}
#[test]
fn test_desc_table_read_used_descriptor() {
let ring = make_ring(8);
let mut writer = make_producer(&ring);
writer.submit_one(0x1000, 4096, true).unwrap();
let mut consumer = make_consumer(&ring);
let (id, _chain) = consumer.poll_available().unwrap();
consumer.submit_used(id, 128).unwrap();
let reader = make_producer(&ring);
let addr = reader.desc_table().desc_addr(0).unwrap();
let flags = Descriptor::read_flags_acquire(reader.mem(), addr).unwrap();
let desc = Descriptor::read_body(reader.mem(), addr, flags).unwrap();
assert!(desc.is_used(true));
assert!(!desc.is_avail(true));
}
}
#[cfg(test)]
mod fuzz {
use quickcheck::{Arbitrary, Gen, QuickCheck};
use super::tests::{OwnedRing, make_consumer, make_producer};
use super::*;
const MAX_RING: usize = 64;
const MAX_OPS: usize = 128;
const MAX_CHAIN_LEN: usize = 8;
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug)]
enum Op {
Submit(BufferChain),
PollAvail(u8),
PollUsed(u8),
CompleteOne,
}
impl Arbitrary for Op {
fn arbitrary(g: &mut Gen) -> Self {
let choice = u8::arbitrary(g) % 4;
match choice {
0 => Op::Submit(BufferChain::arbitrary(g)),
1 => Op::PollAvail(u8::arbitrary(g) % 8 + 1),
2 => Op::PollUsed(u8::arbitrary(g) % 8 + 1),
3 => Op::CompleteOne,
_ => unreachable!(),
}
}
}
#[derive(Clone, Debug)]
struct Scenario {
table_size: usize,
ops: Vec<Op>,
}
impl Arbitrary for Scenario {
fn arbitrary(g: &mut Gen) -> Self {
let table_size = (usize::arbitrary(g) % MAX_RING + 1).next_power_of_two();
let num_ops = usize::arbitrary(g) % MAX_OPS + 1;
let ops = (0..num_ops).map(|_| Op::arbitrary(g)).collect();
Scenario { table_size, ops }
}
}
impl Arbitrary for BufferElement {
fn arbitrary(g: &mut Gen) -> Self {
let addr = u64::arbitrary(g);
let len = u32::arbitrary(g);
let writable = bool::arbitrary(g);
BufferElement {
addr,
len,
writable,
}
}
}
impl Arbitrary for BufferChain {
fn arbitrary(g: &mut Gen) -> Self {
let chain_len = usize::arbitrary(g) % MAX_CHAIN_LEN + 1;
let mut elems = vec![BufferElement::zeroed(); chain_len];
let mut readables = 0;
let mut writables = 0;
for _ in 0..chain_len {
let elem = BufferElement::arbitrary(g);
if elem.writable {
elems[chain_len - 1 - writables] = elem;
writables += 1;
} else {
elems[readables] = elem;
readables += 1;
}
}
BufferChain {
elems: elems.into(),
split: readables,
}
}
}
fn run_scenario(s: Scenario) -> bool {
let ring = OwnedRing::new(s.table_size);
let mut producer = make_producer(&ring);
let mut consumer = make_consumer(&ring);
let mut dev_order: Vec<u16> = Vec::new();
let mut drv_order: Vec<u16> = Vec::new();
let mut dev_ready: Vec<(u16, u32)> = Vec::new();
for op in &s.ops {
match op {
Op::Submit(chain) => {
let _ = producer.submit_available(chain);
}
Op::PollAvail(n) => {
for _ in 0..*n {
if let Ok((id, chain)) = consumer.poll_available() {
dev_ready.push((id, chain.len() as u32));
} else {
break;
}
}
}
Op::PollUsed(n) => {
for _ in 0..*n {
match producer.poll_used() {
Ok(u) => {
drv_order.push(u.id);
if producer.id_num[u.id as usize] != 0 {
return false;
}
if !producer.id_free.contains(&u.id) {
return false;
}
}
Err(RingError::WouldBlock) => break,
Err(_) => return false,
}
}
}
Op::CompleteOne => {
if let Some((id, len)) = dev_ready.pop() {
if consumer.submit_used(id, len).is_err() {
return false;
}
dev_order.push(id);
}
}
}
let outstanding: u16 = producer.id_num.iter().copied().sum();
if outstanding as usize + producer.num_free != ring.len() {
return false;
}
for id in producer.id_free.iter() {
if producer.id_num[*id as usize] != 0 {
return false;
}
}
}
while let Some((id, len)) = dev_ready.pop() {
if consumer.submit_used(id, len).is_err() {
return false;
}
}
loop {
match producer.poll_used() {
Ok(u) => drv_order.push(u.id),
Err(RingError::WouldBlock) => break,
Err(_) => return false,
}
}
true
}
#[test]
fn prop_interleaved_with_order_verification() {
#[cfg(miri)]
let tests = 1;
#[cfg(not(miri))]
let tests = 100;
QuickCheck::new()
.tests(tests)
.quickcheck(run_scenario as fn(Scenario) -> bool);
}
}