Skip to content

Commit 68e1973

Browse files
JackThomson2bchalios
authored andcommitted
Reuse the IoVecBuffer on TX
On the net virtio device reuse the IoVecBuffer on the TX path Signed-off-by: Jack Thomson <[email protected]>
1 parent be2f41a commit 68e1973

File tree

3 files changed

+70
-24
lines changed

3 files changed

+70
-24
lines changed

src/vmm/src/devices/virtio/iovec.rs

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,19 +37,29 @@ type IoVecVec = SmallVec<[iovec; 4]>;
3737
/// It describes a buffer passed to us by the guest that is scattered across multiple
3838
/// memory regions. Additionally, this wrapper provides methods that allow reading arbitrary ranges
3939
/// of data from that buffer.
40-
#[derive(Debug)]
40+
#[derive(Debug, Default)]
4141
pub struct IoVecBuffer {
4242
// container of the memory regions included in this IO vector
4343
vecs: IoVecVec,
4444
// Total length of the IoVecBuffer
4545
len: u32,
4646
}
4747

48+
// SAFETY: `IoVecBuffer` doesn't allow for interior mutability and no shared ownership is possible
49+
// as it doesn't implement clone
50+
unsafe impl Send for IoVecBuffer {}
51+
4852
impl IoVecBuffer {
4953
/// Create an `IoVecBuffer` from a `DescriptorChain`
50-
pub fn from_descriptor_chain(head: DescriptorChain) -> Result<Self, IoVecError> {
51-
let mut vecs = IoVecVec::new();
52-
let mut len = 0u32;
54+
///
55+
/// # Safety
56+
///
57+
/// The descriptor chain cannot be referencing the same memory location as another chain
58+
pub unsafe fn load_descriptor_chain(
59+
&mut self,
60+
head: DescriptorChain,
61+
) -> Result<(), IoVecError> {
62+
self.clear();
5363

5464
let mut next_descriptor = Some(head);
5565
while let Some(desc) = next_descriptor {
@@ -66,18 +76,32 @@ impl IoVecBuffer {
6676
.ptr_guard_mut()
6777
.as_ptr()
6878
.cast::<c_void>();
69-
vecs.push(iovec {
79+
self.vecs.push(iovec {
7080
iov_base,
7181
iov_len: desc.len as size_t,
7282
});
73-
len = len
83+
self.len = self
84+
.len
7485
.checked_add(desc.len)
7586
.ok_or(IoVecError::OverflowedDescriptor)?;
7687

7788
next_descriptor = desc.next_descriptor();
7889
}
7990

80-
Ok(Self { vecs, len })
91+
Ok(())
92+
}
93+
94+
/// Create an `IoVecBuffer` from a `DescriptorChain`
95+
///
96+
/// # Safety
97+
///
98+
/// The descriptor chain cannot be referencing the same memory location as another chain
99+
pub unsafe fn from_descriptor_chain(head: DescriptorChain) -> Result<Self, IoVecError> {
100+
let mut new_buffer: Self = Default::default();
101+
102+
new_buffer.load_descriptor_chain(head)?;
103+
104+
Ok(new_buffer)
81105
}
82106

83107
/// Get the total length of the memory regions covered by this `IoVecBuffer`
@@ -95,6 +119,12 @@ impl IoVecBuffer {
95119
self.vecs.len()
96120
}
97121

122+
/// Clears the `iovec` array
123+
pub fn clear(&mut self) {
124+
self.vecs.clear();
125+
self.len = 0u32;
126+
}
127+
98128
/// Reads a number of bytes from the `IoVecBuffer` starting at a given offset.
99129
///
100130
/// This will try to fill `buf` reading bytes from the `IoVecBuffer` starting from
@@ -428,11 +458,13 @@ mod tests {
428458
let mem = default_mem();
429459
let (mut q, _) = read_only_chain(&mem);
430460
let head = q.pop(&mem).unwrap();
431-
IoVecBuffer::from_descriptor_chain(head).unwrap();
461+
// SAFETY: This descriptor chain is only loaded into one buffer
462+
unsafe { IoVecBuffer::from_descriptor_chain(head).unwrap() };
432463

433464
let (mut q, _) = write_only_chain(&mem);
434465
let head = q.pop(&mem).unwrap();
435-
IoVecBuffer::from_descriptor_chain(head).unwrap_err();
466+
// SAFETY: This descriptor chain is only loaded into one buffer
467+
unsafe { IoVecBuffer::from_descriptor_chain(head).unwrap_err() };
436468

437469
let (mut q, _) = read_only_chain(&mem);
438470
let head = q.pop(&mem).unwrap();
@@ -449,7 +481,8 @@ mod tests {
449481
let (mut q, _) = read_only_chain(&mem);
450482
let head = q.pop(&mem).unwrap();
451483

452-
let iovec = IoVecBuffer::from_descriptor_chain(head).unwrap();
484+
// SAFETY: This descriptor chain is only loaded once in this test
485+
let iovec = unsafe { IoVecBuffer::from_descriptor_chain(head).unwrap() };
453486
assert_eq!(iovec.len(), 4 * 64);
454487
}
455488

@@ -459,6 +492,7 @@ mod tests {
459492
let (mut q, _) = write_only_chain(&mem);
460493
let head = q.pop(&mem).unwrap();
461494

495+
// SAFETY: This descriptor chain is only loaded once in this test
462496
let iovec = IoVecBufferMut::from_descriptor_chain(head).unwrap();
463497
assert_eq!(iovec.len(), 4 * 64);
464498
}
@@ -469,7 +503,8 @@ mod tests {
469503
let (mut q, _) = read_only_chain(&mem);
470504
let head = q.pop(&mem).unwrap();
471505

472-
let iovec = IoVecBuffer::from_descriptor_chain(head).unwrap();
506+
// SAFETY: This descriptor chain is only loaded once in this test
507+
let iovec = unsafe { IoVecBuffer::from_descriptor_chain(head).unwrap() };
473508

474509
let mut buf = vec![0u8; 257];
475510
assert_eq!(

src/vmm/src/devices/virtio/net/device.rs

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@ pub struct Net {
142142
/// Only if MMDS transport has been associated with it.
143143
pub mmds_ns: Option<MmdsNetworkStack>,
144144
pub(crate) metrics: Arc<NetDeviceMetrics>,
145+
146+
tx_buffer: IoVecBuffer,
145147
}
146148

147149
impl Net {
@@ -199,6 +201,7 @@ impl Net {
199201
activate_evt: EventFd::new(libc::EFD_NONBLOCK).map_err(NetError::EventFd)?,
200202
mmds_ns: None,
201203
metrics: NetMetricsPerDevice::alloc(id),
204+
tx_buffer: Default::default(),
202205
})
203206
}
204207

@@ -597,19 +600,19 @@ impl Net {
597600
.add(tx_queue.len(mem).into());
598601
let head_index = head.index;
599602
// Parse IoVecBuffer from descriptor head
600-
let buffer = match IoVecBuffer::from_descriptor_chain(head) {
601-
Ok(buffer) => buffer,
602-
Err(_) => {
603-
self.metrics.tx_fails.inc();
604-
tx_queue
605-
.add_used(mem, head_index, 0)
606-
.map_err(DeviceError::QueueError)?;
607-
continue;
608-
}
603+
// SAFETY: This descriptor chain is only loaded once
604+
// virtio requests are handled sequentially so no two IoVecBuffers
605+
// are live at the same time, meaning this has exclusive ownership over the memory
606+
if unsafe { self.tx_buffer.load_descriptor_chain(head).is_err() } {
607+
self.metrics.tx_fails.inc();
608+
tx_queue
609+
.add_used(mem, head_index, 0)
610+
.map_err(DeviceError::QueueError)?;
611+
continue;
609612
};
610613

611614
// We only handle frames that are up to MAX_BUFFER_SIZE
612-
if buffer.len() as usize > MAX_BUFFER_SIZE {
615+
if self.tx_buffer.len() as usize > MAX_BUFFER_SIZE {
613616
error!("net: received too big frame from driver");
614617
self.metrics.tx_malformed_frames.inc();
615618
tx_queue
@@ -618,7 +621,10 @@ impl Net {
618621
continue;
619622
}
620623

621-
if !Self::rate_limiter_consume_op(&mut self.tx_rate_limiter, u64::from(buffer.len())) {
624+
if !Self::rate_limiter_consume_op(
625+
&mut self.tx_rate_limiter,
626+
u64::from(self.tx_buffer.len()),
627+
) {
622628
tx_queue.undo_pop();
623629
self.metrics.tx_rate_limiter_throttled.inc();
624630
break;
@@ -628,7 +634,7 @@ impl Net {
628634
self.mmds_ns.as_mut(),
629635
&mut self.tx_rate_limiter,
630636
&mut self.tx_frame_headers,
631-
&buffer,
637+
&self.tx_buffer,
632638
&mut self.tap,
633639
self.guest_mac,
634640
&self.metrics,
@@ -649,6 +655,8 @@ impl Net {
649655
self.metrics.no_tx_avail_buffer.inc();
650656
}
651657

658+
// Cleanup tx_buffer to ensure no two buffers point at the same memory
659+
self.tx_buffer.clear();
652660
self.try_signal_queue(NetQueue::Tx)?;
653661

654662
// An incoming frame for the MMDS may trigger the transmission of a new message.

src/vmm/src/devices/virtio/vsock/packet.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,10 @@ impl VsockPacket {
124124
/// - [`VsockError::DescChainTooShortForPacket`] if the contained vsock header describes a vsock
125125
/// packet whose length exceeds the descriptor chain's actual total buffer length.
126126
pub fn from_tx_virtq_head(chain: DescriptorChain) -> Result<Self, VsockError> {
127-
let buffer = IoVecBuffer::from_descriptor_chain(chain)?;
127+
// SAFETY: This descriptor chain is only loaded once
128+
// virtio requests are handled sequentially so no two IoVecBuffers
129+
// are live at the same time, meaning this has exclusive ownership over the memory
130+
let buffer = unsafe { IoVecBuffer::from_descriptor_chain(chain)? };
128131

129132
let mut hdr = VsockPacketHeader::default();
130133
match buffer.read_exact_volatile_at(hdr.as_mut_slice(), 0) {

0 commit comments

Comments
 (0)