mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
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:
committed by
Rob Bradford
parent
f5967cf287
commit
e17c0be127
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,8 @@ use virtio_bindings::virtio_net::{
|
||||
VIRTIO_NET_F_CSUM, VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_ECN,
|
||||
VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6, VIRTIO_NET_F_GUEST_UFO,
|
||||
VIRTIO_NET_F_HOST_ECN, VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_TSO6, VIRTIO_NET_F_HOST_UFO,
|
||||
VIRTIO_NET_F_MAC, VIRTIO_NET_F_MRG_RXBUF, VIRTIO_NET_F_MTU,
|
||||
VIRTIO_NET_F_MAC, VIRTIO_NET_F_MRG_RXBUF, VIRTIO_NET_F_MTU, VIRTIO_NET_F_STATUS,
|
||||
VIRTIO_NET_S_LINK_UP,
|
||||
};
|
||||
use virtio_bindings::virtio_ring::VIRTIO_RING_F_EVENT_IDX;
|
||||
use virtio_queue::QueueT;
|
||||
@@ -86,10 +87,10 @@ impl Net {
|
||||
) = if let Some(state) = state {
|
||||
info!("Restoring vhost-user-net {id}");
|
||||
|
||||
// The backend acknowledged features must not contain
|
||||
// VIRTIO_NET_F_MAC since we don't expect the backend
|
||||
// to handle it.
|
||||
let backend_acked_features = state.acked_features & !(1 << VIRTIO_NET_F_MAC);
|
||||
// The backend acknowledged features must not contain frontend-only
|
||||
// bits since we don't expect the backend to handle them.
|
||||
let backend_acked_features =
|
||||
state.acked_features & !((1 << VIRTIO_NET_F_MAC) | (1 << VIRTIO_NET_F_STATUS));
|
||||
|
||||
vu.set_protocol_features_vhost_user(
|
||||
backend_acked_features,
|
||||
@@ -177,9 +178,9 @@ impl Net {
|
||||
num_queues += 1;
|
||||
}
|
||||
|
||||
// Make sure the virtio feature to set the MAC address is exposed to
|
||||
// the guest, even if it hasn't been negotiated with the backend.
|
||||
acked_features |= 1 << VIRTIO_NET_F_MAC;
|
||||
// Make sure frontend-owned config-space features stay exposed to
|
||||
// the guest, even if they are not negotiated with the backend.
|
||||
acked_features |= (1 << VIRTIO_NET_F_MAC) | (1 << VIRTIO_NET_F_STATUS);
|
||||
|
||||
(
|
||||
acked_features,
|
||||
@@ -227,6 +228,21 @@ impl Net {
|
||||
fn state(&self) -> result::Result<State, MigratableError> {
|
||||
self.vu_common.state(self.config)
|
||||
}
|
||||
|
||||
/// Compute the guest-visible virtio-net status field.
|
||||
fn guest_visible_status(&self) -> u16 {
|
||||
let mut status = 0;
|
||||
|
||||
if self
|
||||
.vu_common
|
||||
.virtio_common
|
||||
.feature_acked(VIRTIO_NET_F_STATUS.into())
|
||||
{
|
||||
status |= VIRTIO_NET_S_LINK_UP as u16;
|
||||
}
|
||||
|
||||
status
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Net {
|
||||
@@ -257,7 +273,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 {
|
||||
@@ -321,10 +339,10 @@ impl VirtioDevice for Net {
|
||||
|
||||
let backend_req_handler: Option<FrontendReqHandler<BackendReqHandler>> = None;
|
||||
|
||||
// The backend acknowledged features must not contain VIRTIO_NET_F_MAC
|
||||
// since we don't expect the backend to handle it.
|
||||
let backend_acked_features =
|
||||
self.vu_common.virtio_common.acked_features & !(1 << VIRTIO_NET_F_MAC);
|
||||
// The backend acknowledged features must not contain frontend-only
|
||||
// bits since we don't expect the backend to handle them.
|
||||
let backend_acked_features = self.vu_common.virtio_common.acked_features
|
||||
& !((1 << VIRTIO_NET_F_MAC) | (1 << VIRTIO_NET_F_STATUS));
|
||||
|
||||
// Run a dedicated thread for handling potential reconnections with
|
||||
// the backend.
|
||||
@@ -416,3 +434,52 @@ impl Migratable for Net {
|
||||
self.vu_common.complete_migration()
|
||||
}
|
||||
}
|
||||
|
||||
#[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 {
|
||||
vu_common: VhostUserCommon {
|
||||
virtio_common: VirtioCommon {
|
||||
acked_features,
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
id: "test-vu-net".to_string(),
|
||||
config: VirtioNetConfig::default(),
|
||||
seccomp_action: SeccompAction::Allow,
|
||||
exit_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(),
|
||||
access_platform_enabled: false,
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user