@@ -17,7 +17,7 @@ use log::{error, warn};
1717use utils:: eventfd:: EventFd ;
1818use utils:: net:: mac:: MacAddr ;
1919use utils:: { u32_to_usize, u64_to_usize} ;
20- use vm_memory:: { GuestMemory , GuestMemoryError } ;
20+ use vm_memory:: { GuestAddress , GuestMemory , GuestMemoryError } ;
2121
2222use crate :: devices:: virtio:: device:: { DeviceState , IrqTrigger , IrqType , VirtioDevice } ;
2323use 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