Skip to content

Commit 10273ef

Browse files
Nikita Zakirovbchalios
Nikita Zakirov
authored andcommitted
fix(net): Apply only supported TAP offloading features
Currently, we assume that the guest virtio-net driver acknowledges all the offload features we offer to it and then subsequently set the corresponding TAP features. This might be the case for the Linux guest kernel drivers we are currently using, but it is not necessarily the case. FreeBSD for example, might try to NACK some of them in some cases: firecracker-microvm#3905. This commit, changes the Firecracker implementation of VirtIO device to only setup the TAP offload features that correspond to the ones that the guest driver acknowledged. Signed-off-by: Nikita Zakirov <[email protected]> Signed-off-by: Babis Chalios <[email protected]>
1 parent de48fd6 commit 10273ef

File tree

3 files changed

+74
-7
lines changed

3 files changed

+74
-7
lines changed

src/vmm/src/devices/virtio/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
1010
use std::any::Any;
1111

12+
use crate::devices::virtio::net::TapError;
13+
1214
pub mod balloon;
1315
pub mod block;
1416
pub mod device;
@@ -66,6 +68,8 @@ pub enum ActivateError {
6668
EventFd,
6769
/// Vhost user: {0}
6870
VhostUser(vhost_user::VhostUserError),
71+
/// Setting tap interface offload flags failed: {0}
72+
TapSetOffload(TapError),
6973
}
7074

7175
/// Trait that helps in upcasting an object to Any

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

Lines changed: 70 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -209,10 +209,6 @@ impl Net {
209209
) -> Result<Self, NetError> {
210210
let tap = Tap::open_named(tap_if_name).map_err(NetError::TapOpen)?;
211211

212-
// Set offload flags to match the virtio features below.
213-
tap.set_offload(gen::TUN_F_CSUM | gen::TUN_F_UFO | gen::TUN_F_TSO4 | gen::TUN_F_TSO6)
214-
.map_err(NetError::TapSetOffload)?;
215-
216212
let vnet_hdr_size = i32::try_from(vnet_hdr_len()).unwrap();
217213
tap.set_vnet_hdr_size(vnet_hdr_size)
218214
.map_err(NetError::TapSetVnetHdrSize)?;
@@ -658,6 +654,40 @@ impl Net {
658654
}
659655
}
660656

657+
/// Builds the offload features we will setup on the TAP device based on the features that the
658+
/// guest supports.
659+
fn build_tap_offload_features(guest_supported_features: u64) -> u32 {
660+
let add_if_supported =
661+
|tap_features: &mut u32, supported_features: u64, tap_flag: u32, virtio_flag: u32| {
662+
if supported_features & (1 << virtio_flag) != 0 {
663+
*tap_features |= tap_flag;
664+
}
665+
};
666+
667+
let mut tap_features: u32 = 0;
668+
669+
add_if_supported(
670+
&mut tap_features,
671+
guest_supported_features,
672+
gen::TUN_F_CSUM,
673+
VIRTIO_NET_F_CSUM,
674+
);
675+
add_if_supported(
676+
&mut tap_features,
677+
guest_supported_features,
678+
gen::TUN_F_UFO,
679+
VIRTIO_NET_F_GUEST_UFO,
680+
);
681+
add_if_supported(
682+
&mut tap_features,
683+
guest_supported_features,
684+
gen::TUN_F_TSO4,
685+
VIRTIO_NET_F_GUEST_TSO4,
686+
);
687+
688+
tap_features
689+
}
690+
661691
/// Updates the parameters for the rate limiters
662692
pub fn patch_rate_limiters(
663693
&mut self,
@@ -861,6 +891,11 @@ impl VirtioDevice for Net {
861891
}
862892
}
863893

894+
let supported_flags: u32 = Net::build_tap_offload_features(self.acked_features);
895+
self.tap
896+
.set_offload(supported_flags)
897+
.map_err(super::super::ActivateError::TapSetOffload)?;
898+
864899
if self.activate_evt.write(1).is_err() {
865900
self.metrics.activate_fails.inc();
866901
return Err(ActivateError::EventFd);
@@ -980,7 +1015,11 @@ pub mod tests {
9801015
| 1 << VIRTIO_NET_F_HOST_TSO4
9811016
| 1 << VIRTIO_NET_F_HOST_UFO
9821017
| 1 << VIRTIO_F_VERSION_1
983-
| 1 << VIRTIO_RING_F_EVENT_IDX;
1018+
| 1 << VIRTIO_RING_F_EVENT_IDX
1019+
| 1 << VIRTIO_NET_F_GUEST_TSO6
1020+
| 1 << VIRTIO_NET_F_HOST_TSO6
1021+
| 1 << VIRTIO_NET_F_GUEST_ECN
1022+
| 1 << VIRTIO_NET_F_HOST_ECN;
9841023

9851024
assert_eq!(
9861025
net.avail_features_by_page(0),
@@ -998,6 +1037,32 @@ pub mod tests {
9981037
assert_eq!(net.acked_features, features);
9991038
}
10001039

1040+
#[test]
1041+
// Test that `Net::build_tap_offload_features` creates the TAP offload features that we expect
1042+
// it to do, based on the available guest features
1043+
fn test_build_tap_offload_features_all() {
1044+
let supported_features =
1045+
1 << VIRTIO_NET_F_CSUM | 1 << VIRTIO_NET_F_GUEST_UFO | 1 << VIRTIO_NET_F_GUEST_TSO4;
1046+
let expected_tap_features = gen::TUN_F_CSUM | gen::TUN_F_UFO | gen::TUN_F_TSO4;
1047+
let supported_flags = Net::build_tap_offload_features(supported_features);
1048+
1049+
assert_eq!(supported_flags, expected_tap_features);
1050+
}
1051+
1052+
#[test]
1053+
// Same as before, however, using each supported feature one by one.
1054+
fn test_build_tap_offload_features_one_by_one() {
1055+
let features = [
1056+
(1 << VIRTIO_NET_F_CSUM, gen::TUN_F_CSUM),
1057+
(1 << VIRTIO_NET_F_GUEST_UFO, gen::TUN_F_UFO),
1058+
(1 << VIRTIO_NET_F_GUEST_TSO4, gen::TUN_F_TSO4),
1059+
];
1060+
for (virtio_flag, tap_flag) in features {
1061+
let supported_flags = Net::build_tap_offload_features(virtio_flag);
1062+
assert_eq!(supported_flags, tap_flag);
1063+
}
1064+
}
1065+
10011066
#[test]
10021067
fn test_virtio_device_read_config() {
10031068
let mut net = default_net();

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,6 @@ pub enum NetQueue {
4444
pub enum NetError {
4545
/// Open tap device failed: {0}
4646
TapOpen(TapError),
47-
/// Setting tap interface offload flags failed: {0}
48-
TapSetOffload(TapError),
4947
/// Setting vnet header size failed: {0}
5048
TapSetVnetHdrSize(TapError),
5149
/// EventFd error: {0}

0 commit comments

Comments
 (0)