use alloc::vec;
use bytes::Bytes;
use fixedbitset::FixedBitSet;
use smallvec::SmallVec;
use super::*;
type WritableElems = SmallVec<[BufferElement; 2]>;
#[derive(Debug, Clone)]
pub struct RecvChain {
token: Token,
segments: Segments,
}
impl RecvChain {
pub fn token(&self) -> Token {
self.token
}
pub fn segments(&self) -> &Segments {
&self.segments
}
pub fn into_segments(self) -> Segments {
self.segments
}
pub fn to_bytes(&self) -> Bytes {
self.segments.to_bytes()
}
pub fn into_bytes(self) -> Bytes {
self.segments.into_bytes()
}
}
#[must_use = "dropping without completing leaks the descriptor"]
pub enum ReplyChain<M: MemOps> {
Writable(WritableChain<M>),
Ack(AckChain),
}
impl<M: MemOps> ReplyChain<M> {
pub fn token(&self) -> Token {
match self {
ReplyChain::Writable(wc) => wc.token(),
ReplyChain::Ack(ack) => ack.token(),
}
}
pub fn written(&self) -> usize {
match self {
ReplyChain::Writable(wc) => wc.written,
ReplyChain::Ack(_) => 0,
}
}
pub fn into_writable(self) -> Result<WritableChain<M>, AckChain> {
match self {
ReplyChain::Writable(wc) => Ok(wc),
ReplyChain::Ack(ack) => Err(ack),
}
}
}
#[must_use = "dropping without completing leaks the descriptor"]
pub struct WritableChain<M: MemOps> {
mem: M,
token: Token,
elems: WritableElems,
capacity: usize,
written: usize,
}
impl<M: MemOps> WritableChain<M> {
fn new(mem: M, token: Token, elems: WritableElems) -> Self {
let capacity = elems.iter().map(|elem| elem.len as usize).sum();
Self {
mem,
token,
elems,
capacity,
written: 0,
}
}
pub fn token(&self) -> Token {
self.token
}
pub fn capacity(&self) -> usize {
self.capacity
}
pub fn written(&self) -> usize {
self.written
}
pub fn remaining(&self) -> usize {
self.capacity() - self.written()
}
pub fn write(&mut self, buf: &[u8]) -> Result<usize, VirtqError> {
let written = write_elements(&self.mem, &self.elems, self.written, buf)
.map_err(|_| VirtqError::MemoryWriteError)?;
self.written += written;
Ok(written)
}
pub fn write_all(&mut self, buf: &[u8]) -> Result<&mut Self, VirtqError> {
if buf.len() > self.remaining() {
return Err(VirtqError::ReplyTooLarge);
}
let written = self.write(buf)?;
debug_assert_eq!(written, buf.len());
Ok(self)
}
pub fn rewind(&mut self) {
self.written = 0;
}
}
#[must_use = "dropping without completing leaks the descriptor"]
pub struct AckChain {
token: Token,
}
impl AckChain {
fn new(token: Token) -> Self {
Self { token }
}
pub fn token(&self) -> Token {
self.token
}
}
pub struct VirtqConsumer<M, N> {
inner: RingConsumer<M>,
notifier: N,
inflight: FixedBitSet,
next_token: u32,
}
impl<M: MemOps + Clone, N: Notifier> VirtqConsumer<M, N> {
pub fn new(layout: Layout, mem: M, notifier: N) -> Self {
let inner = RingConsumer::new(layout, mem);
let inflight = FixedBitSet::with_capacity(inner.len());
Self {
inner,
notifier,
inflight,
next_token: 0,
}
}
pub fn poll(
&mut self,
max_recv_len: usize,
) -> Result<Option<(RecvChain, ReplyChain<M>)>, VirtqError> {
let (id, chain) = match self.inner.poll_available() {
Ok(x) => x,
Err(RingError::WouldBlock) => return Ok(None),
Err(e) => return Err(e.into()),
};
let readables = chain.readables();
let writables = chain.writables();
if readables.is_empty() && writables.is_empty() {
return Err(self.abort_chain(id, VirtqError::BadChain));
}
let recv_len = readables
.iter()
.fold(0usize, |acc, elem| acc.saturating_add(elem.len as usize));
let id_idx = id as usize;
if id_idx >= self.inflight.len() {
return Err(VirtqError::InvalidState);
}
if self.inflight.contains(id_idx) {
return Err(VirtqError::InvalidState);
}
self.inflight.insert(id_idx);
let token = Token {
seq: self.next_token,
id,
};
self.next_token = self.next_token.wrapping_add(1);
if recv_len > max_recv_len {
return Err(self.abort_chain(
id,
VirtqError::PayloadTooLarge {
recv: recv_len,
limit: max_recv_len,
},
));
}
let data = match self.read_elements(readables) {
Ok(d) => d,
Err(e) => return Err(self.abort_chain(id, e)),
};
let chain = RecvChain {
token,
segments: data,
};
let reply = if !writables.is_empty() {
let mem = self.inner.mem().clone();
let writable = WritableChain::new(mem, token, writables.iter().copied().collect());
ReplyChain::Writable(writable)
} else {
let ack = AckChain::new(token);
ReplyChain::Ack(ack)
};
Ok(Some((chain, reply)))
}
pub fn complete(&mut self, reply: impl Into<ReplyChain<M>>) -> Result<(), VirtqError> {
let reply = reply.into();
let id = reply.token().id;
let written = u32::try_from(reply.written()).map_err(|_| VirtqError::ReplyTooLarge)?;
let id_idx = id as usize;
let slot_set = id_idx < self.inflight.len() && self.inflight.contains(id_idx);
if !slot_set {
return Err(VirtqError::InvalidState);
}
self.inflight.set(id_idx, false);
if self.inner.submit_used_with_notify(id, written)? {
self.notifier.notify(QueueStats {
num_free: self.inner.num_free(),
num_inflight: self.inner.num_inflight(),
});
}
Ok(())
}
fn abort_chain(&mut self, id: u16, err: VirtqError) -> VirtqError {
let id_idx = id as usize;
if id_idx < self.inflight.len() {
self.inflight.set(id_idx, false);
}
if let Ok(true) = self.inner.submit_used_with_notify(id, 0) {
self.notifier.notify(QueueStats {
num_free: self.inner.num_free(),
num_inflight: self.inner.num_inflight(),
});
}
err
}
#[inline]
pub fn avail_cursor(&self) -> RingCursor {
self.inner.avail_cursor()
}
#[inline]
pub fn used_cursor(&self) -> RingCursor {
self.inner.used_cursor()
}
pub fn set_avail_suppression(&mut self, kind: SuppressionKind) -> Result<(), VirtqError> {
match kind {
SuppressionKind::Enable => self.inner.enable_avail_notifications()?,
SuppressionKind::Disable => self.inner.disable_avail_notifications()?,
SuppressionKind::Descriptor(cursor) => self
.inner
.enable_avail_notifications_desc(cursor.head(), cursor.wrap())?,
}
Ok(())
}
fn read_elements(&self, elems: &[BufferElement]) -> Result<Segments, VirtqError> {
let mut segments = SmallVec::<[Bytes; 4]>::new();
for elem in elems {
let mut buf = vec![0u8; elem.len as usize];
self.inner
.mem()
.read(elem.addr, &mut buf)
.map_err(|_| VirtqError::MemoryReadError)?;
segments.push(Bytes::from(buf));
}
Ok(Segments::from_smallvec(segments))
}
pub fn reset(&mut self) {
self.inner.reset();
self.inflight.clear();
}
}
fn write_elements<M: MemOps>(
mem: &M,
elems: &[BufferElement],
offset: usize,
buf: &[u8],
) -> Result<usize, M::Error> {
let capacity: usize = elems.iter().map(|elem| elem.len as usize).sum();
let mut src = &buf[..buf.len().min(capacity.saturating_sub(offset))];
let mut written = 0;
let mut skip = offset;
for elem in elems {
if src.is_empty() {
break;
}
let elem_len = elem.len as usize;
if skip >= elem_len {
skip -= elem_len;
continue;
}
let elem_offset = skip;
skip = 0;
let n = (elem_len - elem_offset).min(src.len());
let addr = elem.addr + elem_offset as u64;
mem.write(addr, &src[..n])?;
written += n;
src = &src[n..];
}
Ok(written)
}
impl<M: MemOps> From<WritableChain<M>> for ReplyChain<M> {
fn from(wc: WritableChain<M>) -> Self {
ReplyChain::Writable(wc)
}
}
impl<M: MemOps> From<AckChain> for ReplyChain<M> {
fn from(ack: AckChain) -> Self {
ReplyChain::Ack(ack)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::virtq::ring::tests::{make_producer, make_ring};
use crate::virtq::test_utils::*;
fn poll_data(
consumer: &mut VirtqConsumer<crate::virtq::ring::tests::TestMem, TestNotifier>,
) -> (RecvChain, ReplyChain<crate::virtq::ring::tests::TestMem>) {
consumer.poll(1024).unwrap().unwrap()
}
#[test]
fn test_write_only_recv_is_empty() {
let ring = make_ring(16);
let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
let se = producer.chain().writable(16).build().unwrap();
producer.submit(se).unwrap();
let (recv, reply) = poll_data(&mut consumer);
assert!(recv.to_bytes().is_empty());
assert!(matches!(reply, ReplyChain::Writable(_)));
if let ReplyChain::Writable(mut wc) = reply {
wc.write_all(b"response").unwrap();
consumer.complete(wc).unwrap();
}
}
#[test]
fn test_read_only_ack_reply() {
let ring = make_ring(16);
let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
let mut se = producer.chain().readable(16).build().unwrap();
se.write_all(b"hello").unwrap();
producer.submit(se).unwrap();
let (recv, reply) = poll_data(&mut consumer);
assert_eq!(recv.to_bytes().as_ref(), b"hello");
assert!(matches!(reply, ReplyChain::Ack(_)));
consumer.complete(reply).unwrap();
}
#[test]
fn test_readwrite_round_trip() {
let ring = make_ring(16);
let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
let mut se = producer.chain().readable(32).writable(64).build().unwrap();
se.write_all(b"hello world").unwrap();
producer.submit(se).unwrap();
let (recv, reply) = poll_data(&mut consumer);
assert_eq!(recv.to_bytes().as_ref(), b"hello world");
if let ReplyChain::Writable(mut wc) = reply {
assert_eq!(wc.capacity(), 64);
assert_eq!(wc.written(), 0);
assert_eq!(wc.remaining(), 64);
wc.write_all(b"response").unwrap();
assert_eq!(wc.written(), 8);
assert_eq!(wc.remaining(), 56);
consumer.complete(wc).unwrap();
} else {
panic!("expected Writable reply for recv+reply chain");
}
}
#[test]
fn test_writable_partial_write() {
let ring = make_ring(16);
let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
let se = producer.chain().writable(8).build().unwrap();
producer.submit(se).unwrap();
let (_recv, reply) = poll_data(&mut consumer);
if let ReplyChain::Writable(mut wc) = reply {
let n = wc.write(b"hello world!").unwrap();
assert_eq!(n, 8);
assert_eq!(wc.remaining(), 0);
consumer.complete(wc).unwrap();
} else {
panic!("expected Writable");
}
}
#[test]
fn test_writable_write_all_too_large() {
let ring = make_ring(16);
let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
let se = producer.chain().writable(4).build().unwrap();
producer.submit(se).unwrap();
let (_recv, reply) = poll_data(&mut consumer);
if let ReplyChain::Writable(mut wc) = reply {
let err = wc.write_all(b"too long").err().unwrap();
assert!(matches!(err, VirtqError::ReplyTooLarge));
} else {
panic!("expected Writable");
}
}
#[test]
fn test_poll_too_large_returns_payload_error() {
let ring = make_ring(16);
let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
let mut se = producer.chain().readable(8).writable(16).build().unwrap();
se.write_all(b"too much").unwrap();
producer.submit(se).unwrap();
assert!(matches!(
consumer.poll(4),
Err(VirtqError::PayloadTooLarge { recv: 8, limit: 4 })
));
}
#[test]
fn test_poll_too_large_returns_descriptor() {
let ring = make_ring(16);
let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
let mut se = producer.chain().readable(8).writable(16).build().unwrap();
se.write_all(b"too much").unwrap();
let token = producer.submit(se).unwrap();
assert!(matches!(
consumer.poll(4),
Err(VirtqError::PayloadTooLarge { recv: 8, limit: 4 })
));
let used = producer.poll().unwrap().unwrap();
assert_eq!(used.token(), token);
let se2 = producer.chain().writable(16).build().unwrap();
producer.submit(se2).unwrap();
let (_recv, reply) = poll_data(&mut consumer);
consumer.complete(reply).unwrap();
assert!(producer.poll().unwrap().is_some());
}
#[test]
fn test_writable_chain_writes_single_segment() {
let ring = make_ring(16);
let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
let se = producer.chain().writable(16).build().unwrap();
producer.submit(se).unwrap();
let (_recv, reply) = poll_data(&mut consumer);
let ReplyChain::Writable(mut wc) = reply else {
panic!("expected Writable");
};
wc.write_all(b"hello").unwrap();
consumer.complete(wc).unwrap();
let used = producer.poll().unwrap().unwrap();
assert_eq!(used.to_bytes().unwrap().as_ref(), b"hello");
}
#[test]
fn test_writable_rewind() {
let ring = make_ring(16);
let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
let se = producer.chain().writable(16).build().unwrap();
producer.submit(se).unwrap();
let (_recv, reply) = poll_data(&mut consumer);
if let ReplyChain::Writable(mut wc) = reply {
wc.write_all(b"first").unwrap();
assert_eq!(wc.written(), 5);
wc.rewind();
assert_eq!(wc.written(), 0);
assert_eq!(wc.remaining(), 16);
wc.write_all(b"second").unwrap();
assert_eq!(wc.written(), 6);
consumer.complete(wc).unwrap();
} else {
panic!("expected Writable");
}
}
#[test]
fn test_writable_reply_scatters_across_segments() {
let ring = make_ring(16);
let mem = ring.mem();
let mut ring_producer = make_producer(&ring);
let mut consumer = VirtqConsumer::new(ring.layout(), mem.clone(), TestNotifier::new());
let base = mem.base_addr() + Layout::query_size(ring.len()) as u64 + 0x100;
let chain = BufferChainBuilder::new()
.writable(base, 4)
.writable(base + 4, 4)
.build()
.unwrap();
let id = ring_producer.submit_available(&chain).unwrap();
let (recv, reply) = poll_data(&mut consumer);
assert!(recv.to_bytes().is_empty());
let ReplyChain::Writable(mut wc) = reply else {
panic!("expected Writable");
};
assert_eq!(wc.capacity(), 8);
wc.write_all(b"abcdefgh").unwrap();
assert_eq!(wc.written(), 8);
consumer.complete(wc).unwrap();
let mut first = [0u8; 4];
let mut second = [0u8; 4];
mem.read(base, &mut first).unwrap();
mem.read(base + 4, &mut second).unwrap();
assert_eq!(&first, b"abcd");
assert_eq!(&second, b"efgh");
let used = ring_producer.poll_used().unwrap();
assert_eq!(used.id, id);
assert_eq!(used.len, 8);
}
#[test]
fn test_multiple_pending_replies() {
let ring = make_ring(16);
let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
let se1 = producer.chain().writable(16).build().unwrap();
producer.submit(se1).unwrap();
let se2 = producer.chain().writable(16).build().unwrap();
producer.submit(se2).unwrap();
let (_e1, c1) = poll_data(&mut consumer);
let (_e2, c2) = poll_data(&mut consumer);
consumer.complete(c2).unwrap();
consumer.complete(c1).unwrap();
}
#[test]
fn test_recv_into_bytes() {
let ring = make_ring(16);
let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
let mut se = producer.chain().readable(16).build().unwrap();
se.write_all(b"abc").unwrap();
producer.submit(se).unwrap();
let (recv, reply) = poll_data(&mut consumer);
let data = recv.into_bytes();
assert_eq!(data.as_ref(), b"abc");
consumer.complete(reply).unwrap();
}
#[test]
fn test_virtq_consumer_reset() {
let ring = make_ring(16);
let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
let se = producer.chain().writable(16).build().unwrap();
producer.submit(se).unwrap();
let (_recv, reply) = poll_data(&mut consumer);
assert!(consumer.inflight.count_ones(..) > 0);
consumer.complete(reply).unwrap();
consumer.reset();
assert_eq!(consumer.inflight.count_ones(..), 0);
assert_eq!(consumer.inner.num_inflight(), 0);
}
#[test]
fn test_virtq_consumer_reset_clears_inflight() {
let ring = make_ring(16);
let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
let se1 = producer.chain().writable(16).build().unwrap();
producer.submit(se1).unwrap();
let se2 = producer.chain().writable(16).build().unwrap();
producer.submit(se2).unwrap();
let (_e1, c1) = poll_data(&mut consumer);
let (_e2, c2) = poll_data(&mut consumer);
consumer.complete(c1).unwrap();
consumer.complete(c2).unwrap();
consumer.reset();
assert_eq!(consumer.inflight.count_ones(..), 0);
assert_eq!(consumer.inner.num_inflight(), 0);
}
}