Skip to content

Commit 0588bff

Browse files
committed
feat: better handling of packets
fixing ingress with a loop Signed-off-by: Egor Lazarchuk <[email protected]>
1 parent 3d23794 commit 0588bff

File tree

1 file changed

+77
-53
lines changed

1 file changed

+77
-53
lines changed

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

Lines changed: 77 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use log::{error, warn};
1717
use utils::eventfd::EventFd;
1818
use utils::net::mac::MacAddr;
1919
use utils::{u32_to_usize, u64_to_usize};
20-
use vm_memory::{GuestMemory, GuestMemoryError};
20+
use vm_memory::{GuestAddress, GuestMemory, GuestMemoryError};
2121

2222
use crate::devices::virtio::device::{DeviceState, IrqTrigger, IrqType, VirtioDevice};
2323
use crate::devices::virtio::gen::virtio_blk::VIRTIO_F_VERSION_1;
@@ -129,7 +129,10 @@ pub struct Net {
129129
pub(crate) rx_deferred_frame: bool,
130130

131131
rx_bytes_read: usize,
132+
rx_bytes_send: usize,
132133
rx_frame_buf: [u8; MAX_BUFFER_SIZE],
134+
rx_header_addr: Option<GuestAddress>,
135+
rx_descriptors_used: u16,
133136

134137
tx_frame_headers: [u8; frame_hdr_len()],
135138

@@ -192,7 +195,10 @@ impl Net {
192195
tx_rate_limiter,
193196
rx_deferred_frame: false,
194197
rx_bytes_read: 0,
198+
rx_bytes_send: 0,
195199
rx_frame_buf: [0u8; MAX_BUFFER_SIZE],
200+
rx_header_addr: None,
201+
rx_descriptors_used: 0,
196202
tx_frame_headers: [0u8; frame_hdr_len()],
197203
irq_trigger: IrqTrigger::new().map_err(NetError::EventFd)?,
198204
config_space,
@@ -336,31 +342,26 @@ impl Net {
336342
///
337343
/// # Errors
338344
///
339-
/// Returns an error if the descriptor chain is too short or
340-
/// an inappropriate (read only) descriptor is found in the chain
341-
fn write_to_descriptor_chain(
345+
/// Returns an error if an inappropriate (read only) descriptor is found in the chain
346+
fn write_to_descriptor_chain<'a>(
342347
mem: &GuestMemoryMmap,
343-
data: &[u8],
348+
data: &'a [u8],
344349
head: DescriptorChain,
345350
net_metrics: &NetDeviceMetrics,
346-
) -> Result<(), FrontendError> {
351+
) -> Result<(&'a [u8], u16), FrontendError> {
347352
let mut chunk = data;
348-
let header_addr = head.addr;
349353
let mut next_descriptor = Some(head);
350354

351-
let mut i: u16 = 1;
355+
let mut num_descriptors_used: u16 = 0;
352356
while let Some(descriptor) = &next_descriptor {
353357
if !descriptor.is_write_only() {
354358
return Err(FrontendError::ReadOnlyDescriptor);
355359
}
356360

357-
if i == 1 && u32_to_usize(descriptor.len) < vnet_hdr_len() {
358-
return Err(FrontendError::SmallerThanVnetHdr);
359-
}
360-
361361
let len = std::cmp::min(chunk.len(), descriptor.len as usize);
362362
match mem.write_slice(&chunk[..len], descriptor.addr) {
363363
Ok(()) => {
364+
num_descriptors_used += 1;
364365
net_metrics.rx_count.inc();
365366
chunk = &chunk[len..];
366367
}
@@ -375,65 +376,88 @@ impl Net {
375376

376377
// If chunk is empty we are done here.
377378
if chunk.is_empty() {
378-
// # Safety:
379-
// - header_addr is a valid memory location in guest memory
380-
// - length of the first descriptor is bigger than virtio_net_hdr_v1
381-
#[allow(clippy::transmute_ptr_to_ref)]
382-
let header: &mut virtio_net_hdr_v1 = unsafe {
383-
std::mem::transmute(
384-
mem.get_slice(header_addr, std::mem::size_of::<virtio_net_hdr_v1>())
385-
.unwrap()
386-
.ptr_guard()
387-
.as_ptr(),
388-
)
389-
};
390-
header.num_buffers = i;
391-
392379
let len = data.len() as u64;
393380
net_metrics.rx_bytes_count.add(len);
394381
net_metrics.rx_packets_count.inc();
395-
return Ok(());
382+
return Ok((chunk, num_descriptors_used));
396383
}
397384

398-
i += 1;
399385
next_descriptor = descriptor.next_descriptor();
400386
}
401387

402-
warn!("Receiving buffer is too small to hold frame of current size");
403-
Err(FrontendError::DescriptorChainTooSmall)
388+
Ok((chunk, num_descriptors_used))
404389
}
405390

406-
// Copies a single frame from `self.rx_frame_buf` into the guest.
391+
// Copies `self.rx_frame_buf` into the guest.
407392
fn do_write_frame_to_guest(&mut self) -> Result<(), FrontendError> {
408393
// This is safe since we checked in the event handler that the device is activated.
409394
let mem = self.device_state.mem().unwrap();
410395

411396
let queue = &mut self.queues[RX_INDEX];
412-
let head_descriptor = queue.pop_or_enable_notification(mem).ok_or_else(|| {
413-
self.metrics.no_rx_avail_buffer.inc();
414-
FrontendError::EmptyQueue
415-
})?;
416-
let head_index = head_descriptor.index;
417-
418-
match Self::write_to_descriptor_chain(
419-
mem,
420-
&self.rx_frame_buf[..self.rx_bytes_read],
421-
head_descriptor,
422-
&self.metrics,
423-
) {
424-
Ok(_) => {
425-
let len = u32::try_from(self.rx_bytes_read).unwrap();
426-
queue.add_used(mem, head_index, len).map_err(|err| {
427-
error!("Failed to add available descriptor {}: {}", head_index, err);
428-
FrontendError::AddUsed
429-
})?;
430-
Ok(())
397+
398+
let mut bytes = &self.rx_frame_buf[self.rx_bytes_send..self.rx_bytes_read];
399+
loop {
400+
let head_descriptor = queue.pop_or_enable_notification(mem).ok_or_else(|| {
401+
self.metrics.no_rx_avail_buffer.inc();
402+
FrontendError::EmptyQueue
403+
})?;
404+
let head_index = head_descriptor.index;
405+
406+
if self.rx_header_addr.is_none() {
407+
if u32_to_usize(head_descriptor.len) < vnet_hdr_len() {
408+
return Err(FrontendError::SmallerThanVnetHdr);
409+
}
410+
self.rx_header_addr = Some(head_descriptor.addr);
431411
}
432-
Err(e) => {
433-
self.metrics.rx_fails.inc();
434-
Err(e)
412+
413+
match Self::write_to_descriptor_chain(mem, bytes, head_descriptor, &self.metrics) {
414+
Ok((remainin_chunk, num_descriptors_used)) => {
415+
self.rx_bytes_send += self.rx_bytes_read - remainin_chunk.len();
416+
self.rx_descriptors_used += num_descriptors_used;
417+
let bytes_written =
418+
u32::try_from(self.rx_bytes_read - remainin_chunk.len()).unwrap();
419+
queue
420+
.add_used(mem, head_index, bytes_written)
421+
.map_err(|err| {
422+
error!("Failed to add available descriptor {}: {}", head_index, err);
423+
FrontendError::AddUsed
424+
})?;
425+
if remainin_chunk.is_empty() {
426+
// self.rx_header_addr is Some(_) if we in the
427+
// middle of processing a packet.
428+
let header_addr = self.rx_header_addr.unwrap();
429+
// # Safety:
430+
// - header_addr is a valid memory location in guest memory
431+
// - length of the first descriptor is bigger than virtio_net_hdr_v1
432+
#[allow(clippy::transmute_ptr_to_ref)]
433+
let header: &mut virtio_net_hdr_v1 = unsafe {
434+
std::mem::transmute(
435+
mem.get_slice(
436+
header_addr,
437+
std::mem::size_of::<virtio_net_hdr_v1>(),
438+
)
439+
.unwrap()
440+
.ptr_guard()
441+
.as_ptr(),
442+
)
443+
};
444+
header.num_buffers = self.rx_descriptors_used;
445+
446+
self.rx_bytes_send = 0;
447+
self.rx_header_addr = None;
448+
self.rx_descriptors_used = 0;
449+
break;
450+
} else {
451+
bytes = remainin_chunk;
452+
}
453+
}
454+
Err(e) => {
455+
self.metrics.rx_fails.inc();
456+
return Err(e);
457+
}
435458
}
436459
}
460+
Ok(())
437461
}
438462

439463
// Tries to detour the frame to MMDS and if MMDS doesn't accept it, sends it on the host TAP.

0 commit comments

Comments
 (0)