virtio-devices: net: report link up in config status

Expose `VIRTIO_NET_S_LINK_UP` through the virtio-net config status field
when `VIRTIO_NET_F_STATUS` was negotiated.

This makes the guest-visible status bits reflect the device runtime
state and prepares the config status path used by later post-migration
announce handling.

On-behalf-of: SAP sebastian.eydam@sap.com
Signed-off-by: Sebastian Eydam <sebastian.eydam@cyberus-technology.de>
This commit is contained in:
Sebastian Eydam
2026-04-07 14:39:30 +02:00
committed by Rob Bradford
parent f5967cf287
commit e17c0be127
2 changed files with 144 additions and 14 deletions

View File

@@ -487,6 +487,7 @@ impl Net {
}
avail_features |= 1 << VIRTIO_NET_F_CTRL_VQ;
avail_features |= 1 << VIRTIO_NET_F_STATUS;
let queue_num = num_queues + 1;
let mut config = VirtioNetConfig::default();
@@ -639,6 +640,17 @@ impl Net {
}
}
/// Compute the guest-visible virtio-net status field.
fn guest_visible_status(&self) -> u16 {
let mut status = 0;
if self.common.feature_acked(VIRTIO_NET_F_STATUS.into()) {
status |= VIRTIO_NET_S_LINK_UP as u16;
}
status
}
#[cfg(fuzzing)]
pub fn wait_for_epoll_threads(&mut self) {
self.common.wait_for_epoll_threads();
@@ -663,7 +675,9 @@ impl VirtioDevice for Net {
}
fn read_config(&self, offset: u64, data: &mut [u8]) {
self.read_config_from_slice(self.config.as_slice(), offset, data);
let mut config = self.config;
config.status = self.guest_visible_status();
self.read_config_from_slice(config.as_slice(), offset, data);
}
fn activate(&mut self, context: ActivationContext) -> ActivateResult {
@@ -857,3 +871,52 @@ impl Snapshottable for Net {
}
impl Transportable for Net {}
impl Migratable for Net {}
#[cfg(test)]
mod unit_tests {
use std::mem::size_of;
use seccompiler::SeccompAction;
use virtio_bindings::virtio_net::{VIRTIO_NET_F_STATUS, VIRTIO_NET_S_LINK_UP};
use vmm_sys_util::eventfd::EventFd;
use super::*;
fn test_net(acked_features: u64) -> Net {
Net {
common: VirtioCommon {
acked_features,
..Default::default()
},
id: "test-net".to_string(),
taps: Vec::new(),
config: VirtioNetConfig::default(),
counters: NetCounters::default(),
seccomp_action: SeccompAction::Allow,
rate_limiter_config: None,
exit_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(),
device_status: Arc::new(AtomicU8::new(0)),
}
}
const STATUS_OFFSET: usize = std::mem::offset_of!(VirtioNetConfig, status);
fn read_status(device: &Net) -> u16 {
let mut data = vec![0; size_of::<VirtioNetConfig>()];
device.read_config(0, &mut data);
u16::from_le_bytes(
data[STATUS_OFFSET..STATUS_OFFSET + size_of::<u16>()]
.try_into()
.unwrap(),
)
}
#[test]
fn test_status_feature_reports_link_up() {
// The current implementation should always report "link up" if
// VIRTIO_NET_F_STATUS has been negotiated.
let net = test_net(1 << VIRTIO_NET_F_STATUS);
assert_eq!(read_status(&net), VIRTIO_NET_S_LINK_UP as u16);
}
}