Skip to content

Add VIRTIO_NET_F_MRG_RXBUF to virtio-net #4658

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 13 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub use vmm_sys_util::{
pub mod arg_parser;
pub mod byte_order;
pub mod net;
pub mod ring_buffer;
pub mod signal;
pub mod sm;
pub mod time;
Expand Down
193 changes: 193 additions & 0 deletions src/utils/src/ring_buffer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
#[derive(Debug, Default, Clone)]
pub struct RingBuffer<T: std::fmt::Debug + Default + Clone> {
pub items: Box<[T]>,
pub start: usize,
pub len: usize,
}

impl<T: std::fmt::Debug + Default + Clone> RingBuffer<T> {
/// New with zero size
pub fn new() -> Self {
Self {
items: Box::new([]),
start: 0,
len: 0,
}
}

/// New with specified size
pub fn new_with_size(size: usize) -> Self {
Self {
items: vec![T::default(); size].into_boxed_slice(),
start: 0,
len: 0,
}
}

/// Get number of items in the buffer
pub fn len(&self) -> usize {
self.len
}

/// Check if ring is empty
pub fn is_empty(&self) -> bool {
self.len == 0
}

/// Check if ring is full
pub fn is_full(&self) -> bool {
self.len == self.items.len()
}

/// Push new item to the end of the ring and increases
/// the length.
/// If there is no space for it, nothing will happen.
pub fn push(&mut self, item: T) {
if !self.is_full() {
let index = (self.start + self.len) % self.items.len();
self.items[index] = item;
self.len += 1;
}
}

/// Return next item that will be written to and increases
/// the length.
/// If ring is full returns None.
pub fn next_available(&mut self) -> Option<&mut T> {
if self.is_full() {
None
} else {
let index = (self.start + self.len) % self.items.len();
self.len += 1;
Some(&mut self.items[index])
}
}

/// Pop item from the from of the ring.
/// If ring is empty returns None.
pub fn pop_front(&mut self) -> Option<&mut T> {
if self.is_empty() {
None
} else {
let index = self.start;
self.start += 1;
self.start %= self.items.len();
self.len -= 1;
Some(&mut self.items[index])
}
}
}

#[cfg(test)]
pub mod tests {
use super::*;

#[test]
fn test_new() {
let a = RingBuffer::<u8>::new();
assert_eq!(a.items.len(), 0);
assert_eq!(a.start, 0);
assert_eq!(a.len, 0);
assert!(a.is_empty());
assert!(a.is_full());

let a = RingBuffer::<u8>::new_with_size(69);
assert_eq!(a.items.len(), 69);
assert_eq!(a.start, 0);
assert_eq!(a.len, 0);
assert!(a.is_empty());
assert!(!a.is_full());
}

#[test]
fn test_push() {
let mut a = RingBuffer::<u8>::new_with_size(4);

a.push(0);
assert!(!a.is_empty());
assert!(!a.is_full());

a.push(1);
assert!(!a.is_empty());
assert!(!a.is_full());

a.push(2);
assert!(!a.is_empty());
assert!(!a.is_full());

a.push(3);
assert!(!a.is_empty());
assert!(a.is_full());

assert_eq!(a.items.as_ref(), &[0, 1, 2, 3]);

a.push(4);
assert!(!a.is_empty());
assert!(a.is_full());

assert_eq!(a.items.as_ref(), &[0, 1, 2, 3]);
}

#[test]
fn test_next_available() {
let mut a = RingBuffer::<u8>::new_with_size(4);
assert!(a.is_empty());
assert!(!a.is_full());

*a.next_available().unwrap() = 0;
assert!(!a.is_empty());
assert!(!a.is_full());

*a.next_available().unwrap() = 1;
assert!(!a.is_empty());
assert!(!a.is_full());

*a.next_available().unwrap() = 2;
assert!(!a.is_empty());
assert!(!a.is_full());

*a.next_available().unwrap() = 3;
assert!(!a.is_empty());
assert!(a.is_full());

assert_eq!(a.items.as_ref(), &[0, 1, 2, 3]);

assert!(a.next_available().is_none());

assert_eq!(a.items.as_ref(), &[0, 1, 2, 3]);
}

#[test]
fn test_pop_front() {
let mut a = RingBuffer::<u8>::new_with_size(4);
a.push(0);
a.push(1);
a.push(2);
a.push(3);
assert!(!a.is_empty());
assert!(a.is_full());

assert_eq!(*a.pop_front().unwrap(), 0);
assert!(!a.is_empty());
assert!(!a.is_full());

assert_eq!(*a.pop_front().unwrap(), 1);
assert!(!a.is_empty());
assert!(!a.is_full());

assert_eq!(*a.pop_front().unwrap(), 2);
assert!(!a.is_empty());
assert!(!a.is_full());

assert_eq!(*a.pop_front().unwrap(), 3);
assert!(a.is_empty());
assert!(!a.is_full());

assert!(a.pop_front().is_none());
assert!(a.is_empty());
assert!(!a.is_full());
}
}
11 changes: 10 additions & 1 deletion src/vmm/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1225,7 +1225,16 @@ pub mod tests {
MmdsNetworkStack::default_ipv4_addr(),
Arc::new(Mutex::new(mmds)),
);

// Minimal setup for queues to be considered `valid`
for q in net.lock().unwrap().queues.iter_mut() {
q.ready = true;
q.size = 1;
// Need to explicitly set these addresses otherwise the aarch64
// will error out as it's memory does not start at 0.
q.desc_table = GuestAddress(crate::arch::SYSTEM_MEM_START);
q.avail_ring = GuestAddress(crate::arch::SYSTEM_MEM_START);
q.used_ring = GuestAddress(crate::arch::SYSTEM_MEM_START);
}
attach_net_devices(vmm, cmdline, net_builder.iter(), event_manager).unwrap();
}

