diff --git a/fuzz/fuzz_targets/net.rs b/fuzz/fuzz_targets/net.rs index 99e322065..e77ac6e79 100644 --- a/fuzz/fuzz_targets/net.rs +++ b/fuzz/fuzz_targets/net.rs @@ -76,6 +76,9 @@ fuzz_target!(|bytes| { None, EventFd::new(EFD_NONBLOCK).unwrap(), None, + true, + true, + true, ) .unwrap(); diff --git a/virtio-devices/src/net.rs b/virtio-devices/src/net.rs index 2b2defbc6..07bf9d6fd 100644 --- a/virtio-devices/src/net.rs +++ b/virtio-devices/src/net.rs @@ -446,6 +446,9 @@ impl Net { rate_limiter_config: Option, exit_evt: EventFd, state: Option, + offload_tso: bool, + offload_ufo: bool, + offload_csum: bool, ) -> Result { assert!(!taps.is_empty()); @@ -462,25 +465,33 @@ impl Net { true, ) } else { - let mut avail_features = 1 << VIRTIO_NET_F_CSUM - | 1 << VIRTIO_NET_F_CTRL_GUEST_OFFLOADS - | 1 << VIRTIO_NET_F_GUEST_CSUM - | 1 << VIRTIO_NET_F_GUEST_ECN - | 1 << VIRTIO_NET_F_GUEST_TSO4 - | 1 << VIRTIO_NET_F_GUEST_TSO6 - | 1 << VIRTIO_NET_F_GUEST_UFO - | 1 << VIRTIO_NET_F_HOST_ECN - | 1 << VIRTIO_NET_F_HOST_TSO4 - | 1 << VIRTIO_NET_F_HOST_TSO6 - | 1 << VIRTIO_NET_F_HOST_UFO - | 1 << VIRTIO_NET_F_MTU - | 1 << VIRTIO_RING_F_EVENT_IDX - | 1 << VIRTIO_F_VERSION_1; + let mut avail_features = + 1 << VIRTIO_NET_F_MTU | 1 << VIRTIO_RING_F_EVENT_IDX | 1 << VIRTIO_F_VERSION_1; if iommu { avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM; } + // Configure TSO/UFO features when hardware checksum offload is enabled. + if offload_csum { + avail_features |= 1 << VIRTIO_NET_F_CSUM + | 1 << VIRTIO_NET_F_GUEST_CSUM + | 1 << VIRTIO_NET_F_CTRL_GUEST_OFFLOADS; + + if offload_tso { + avail_features |= 1 << VIRTIO_NET_F_HOST_ECN + | 1 << VIRTIO_NET_F_HOST_TSO4 + | 1 << VIRTIO_NET_F_HOST_TSO6 + | 1 << VIRTIO_NET_F_GUEST_ECN + | 1 << VIRTIO_NET_F_GUEST_TSO4 + | 1 << VIRTIO_NET_F_GUEST_TSO6; + } + + if offload_ufo { + avail_features |= 1 << VIRTIO_NET_F_HOST_UFO | 1 << VIRTIO_NET_F_GUEST_UFO; + } + } + avail_features |= 1 << VIRTIO_NET_F_CTRL_VQ; let queue_num = num_queues + 1; @@ -551,6 +562,9 @@ impl Net { rate_limiter_config: Option, exit_evt: EventFd, state: Option, + offload_tso: bool, + offload_ufo: bool, + offload_csum: bool, ) -> Result { let taps = open_tap( if_name, @@ -574,6 +588,9 @@ impl Net { rate_limiter_config, exit_evt, state, + offload_tso, + offload_ufo, + offload_csum, ) } @@ -589,6 +606,9 @@ impl Net { rate_limiter_config: Option, exit_evt: EventFd, state: Option, + offload_tso: bool, + offload_ufo: bool, + offload_csum: bool, ) -> Result { let mut taps: Vec = Vec::new(); let num_queue_pairs = fds.len(); @@ -621,6 +641,9 @@ impl Net { rate_limiter_config, exit_evt, state, + offload_tso, + offload_ufo, + offload_csum, ) } diff --git a/virtio-devices/src/vhost_user/net.rs b/virtio-devices/src/vhost_user/net.rs index af366e58b..80bf1dd46 100644 --- a/virtio-devices/src/vhost_user/net.rs +++ b/virtio-devices/src/vhost_user/net.rs @@ -78,6 +78,9 @@ impl Net { exit_evt: EventFd, iommu: bool, state: Option, + offload_tso: bool, + offload_ufo: bool, + offload_csum: bool, ) -> Result { let mut num_queues = vu_cfg.num_queues; @@ -120,17 +123,7 @@ impl Net { ) } else { // Filling device and vring features VMM supports. - let mut avail_features = 1 << VIRTIO_NET_F_CSUM - | 1 << VIRTIO_NET_F_GUEST_CSUM - | 1 << VIRTIO_NET_F_GUEST_TSO4 - | 1 << VIRTIO_NET_F_GUEST_TSO6 - | 1 << VIRTIO_NET_F_GUEST_ECN - | 1 << VIRTIO_NET_F_GUEST_UFO - | 1 << VIRTIO_NET_F_HOST_TSO4 - | 1 << VIRTIO_NET_F_HOST_TSO6 - | 1 << VIRTIO_NET_F_HOST_ECN - | 1 << VIRTIO_NET_F_HOST_UFO - | 1 << VIRTIO_NET_F_MRG_RXBUF + let mut avail_features = 1 << VIRTIO_NET_F_MRG_RXBUF | 1 << VIRTIO_NET_F_CTRL_VQ | 1 << VIRTIO_F_RING_EVENT_IDX | 1 << VIRTIO_F_VERSION_1 @@ -140,6 +133,24 @@ impl Net { avail_features |= 1u64 << VIRTIO_NET_F_MTU; } + // Configure TSO/UFO features when hardware checksum offload is enabled. + if offload_csum { + avail_features |= 1 << VIRTIO_NET_F_CSUM | 1 << VIRTIO_NET_F_GUEST_CSUM; + + if offload_tso { + avail_features |= 1 << VIRTIO_NET_F_HOST_ECN + | 1 << VIRTIO_NET_F_HOST_TSO4 + | 1 << VIRTIO_NET_F_HOST_TSO6 + | 1 << VIRTIO_NET_F_GUEST_ECN + | 1 << VIRTIO_NET_F_GUEST_TSO4 + | 1 << VIRTIO_NET_F_GUEST_TSO6; + } + + if offload_ufo { + avail_features |= 1 << VIRTIO_NET_F_HOST_UFO | 1 << VIRTIO_NET_F_GUEST_UFO; + } + } + let mut config = VirtioNetConfig::default(); build_net_config_space(&mut config, mac_addr, num_queues, mtu, &mut avail_features); diff --git a/vmm/src/config.rs b/vmm/src/config.rs index 7af624fc3..4e43abce6 100644 --- a/vmm/src/config.rs +++ b/vmm/src/config.rs @@ -134,6 +134,8 @@ pub enum ValidationError { VnetQueueFdMismatch, /// Using reserved fd VnetReservedFd, + /// Hardware checksum offload is disabled. + NoHardwareChecksumOffload, /// Hugepages not turned on HugePageSizeWithoutHugePages, /// Huge page size is not power of 2 @@ -205,6 +207,10 @@ impl fmt::Display for ValidationError { "Number of queues to virtio_net does not match the number of input FDs" ), VnetReservedFd => write!(f, "Reserved fd number (<= 2)"), + NoHardwareChecksumOffload => write!( + f, + "\"offload_tso\" and \"offload_ufo\" depend on \"offload_tso\"" + ), HugePageSizeWithoutHugePages => { write!(f, "Huge page size specified but huge pages not enabled") } @@ -1006,7 +1012,8 @@ impl NetConfig { num_queues=,queue_size=,id=,\ vhost_user=,socket=,vhost_mode=client|server,\ bw_size=,bw_one_time_burst=,bw_refill_time=,\ - ops_size=,ops_one_time_burst=,ops_refill_time=,pci_segment=\""; + ops_size=,ops_one_time_burst=,ops_refill_time=,pci_segment=\ + offload_tso=on|off,offload_ufo=on|off,offload_csum=on|off\""; pub fn parse(net: &str) -> Result { let mut parser = OptionParser::new(); @@ -1017,6 +1024,9 @@ impl NetConfig { .add("mask") .add("mac") .add("host_mac") + .add("offload_tso") + .add("offload_ufo") + .add("offload_csum") .add("mtu") .add("iommu") .add("queue_size") @@ -1049,6 +1059,21 @@ impl NetConfig { .map_err(Error::ParseNetwork)? .unwrap_or_else(default_netconfig_mac); let host_mac = parser.convert("host_mac").map_err(Error::ParseNetwork)?; + let offload_tso = parser + .convert::("offload_tso") + .map_err(Error::ParseNetwork)? + .unwrap_or(Toggle(true)) + .0; + let offload_ufo = parser + .convert::("offload_ufo") + .map_err(Error::ParseNetwork)? + .unwrap_or(Toggle(true)) + .0; + let offload_csum = parser + .convert::("offload_csum") + .map_err(Error::ParseNetwork)? + .unwrap_or(Toggle(true)) + .0; let mtu = parser.convert("mtu").map_err(Error::ParseNetwork)?; let iommu = parser .convert::("iommu") @@ -1150,6 +1175,9 @@ impl NetConfig { fds, rate_limiter_config, pci_segment, + offload_tso, + offload_ufo, + offload_csum, }; Ok(config) } @@ -1197,6 +1225,10 @@ impl NetConfig { } } + if !self.offload_csum && (self.offload_tso || self.offload_ufo) { + return Err(ValidationError::NoHardwareChecksumOffload); + } + Ok(()) } } @@ -2932,6 +2964,16 @@ mod tests { Err(ValidationError::VnetReservedFd) ); + let mut invalid_config = valid_config.clone(); + invalid_config.net = Some(vec![NetConfig { + offload_csum: false, + ..Default::default() + }]); + assert_eq!( + invalid_config.validate(), + Err(ValidationError::NoHardwareChecksumOffload) + ); + let mut invalid_config = valid_config.clone(); invalid_config.fs = Some(vec![FsConfig { ..Default::default() diff --git a/vmm/src/device_manager.rs b/vmm/src/device_manager.rs index cabd185a7..76ca37759 100644 --- a/vmm/src/device_manager.rs +++ b/vmm/src/device_manager.rs @@ -2334,6 +2334,9 @@ impl DeviceManager { .map(|s| s.to_versioned_state()) .transpose() .map_err(DeviceManagerError::RestoreGetState)?, + net_cfg.offload_tso, + net_cfg.offload_ufo, + net_cfg.offload_csum, ) { Ok(vun_device) => vun_device, Err(e) => { @@ -2371,6 +2374,9 @@ impl DeviceManager { .try_clone() .map_err(DeviceManagerError::EventFd)?, state, + net_cfg.offload_tso, + net_cfg.offload_ufo, + net_cfg.offload_csum, ) .map_err(DeviceManagerError::CreateVirtioNet)?, )) @@ -2389,6 +2395,9 @@ impl DeviceManager { .try_clone() .map_err(DeviceManagerError::EventFd)?, state, + net_cfg.offload_tso, + net_cfg.offload_ufo, + net_cfg.offload_csum, ) .map_err(DeviceManagerError::CreateVirtioNet)?, )) @@ -2411,6 +2420,9 @@ impl DeviceManager { .try_clone() .map_err(DeviceManagerError::EventFd)?, state, + net_cfg.offload_tso, + net_cfg.offload_ufo, + net_cfg.offload_csum, ) .map_err(DeviceManagerError::CreateVirtioNet)?, )) diff --git a/vmm/src/vm_config.rs b/vmm/src/vm_config.rs index 376f2057d..8b49489d2 100644 --- a/vmm/src/vm_config.rs +++ b/vmm/src/vm_config.rs @@ -291,6 +291,16 @@ pub struct NetConfig { pub rate_limiter_config: Option, #[serde(default)] pub pci_segment: u16, + #[serde(default = "default_netconfig_true")] + pub offload_tso: bool, + #[serde(default = "default_netconfig_true")] + pub offload_ufo: bool, + #[serde(default = "default_netconfig_true")] + pub offload_csum: bool, +} + +pub fn default_netconfig_true() -> bool { + true } pub fn default_netconfig_tap() -> Option { @@ -340,6 +350,9 @@ impl Default for NetConfig { fds: None, rate_limiter_config: None, pci_segment: 0, + offload_tso: true, + offload_ufo: true, + offload_csum: true, } } }