Skip to content

Commit bffc6a0

Browse files
committed
perf(virtio-net): Don't copy packets when num_buffers is 1
Currently, we would copy every single received packet from DeviceAlloc to a new Vec that is allocated with GlobalAlloc. If we use DeviceAlloc for the payload of the RxToken, we can avoid copying all packets for the special (and common) case of num_buffers = 1. While at it, I refactored the code to make it more readable and avoid allocating the intermediate Vec that would be collected into a flattened Vec<u8> anyways. Signed-off-by: Jens Reidel <[email protected]>
1 parent 1ca3169 commit bffc6a0

File tree

4 files changed

+28
-25
lines changed

4 files changed

+28
-25
lines changed

src/drivers/net/gem.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ impl NetworkDriver for GEMDriver {
340340
};
341341
trace!("BUFFER: {buffer:x?}");
342342
self.rx_buffer_consumed(index as usize);
343-
Some((RxToken::new(buffer.to_vec()), TxToken::new()))
343+
Some((RxToken::new(buffer.to_vec_in(DeviceAlloc)), TxToken::new()))
344344
}
345345
None => None,
346346
}

src/drivers/net/rtl8139.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#![allow(dead_code)]
44

55
use alloc::boxed::Box;
6+
use alloc::vec::Vec;
67
use core::mem;
78

89
use memory_addresses::VirtAddr;
@@ -17,6 +18,7 @@ use crate::drivers::error::DriverError;
1718
use crate::drivers::net::NetworkDriver;
1819
use crate::drivers::pci::PciDevice;
1920
use crate::executor::device::{RxToken, TxToken};
21+
use crate::mm::device_alloc::DeviceAlloc;
2022

2123
/// size of the receive buffer
2224
const RX_BUF_LEN: usize = 8192;
@@ -287,14 +289,18 @@ impl NetworkDriver for RTL8139Driver {
287289
let length = self.rx_peek_u16() - 4; // copy packet (but not the CRC)
288290
let pos = (self.rxpos + mem::size_of::<u16>()) % RX_BUF_LEN;
289291

292+
let mut vec_data = Vec::with_capacity_in(length as usize, DeviceAlloc);
293+
290294
// do we reach the end of the receive buffers?
291295
// in this case, we contact the two slices to one vec
292-
let vec_data = if pos + length as usize > RX_BUF_LEN {
296+
if pos + length as usize > RX_BUF_LEN {
293297
let first = &self.rxbuffer[pos..RX_BUF_LEN];
294298
let second = &self.rxbuffer[..length as usize - first.len()];
295-
[first, second].concat()
299+
300+
vec_data.extend_from_slice(first);
301+
vec_data.extend_from_slice(second);
296302
} else {
297-
(self.rxbuffer[pos..][..length.into()]).to_vec()
303+
vec_data.extend_from_slice(&self.rxbuffer[pos..][..length.into()]);
298304
};
299305

300306
self.consume_current_buffer();

src/drivers/net/virtio/mod.rs

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -305,13 +305,17 @@ impl NetworkDriver for VirtioNetDriver {
305305
}
306306

307307
fn receive_packet(&mut self) -> Option<(RxToken, TxToken)> {
308-
let mut buffer_tkn = self.recv_vqs.get_next()?;
309-
RxQueues::post_processing(&mut buffer_tkn)
310-
.inspect_err(|vnet_err| warn!("Post processing failed. Err: {vnet_err:?}"))
311-
.ok()?;
312-
let first_header = buffer_tkn.used_recv_buff.pop_front_downcast::<Hdr>()?;
313-
let first_packet = buffer_tkn.used_recv_buff.pop_front_vec()?;
314-
trace!("Header: {first_header:?}");
308+
let mut receive_single_packet = || {
309+
let mut buffer_tkn = self.recv_vqs.get_next()?;
310+
RxQueues::post_processing(&mut buffer_tkn)
311+
.inspect_err(|vnet_err| warn!("Post processing failed. Err: {vnet_err:?}"))
312+
.ok()?;
313+
let header = buffer_tkn.used_recv_buff.pop_front_downcast::<Hdr>()?;
314+
let packet = buffer_tkn.used_recv_buff.pop_front_vec()?;
315+
Some((header, packet))
316+
};
317+
318+
let (first_header, first_packet) = receive_single_packet()?;
315319

316320
// According to VIRTIO spec v1.2 sec. 5.1.6.3.2, "num_buffers will always be 1 if VIRTIO_NET_F_MRG_RXBUF is not negotiated."
317321
// Unfortunately, NVIDIA MLX5 does not comply with this requirement and we have to manually set the value to the correct one.
@@ -321,17 +325,11 @@ impl NetworkDriver for VirtioNetDriver {
321325
1
322326
};
323327

324-
let mut packets = Vec::with_capacity(num_buffers.into());
325-
packets.push(first_packet);
328+
let mut combined_packets = first_packet;
326329

327330
for _ in 1..num_buffers {
328-
let mut buffer_tkn = self.recv_vqs.get_next().unwrap();
329-
RxQueues::post_processing(&mut buffer_tkn)
330-
.inspect_err(|vnet_err| warn!("Post processing failed. Err: {vnet_err:?}"))
331-
.ok()?;
332-
let _header = buffer_tkn.used_recv_buff.pop_front_downcast::<Hdr>()?;
333-
let packet = buffer_tkn.used_recv_buff.pop_front_vec()?;
334-
packets.push(packet);
331+
let (_header, packet) = receive_single_packet()?;
332+
combined_packets.extend_from_slice(&packet);
335333
}
336334

337335
fill_queue(
@@ -340,9 +338,7 @@ impl NetworkDriver for VirtioNetDriver {
340338
self.recv_vqs.packet_size,
341339
);
342340

343-
let vec_data = packets.into_iter().flatten().collect();
344-
345-
Some((RxToken::new(vec_data), TxToken::new()))
341+
Some((RxToken::new(combined_packets), TxToken::new()))
346342
}
347343

348344
fn set_polling_mode(&mut self, value: bool) {

src/executor/device.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use crate::arch::kernel::mmio as hardware;
2323
use crate::drivers::net::NetworkDriver;
2424
#[cfg(feature = "pci")]
2525
use crate::drivers::pci as hardware;
26+
use crate::mm::device_alloc::DeviceAlloc;
2627

2728
/// Data type to determine the mac address
2829
#[derive(Debug, Clone)]
@@ -220,11 +221,11 @@ pub(crate) type RxHandle = usize;
220221

221222
#[doc(hidden)]
222223
pub(crate) struct RxToken {
223-
buffer: Vec<u8>,
224+
buffer: Vec<u8, DeviceAlloc>,
224225
}
225226

226227
impl RxToken {
227-
pub(crate) fn new(buffer: Vec<u8>) -> Self {
228+
pub(crate) fn new(buffer: Vec<u8, DeviceAlloc>) -> Self {
228229
Self { buffer }
229230
}
230231
}

0 commit comments

Comments
 (0)