Expand Down
92 changes: 73 additions & 19 deletions src/vmm/src/devices/virtio/iovec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,8 @@ impl IoVecBuffer {
///
/// The descriptor chain cannot be referencing the same memory location as another chain
pub unsafe fn from_descriptor_chain(head: DescriptorChain) -> Result<Self, IoVecError> {
let mut new_buffer: Self = Default::default();

let mut new_buffer = Self::default();
new_buffer.load_descriptor_chain(head)?;

Ok(new_buffer)
}

Expand Down Expand Up @@ -217,21 +215,31 @@ impl IoVecBuffer {
/// It describes a write-only buffer passed to us by the guest that is scattered across multiple
/// memory regions. Additionally, this wrapper provides methods that allow reading arbitrary ranges
/// of data from that buffer.
#[derive(Debug)]
#[derive(Debug, Default, Clone)]
pub struct IoVecBufferMut {
// Index of the head desciptor
pub head_index: u16,
// container of the memory regions included in this IO vector
vecs: IoVecVec,
pub vecs: IoVecVec,
// Total length of the IoVecBufferMut
len: u32,
pub len: u32,
}

impl IoVecBufferMut {
/// Create an `IoVecBufferMut` from a `DescriptorChain`
pub fn from_descriptor_chain(head: DescriptorChain) -> Result<Self, IoVecError> {
let mut vecs = IoVecVec::new();
let mut len = 0u32;
/// Create an `IoVecBuffer` from a `DescriptorChain`
///
/// # Safety
///
/// The descriptor chain cannot be referencing the same memory location as another chain
pub unsafe fn load_descriptor_chain(
&mut self,
head: DescriptorChain,
) -> Result<(), IoVecError> {
self.clear();
self.head_index = head.index;

for desc in head {
let mut next_descriptor = Some(head);
while let Some(desc) = next_descriptor {
if !desc.is_write_only() {
return Err(IoVecError::ReadOnlyDescriptor);
}
Expand All @@ -247,23 +255,60 @@ impl IoVecBufferMut {
slice.bitmap().mark_dirty(0, desc.len as usize);

let iov_base = slice.ptr_guard_mut().as_ptr().cast::<c_void>();
vecs.push(iovec {
self.vecs.push(iovec {
iov_base,
iov_len: desc.len as size_t,
});
len = len
self.len = self
.len
.checked_add(desc.len)
.ok_or(IoVecError::OverflowedDescriptor)?;

next_descriptor = desc.next_descriptor();
}

Ok(Self { vecs, len })
Ok(())
}

/// Create an `IoVecBuffer` from a `DescriptorChain`
///
/// # Safety
///
/// The descriptor chain cannot be referencing the same memory location as another chain
pub unsafe fn from_descriptor_chain(head: DescriptorChain) -> Result<Self, IoVecError> {
let mut new_buffer = Self::default();
new_buffer.load_descriptor_chain(head)?;
Ok(new_buffer)
}

/// Get the index of the haed descriptor from which this IoVecBuffer
/// was built.
pub fn head_index(&self) -> u16 {
self.head_index
}

/// Get the host pointer to the first buffer in the guest,
/// this buffer points to.
///
/// # Safety
///
/// It is assumed that IoVecBuffer will never have 0 elements
/// as it is build from at DescriptorChain with length of at least 1.
pub fn start_address(&self) -> *mut libc::c_void {
self.vecs[0].iov_base
}

/// Get the total length of the memory regions covered by this `IoVecBuffer`
pub(crate) fn len(&self) -> u32 {
self.len
}

/// Clears the `iovec` array
pub fn clear(&mut self) {
self.vecs.clear();
self.len = 0u32;
}

/// Writes a number of bytes into the `IoVecBufferMut` starting at a given offset.
///
/// This will try to fill `IoVecBufferMut` writing bytes from the `buf` starting from
Expand Down Expand Up @@ -397,6 +442,7 @@ mod tests {
impl From<&mut [u8]> for IoVecBufferMut {
fn from(buf: &mut [u8]) -> Self {
Self {
head_index: 0,
vecs: vec![iovec {
iov_base: buf.as_mut_ptr().cast::<c_void>(),
iov_len: buf.len(),
Expand Down Expand Up @@ -468,11 +514,13 @@ mod tests {

let (mut q, _) = read_only_chain(&mem);
let head = q.pop(&mem).unwrap();
IoVecBufferMut::from_descriptor_chain(head).unwrap_err();
// SAFETY: This descriptor chain is only loaded into one buffer
unsafe { IoVecBufferMut::from_descriptor_chain(head).unwrap_err() };

let (mut q, _) = write_only_chain(&mem);
let head = q.pop(&mem).unwrap();
IoVecBufferMut::from_descriptor_chain(head).unwrap();
// SAFETY: This descriptor chain is only loaded into one buffer
unsafe { IoVecBufferMut::from_descriptor_chain(head).unwrap() };
}

#[test]
Expand All @@ -493,7 +541,7 @@ mod tests {
let head = q.pop(&mem).unwrap();

// SAFETY: This descriptor chain is only loaded once in this test
let iovec = IoVecBufferMut::from_descriptor_chain(head).unwrap();
let iovec = unsafe { IoVecBufferMut::from_descriptor_chain(head).unwrap() };
assert_eq!(iovec.len(), 4 * 64);
}

Expand Down Expand Up @@ -558,7 +606,8 @@ mod tests {
// This is a descriptor chain with 4 elements 64 bytes long each.
let head = q.pop(&mem).unwrap();

let mut iovec = IoVecBufferMut::from_descriptor_chain(head).unwrap();
// SAFETY: This descriptor chain is only loaded into one buffer
let mut iovec = unsafe { IoVecBufferMut::from_descriptor_chain(head).unwrap() };
let buf = vec![0u8, 1, 2, 3, 4];

// One test vector for each part of the chain
Expand Down Expand Up @@ -705,7 +754,12 @@ mod verification {
};

let (vecs, len) = create_iovecs(mem, GUEST_MEMORY_SIZE);
Self { vecs, len }
let head_index = kani::any();
Self {
head_index,
vecs,
len,
}
}
}

Expand Down
Loading
Loading