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

Code before:
                    } else {
                        self.rx_deferred_frame = true;
                        break;
                    }
                }
                Err(NetError::IO(err)) => {
                    // The tap device is non-blocking, so any error aside from EAGAIN is
                    // unexpected.
                    match err.raw_os_error() {
                        Some(err) if err == EAGAIN => (),
                        _ => {
                            error!("Failed to read tap: {:?}", err);
                            self.metrics.tap_read_fails.inc();
                            return Err(DeviceError::FailedReadTap);
                        }
                    };
                    break;
                }
                Err(err) => {
                    error!("Spurious error in network RX: {:?}", err);
                }
            }
        }

        // We only notify guest if there is at least one packet processed successfully.
        if signal_queue {
            self.signal_used_queue(NetQueue::Rx)?;
        }
        Ok(signal_queue)
    }

    // Process the deferred frame first, then continue reading from tap.
    fn handle_deferred_frame(&mut self) -> Result<(), DeviceError> {
        if self.rate_limited_rx_single_frame() {
            self.rx_deferred_frame = false;
            // process_rx() was interrupted possibly before consuming all
            // packets in the tap; try continuing now.
            // we only signal queue if process_rx() did not already.
            if !self.process_rx()? {
                self.signal_used_queue(NetQueue::Rx)?;
            }
        }
        Ok(())
    }

    fn resume_rx(&mut self) -> Result<(), DeviceError> {
        if self.rx_deferred_frame {
            self.handle_deferred_frame()
        } else {
            Ok(())
        }

Review comment (suggestion):
We still only signal once, no matter how many descriptors we put into the used ring, right? So we're still not completely spec compliant:

> After the device writes a descriptor index into the used ring:
> – If flags is 1, the device SHOULD NOT send a notification.
> – If flags is 0, the device MUST send a notification

(from Section 2.6.7.2: https://docs.oasis-open.org/virtio/virtio/v1.1/csprd01/virtio-v1.1-csprd01.pdf)

Code after:
                    if !self.rate_limited_rx_single_frame() {
                        self.rx_deferred_frame = true;
                        break;
                    }
                }
                Err(NetError::IO(err)) => {
                    // The tap device is non-blocking, so any error aside from EAGAIN is
                    // unexpected.
                    match err.raw_os_error() {
                        Some(err) if err == EAGAIN => (),
                        _ => {
                            error!("Failed to read tap: {:?}", err);
                            self.metrics.tap_read_fails.inc();
                            return Err(DeviceError::FailedReadTap);
                        }
                    };
                    break;
                }
                Err(err) => {
                    error!("Spurious error in network RX: {:?}", err);
                }
            }
        }

        self.try_signal_queue(NetQueue::Rx)
    }

    // Process the deferred frame first, then continue reading from tap.
    fn handle_deferred_frame(&mut self) -> Result<(), DeviceError> {
        if self.rate_limited_rx_single_frame() {
            self.rx_deferred_frame = false;
            // process_rx() was interrupted possibly before consuming all
            // packets in the tap; try continuing now.
            return self.process_rx();
        }

        self.try_signal_queue(NetQueue::Rx)
    }

    fn resume_rx(&mut self) -> Result<(), DeviceError> {
        if self.rx_deferred_frame {
            self.handle_deferred_frame()
        } else {
            Ok(())
        }
    }

    fn process_tx(&mut self) -> Result<(), DeviceError> {
        // This is safe since we checked in the event handler that the device is activated.
        let mem = self.device_state.mem().unwrap();