Repository: firecracker-microvm/firecracker
File: src/vmm/src/devices/virtio/iovec.rs (Rust)

Code before:
    WriteOnlyDescriptor,
    /// Tried to create an 'IoVecMut` from a read-only descriptor chain
    ReadOnlyDescriptor,
    /// Guest memory error: {0}
    GuestMemory(#[from] GuestMemoryError),
}

// Using SmallVec in the kani proofs causes kani to use unbounded amounts of memory
// during post-processing, and then crash.
// TODO: remove new-type once kani performance regression are resolved
#[cfg(kani)]
type IoVecVec = Vec<iovec>;
#[cfg(not(kani))]
type IoVecVec = SmallVec<[iovec; 4]>;

/// This is essentially a wrapper of a `Vec<libc::iovec>` which can be passed to `libc::writev`.
///
/// It describes a 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, Default)]
pub struct IoVecBuffer {
    // container of the memory regions included in this IO vector
    vecs: IoVecVec,
    // Total length of the IoVecBuffer
    len: usize,
}

// SAFETY: `IoVecBuffer` doesn't allow for interior mutability and no shared ownership is possible
// as it doesn't implement clone
unsafe impl Send for IoVecBuffer {}

impl IoVecBuffer {
    /// Loads a `DescriptorChain` into the buffer
    ///
    /// # 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.len = 0;
        self.vecs.clear();

        let mut next_descriptor = Some(head);
        while let Some(desc) = next_descriptor {
            if desc.is_write_only() {
                return Err(IoVecError::WriteOnlyDescriptor);
            }

            // We use get_slice instead of `get_host_address` here in order to have the whole
            // range of the descriptor chain checked, i.e. [addr, addr + len) is a valid memory
            // region in the GuestMemoryMmap.

Review comment (performance):
Mh, now that we're reusing iovec buffers, I was hoping that the `IoVecVec`/`smallvec` stuff won't be needed anymore. Can we revert https://github.com/firecracker-microvm/firecracker/commit/e5a5d0e02b3d5941ada4f48891b836507cce1dea on top of this and run some performance tests to validate that hope? It'd simplify the code nicely

Code after:
    WriteOnlyDescriptor,
    /// Tried to create an 'IoVecMut` from a read-only descriptor chain
    ReadOnlyDescriptor,
    /// Tried to create an `IoVec` or `IoVecMut` from a descriptor chain that was too large
    OverflowedDescriptor,
    /// Guest memory error: {0}
    GuestMemory(#[from] GuestMemoryError),
}

// Using SmallVec in the kani proofs causes kani to use unbounded amounts of memory
// during post-processing, and then crash.
// TODO: remove new-type once kani performance regression are resolved
#[cfg(kani)]
type IoVecVec = Vec<iovec>;
#[cfg(not(kani))]
type IoVecVec = SmallVec<[iovec; 4]>;

/// This is essentially a wrapper of a `Vec<libc::iovec>` which can be passed to `libc::writev`.
///
/// It describes a 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, Default)]
pub struct IoVecBuffer {
    // container of the memory regions included in this IO vector
    vecs: IoVecVec,
    // Total length of the IoVecBuffer
    len: u32,
}

// SAFETY: `IoVecBuffer` doesn't allow for interior mutability and no shared ownership is possible
// as it doesn't implement clone
unsafe impl Send for IoVecBuffer {}

impl IoVecBuffer {
    /// 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();

        let mut next_descriptor = Some(head);
        while let Some(desc) = next_descriptor {
            if desc.is_write_only() {
                return Err(IoVecError::WriteOnlyDescriptor);
            }