Merge pull request #13423 from justxuewei/net-dev-mgr

dragonball: Merge the three network device managers into one
This commit is contained in:
Alex Lyn
2026-07-22 21:13:51 +08:00
committed by GitHub
11 changed files with 1236 additions and 1853 deletions

View File

@@ -24,14 +24,15 @@ pub use self::machine_config::{VmConfigError, MAX_SUPPORTED_VCPUS};
feature = "vhost-net",
feature = "vhost-user-net"
))]
mod virtio_net;
#[cfg(feature = "vhost-user-net")]
pub use virtio_net::VhostUserConfig;
pub use crate::device_manager::net_dev_mgr::VhostUserConfig;
#[cfg(any(feature = "virtio-net", feature = "vhost-net"))]
pub use virtio_net::VirtioConfig;
pub use crate::device_manager::net_dev_mgr::VirtioConfig;
#[cfg(any(
feature = "virtio-net",
feature = "vhost-net",
feature = "vhost-user-net"
))]
pub use virtio_net::{Backend, NetworkInterfaceConfig, NetworkInterfaceUpdateConfig};
pub use crate::device_manager::net_dev_mgr::{
Backend, NetworkInterfaceConfig, NetworkInterfaceUpdateConfig,
};

View File

@@ -1,306 +0,0 @@
// Copyright (C) 2019-2023 Alibaba Cloud. All rights reserved.
// Copyright (C) 2019-2023 Ant Group. All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0
use core::panic;
use dbs_utils::net::MacAddr;
use serde::{Deserialize, Serialize};
#[cfg(feature = "virtio-net")]
use super::{VirtioNetDeviceConfigInfo, VirtioNetDeviceConfigUpdateInfo};
use crate::config_manager::RateLimiterConfigInfo;
#[cfg(feature = "vhost-net")]
use crate::device_manager::vhost_net_dev_mgr;
#[cfg(feature = "vhost-net")]
use crate::device_manager::vhost_net_dev_mgr::VhostNetDeviceConfigInfo;
#[cfg(feature = "vhost-user-net")]
use crate::device_manager::vhost_user_net_dev_mgr;
#[cfg(feature = "vhost-user-net")]
use crate::device_manager::vhost_user_net_dev_mgr::VhostUserNetDeviceConfigInfo;
#[cfg(feature = "virtio-net")]
use crate::device_manager::virtio_net_dev_mgr;
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(tag = "type", deny_unknown_fields)]
/// An enum to specify a backend of Virtio network
pub enum Backend {
#[serde(rename = "virtio")]
#[cfg(feature = "virtio-net")]
/// Virtio-net
Virtio(VirtioConfig),
#[serde(rename = "vhost")]
#[cfg(feature = "vhost-net")]
/// Vhost-net
Vhost(VirtioConfig),
#[serde(rename = "vhost-user")]
#[cfg(feature = "vhost-user-net")]
/// Vhost-user-net
VhostUser(VhostUserConfig),
}
impl Default for Backend {
#[allow(unreachable_code)]
fn default() -> Self {
#[cfg(feature = "virtio-net")]
return Self::Virtio(VirtioConfig::default());
#[cfg(feature = "vhost-net")]
return Self::Vhost(VirtioConfig::default());
panic!("no available default network backend")
}
}
/// Virtio network config, working for virtio-net and vhost-net.
#[cfg(any(feature = "virtio-net", feature = "vhost-net"))]
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
pub struct VirtioConfig {
/// ID of the guest network interface.
pub iface_id: String,
/// Host level path for the guest network interface.
pub host_dev_name: String,
/// Rate Limiter for received packages.
pub rx_rate_limiter: Option<RateLimiterConfigInfo>,
/// Rate Limiter for transmitted packages.
pub tx_rate_limiter: Option<RateLimiterConfigInfo>,
/// Allow duplicate mac
pub allow_duplicate_mac: bool,
}
/// Config for vhost-user-net device
#[cfg(feature = "vhost-user-net")]
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
pub struct VhostUserConfig {
/// Vhost-user socket path.
pub sock_path: String,
}
/// This struct represents the strongly typed equivalent of the json body from
/// net iface related requests.
/// This struct works with virtio-net devices and vhost-net devices.
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, Default)]
#[serde(deny_unknown_fields)]
pub struct NetworkInterfaceConfig {
/// Number of virtqueue pairs to use. (https://www.linux-kvm.org/page/Multiqueue)
pub num_queues: Option<usize>,
/// Size of each virtqueue.
pub queue_size: Option<u16>,
/// Net backend driver.
#[serde(default = "Backend::default")]
pub backend: Backend,
/// mac of the interface.
pub guest_mac: Option<MacAddr>,
/// Use shared irq
pub use_shared_irq: Option<bool>,
/// Use generic irq
pub use_generic_irq: Option<bool>,
}
#[cfg(feature = "virtio-net")]
impl From<NetworkInterfaceConfig> for VirtioNetDeviceConfigInfo {
fn from(value: NetworkInterfaceConfig) -> Self {
let self_ref = &value;
self_ref.into()
}
}
#[cfg(feature = "virtio-net")]
impl From<&NetworkInterfaceConfig> for VirtioNetDeviceConfigInfo {
fn from(value: &NetworkInterfaceConfig) -> Self {
let queue_size = value
.queue_size
.unwrap_or(virtio_net_dev_mgr::DEFAULT_QUEUE_SIZE);
// It is safe because we tested the type of config before.
#[allow(unreachable_patterns)]
let config = match &value.backend {
Backend::Virtio(config) => config,
_ => panic!("The virtio backend config is invalid: {:?}", value),
};
Self {
iface_id: config.iface_id.clone(),
host_dev_name: config.host_dev_name.clone(),
num_queues: virtio_net_dev_mgr::DEFAULT_NUM_QUEUES,
queue_size,
guest_mac: value.guest_mac,
rx_rate_limiter: config.rx_rate_limiter.clone(),
tx_rate_limiter: config.tx_rate_limiter.clone(),
allow_duplicate_mac: config.allow_duplicate_mac,
use_shared_irq: value.use_shared_irq,
use_generic_irq: value.use_generic_irq,
}
}
}
#[cfg(feature = "vhost-net")]
impl From<NetworkInterfaceConfig> for VhostNetDeviceConfigInfo {
fn from(value: NetworkInterfaceConfig) -> Self {
let self_ref = &value;
self_ref.into()
}
}
#[cfg(feature = "vhost-net")]
impl From<&NetworkInterfaceConfig> for VhostNetDeviceConfigInfo {
fn from(value: &NetworkInterfaceConfig) -> Self {
let num_queues = value
.num_queues
.map(|nq| {
if nq == 0 {
vhost_net_dev_mgr::DEFAULT_NUM_QUEUES
} else {
nq
}
})
.unwrap_or(vhost_net_dev_mgr::DEFAULT_NUM_QUEUES);
let queue_size = value
.queue_size
.map(|qs| {
if qs == 0 {
vhost_net_dev_mgr::DEFAULT_QUEUE_SIZE
} else {
qs
}
})
.unwrap_or(vhost_net_dev_mgr::DEFAULT_QUEUE_SIZE);
// It is safe because we tested the type of config before.
#[allow(unreachable_patterns)]
let config = match &value.backend {
Backend::Vhost(config) => config,
_ => panic!("The virtio backend config is invalid: {:?}", value),
};
Self {
iface_id: config.iface_id.clone(),
host_dev_name: config.host_dev_name.clone(),
num_queues,
queue_size,
guest_mac: value.guest_mac,
allow_duplicate_mac: config.allow_duplicate_mac,
use_shared_irq: value.use_shared_irq,
use_generic_irq: value.use_generic_irq,
}
}
}
#[cfg(feature = "vhost-user-net")]
impl From<NetworkInterfaceConfig> for VhostUserNetDeviceConfigInfo {
fn from(value: NetworkInterfaceConfig) -> Self {
let self_ref = &value;
self_ref.into()
}
}
#[cfg(feature = "vhost-user-net")]
impl From<&NetworkInterfaceConfig> for VhostUserNetDeviceConfigInfo {
fn from(value: &NetworkInterfaceConfig) -> Self {
let num_queues = value
.num_queues
.map(|nq| {
if nq == 0 {
vhost_user_net_dev_mgr::DEFAULT_NUM_QUEUES
} else {
nq
}
})
.unwrap_or(vhost_user_net_dev_mgr::DEFAULT_NUM_QUEUES);
let queue_size = value
.queue_size
.map(|qs| {
if qs == 0 {
vhost_user_net_dev_mgr::DEFAULT_QUEUE_SIZE
} else {
qs
}
})
.unwrap_or(vhost_user_net_dev_mgr::DEFAULT_QUEUE_SIZE);
// It is safe because we tested the type of config before.
#[allow(unreachable_patterns)]
let config = match &value.backend {
Backend::VhostUser(config) => config,
_ => panic!("The virtio backend config is invalid: {:?}", value),
};
Self {
sock_path: config.sock_path.clone(),
num_queues,
queue_size,
guest_mac: value.guest_mac,
use_shared_irq: value.use_shared_irq,
use_generic_irq: value.use_generic_irq,
}
}
}
/// The data fed into a network iface update request. Currently, only the RX and
/// TX rate limiters can be updated.
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, Default)]
#[serde(deny_unknown_fields)]
pub struct NetworkInterfaceUpdateConfig {
/// ID of the guest network interface.
pub iface_id: String,
/// New RX rate limiter config. Only provided data will be updated. I.e. if any optional data
/// is missing, it will not be nullified, but left unchanged.
pub rx_rate_limiter: Option<RateLimiterConfigInfo>,
/// New TX rate limiter config. Only provided data will be updated. I.e. if any optional data
/// is missing, it will not be nullified, but left unchanged.
pub tx_rate_limiter: Option<RateLimiterConfigInfo>,
}
#[cfg(feature = "virtio-net")]
impl From<NetworkInterfaceUpdateConfig> for VirtioNetDeviceConfigUpdateInfo {
fn from(value: NetworkInterfaceUpdateConfig) -> Self {
let self_ref = &value;
self_ref.into()
}
}
#[cfg(feature = "virtio-net")]
impl From<&NetworkInterfaceUpdateConfig> for VirtioNetDeviceConfigUpdateInfo {
fn from(value: &NetworkInterfaceUpdateConfig) -> Self {
Self {
iface_id: value.iface_id.clone(),
rx_rate_limiter: value.rx_rate_limiter.clone(),
tx_rate_limiter: value.tx_rate_limiter.clone(),
}
}
}
#[cfg(feature = "virtio-net")]
#[cfg(test)]
mod tests {
use dbs_utils::net::MacAddr;
use super::NetworkInterfaceConfig;
use crate::api::v1::Backend;
#[test]
fn test_network_interface_config() {
let json_str = r#"{
"num_queues": 4,
"queue_size": 512,
"backend": {
"type": "virtio",
"iface_id": "eth0",
"host_dev_name": "tap0",
"allow_duplicate_mac": true
},
"guest_mac": "81:87:1D:00:08:A9"
}"#;
let net_config: NetworkInterfaceConfig = serde_json::from_str(json_str).unwrap();
assert_eq!(net_config.num_queues, Some(4));
assert_eq!(net_config.queue_size, Some(512));
assert_eq!(
net_config.guest_mac,
Some(MacAddr::from_bytes(&[129, 135, 29, 0, 8, 169]).unwrap())
);
if let Backend::Virtio(config) = net_config.backend {
assert_eq!(config.iface_id, "eth0");
assert_eq!(config.host_dev_name, "tap0");
assert!(config.allow_duplicate_mac);
} else {
panic!("Unexpected backend type");
}
}
}

View File

@@ -37,21 +37,16 @@ pub use crate::device_manager::fs_dev_mgr::{
};
#[cfg(feature = "virtio-mem")]
pub use crate::device_manager::mem_dev_mgr::{MemDeviceConfigInfo, MemDeviceError};
#[cfg(any(
feature = "virtio-net",
feature = "vhost-net",
feature = "vhost-user-net"
))]
pub use crate::device_manager::net_dev_mgr::{
NetworkDeviceError, NetworkDeviceMgr, NetworkInterfaceConfig, NetworkInterfaceUpdateConfig,
};
#[cfg(feature = "host-device")]
use crate::device_manager::vfio_dev_mgr::{HostDeviceConfig, VfioDeviceError};
#[cfg(feature = "vhost-net")]
pub use crate::device_manager::vhost_net_dev_mgr::{
VhostNetDeviceConfigInfo, VhostNetDeviceError, VhostNetDeviceMgr,
};
#[cfg(feature = "vhost-user-net")]
use crate::device_manager::vhost_user_net_dev_mgr::{
VhostUserNetDeviceConfigInfo, VhostUserNetDeviceError, VhostUserNetDeviceMgr,
};
#[cfg(feature = "virtio-net")]
pub use crate::device_manager::virtio_net_dev_mgr::{
VirtioNetDeviceConfigInfo, VirtioNetDeviceConfigUpdateInfo, VirtioNetDeviceError,
VirtioNetDeviceMgr,
};
#[cfg(feature = "virtio-vsock")]
pub use crate::device_manager::vsock_dev_mgr::{VsockDeviceConfigInfo, VsockDeviceError};
#[cfg(feature = "host-device")]
@@ -109,20 +104,14 @@ pub enum VmmActionError {
#[error("virtio-blk device error: {0}")]
Block(#[source] BlockDeviceError),
#[cfg(feature = "virtio-net")]
/// Net device related errors.
#[error("virtio-net device error: {0}")]
VirtioNet(#[source] VirtioNetDeviceError),
#[cfg(feature = "vhost-net")]
#[error("vhost-net device error: {0:?}")]
/// Vhost-net device relared errors.
VhostNet(#[source] VhostNetDeviceError),
#[error("vhost-user-net device error: {0:?}")]
#[cfg(feature = "vhost-user-net")]
/// Vhost-user-net device relared errors.
VhostUserNet(#[source] VhostUserNetDeviceError),
#[cfg(any(
feature = "virtio-net",
feature = "vhost-net",
feature = "vhost-user-net"
))]
/// Network device related errors.
#[error("network device error: {0}")]
Network(#[source] NetworkDeviceError),
#[cfg(any(feature = "virtio-fs", feature = "vhost-user-fs"))]
/// The action `InsertFsDevice` failed either because of bad user input or an internal error.
@@ -233,7 +222,7 @@ pub enum VmmAction {
/// are the RX and TX rate limiters.
/// TODO: vhost-net rate limiters aren't implemented, see:
/// https://github.com/kata-containers/kata-containers/issues/8327
UpdateNetworkInterface(VirtioNetDeviceConfigUpdateInfo),
UpdateNetworkInterface(NetworkInterfaceUpdateConfig),
#[cfg(any(feature = "virtio-fs", feature = "vhost-user-fs"))]
/// Add a new shared fs device or update one that already exists using the
@@ -376,16 +365,9 @@ impl VmmService {
feature = "vhost-net",
feature = "vhost-user-net"
))]
VmmAction::InsertNetworkDevice(config) => match config.backend {
#[cfg(feature = "virtio-net")]
Backend::Virtio(_) => self.add_virtio_net_device(vmm, event_mgr, config.into()),
#[cfg(feature = "vhost-net")]
Backend::Vhost(_) => self.add_vhost_net_device(vmm, event_mgr, config.into()),
#[cfg(feature = "vhost-user-net")]
Backend::VhostUser(_) => {
self.add_vhost_user_net_device(vmm, event_mgr, config.into())
}
},
VmmAction::InsertNetworkDevice(config) => {
self.add_network_device(vmm, event_mgr, config)
}
#[cfg(feature = "virtio-net")]
VmmAction::UpdateNetworkInterface(netif_update) => {
self.update_net_rate_limiters(vmm, netif_update)
@@ -764,20 +746,24 @@ impl VmmService {
.map_err(VmmActionError::Block)
}
#[cfg(feature = "virtio-net")]
#[cfg(any(
feature = "virtio-net",
feature = "vhost-net",
feature = "vhost-user-net"
))]
#[instrument(skip(self, event_mgr))]
fn add_virtio_net_device(
fn add_network_device(
&mut self,
vmm: &mut Vmm,
event_mgr: &mut EventManager,
config: VirtioNetDeviceConfigInfo,
config: NetworkInterfaceConfig,
) -> VmmRequestResult {
let vm = vmm.get_vm_mut().ok_or(VmmActionError::InvalidVMID)?;
let ctx = vm
.create_device_op_context(Some(event_mgr.epoll_manager()))
.map_err(|e| {
if let StartMicroVmError::MicroVMAlreadyRunning = e {
VmmActionError::VirtioNet(VirtioNetDeviceError::UpdateNotAllowedPostBoot)
VmmActionError::Network(NetworkDeviceError::UpdateNotAllowedPostBoot)
} else if let StartMicroVmError::UpcallServerNotReady = e {
VmmActionError::UpcallServerNotReady
} else {
@@ -786,10 +772,10 @@ impl VmmService {
})?;
vm.device_manager_mut()
.virtio_net_manager
.net_manager
.insert_device(ctx, config)
.map(|_| VmmData::Empty)
.map_err(VmmActionError::VirtioNet)
.map_err(VmmActionError::Network)
}
#[cfg(feature = "virtio-net")]
@@ -797,61 +783,15 @@ impl VmmService {
fn update_net_rate_limiters(
&mut self,
vmm: &mut Vmm,
config: VirtioNetDeviceConfigUpdateInfo,
config: NetworkInterfaceUpdateConfig,
) -> VmmRequestResult {
let vm = vmm.get_vm_mut().ok_or(VmmActionError::InvalidVMID)?;
vm.device_manager_mut()
.virtio_net_manager
.net_manager
.update_device_ratelimiters(config)
.map(|_| VmmData::Empty)
.map_err(VmmActionError::VirtioNet)
}
#[cfg(feature = "vhost-net")]
fn add_vhost_net_device(
&mut self,
vmm: &mut Vmm,
event_mgr: &mut EventManager,
config: VhostNetDeviceConfigInfo,
) -> VmmRequestResult {
let vm = vmm.get_vm_mut().ok_or(VmmActionError::InvalidVMID)?;
let ctx = vm
.create_device_op_context(Some(event_mgr.epoll_manager()))
.map_err(|err| match err {
StartMicroVmError::MicroVMAlreadyRunning => {
VmmActionError::VhostNet(VhostNetDeviceError::UpdateNotAllowedPostBoot)
}
StartMicroVmError::UpcallServerNotReady => VmmActionError::UpcallServerNotReady,
_ => VmmActionError::StartMicroVm(err),
})?;
VhostNetDeviceMgr::insert_device(vm.device_manager_mut(), ctx, config)
.map(|_| VmmData::Empty)
.map_err(VmmActionError::VhostNet)
}
#[cfg(feature = "vhost-user-net")]
fn add_vhost_user_net_device(
&mut self,
vmm: &mut Vmm,
event_mgr: &mut EventManager,
config: VhostUserNetDeviceConfigInfo,
) -> VmmRequestResult {
let vm = vmm.get_vm_mut().ok_or(VmmActionError::InvalidVMID)?;
let ctx = vm
.create_device_op_context(Some(event_mgr.epoll_manager()))
.map_err(|err| {
if let StartMicroVmError::MicroVMAlreadyRunning = err {
VmmActionError::VhostUserNet(VhostUserNetDeviceError::UpdateNotAllowedPostBoot)
} else if let StartMicroVmError::UpcallServerNotReady = err {
VmmActionError::UpcallServerNotReady
} else {
VmmActionError::StartMicroVm(err)
}
})?;
VhostUserNetDeviceMgr::insert_device(vm.device_manager_mut(), ctx, config)
.map(|_| VmmData::Empty)
.map_err(VmmActionError::VhostUserNet)
.map_err(VmmActionError::Network)
}
#[cfg(any(feature = "virtio-fs", feature = "vhost-user-fs"))]
@@ -1772,10 +1712,20 @@ mod tests {
fn test_vmm_action_insert_network_device() {
skip_if_kvm_unaccessable!();
// A device must be named: the id identifies it for conflict detection
// and for later requests, so the manager refuses an unnamed one.
let named = || NetworkInterfaceConfig {
backend: Backend::Virtio(VirtioConfig {
iface_id: String::from("eth0"),
..Default::default()
}),
..Default::default()
};
let tests = &mut [
// hotplug unready
TestData::new(
VmmAction::InsertNetworkDevice(NetworkInterfaceConfig::default()),
VmmAction::InsertNetworkDevice(named()),
InstanceState::Running,
&|result| {
assert!(matches!(
@@ -1794,7 +1744,7 @@ mod tests {
),
// success
TestData::new(
VmmAction::InsertNetworkDevice(NetworkInterfaceConfig::default()),
VmmAction::InsertNetworkDevice(named()),
InstanceState::Uninitialized,
&|result| {
assert!(result.is_ok());
@@ -1815,7 +1765,7 @@ mod tests {
let tests = &mut [
// invalid id
TestData::new(
VmmAction::UpdateNetworkInterface(VirtioNetDeviceConfigUpdateInfo {
VmmAction::UpdateNetworkInterface(NetworkInterfaceUpdateConfig {
iface_id: String::from("1"),
rx_rate_limiter: None,
tx_rate_limiter: None,
@@ -1824,14 +1774,14 @@ mod tests {
&|result| {
assert!(matches!(
result,
Err(VmmActionError::VirtioNet(
VirtioNetDeviceError::InvalidIfaceId(_)
))
Err(VmmActionError::Network(NetworkDeviceError::InvalidIfaceId(
_
)))
));
let err_string = format!("{}", result.unwrap_err());
let expected_err = String::from(
"virtio-net device error: \
invalid virtio-net iface id '1'",
"network device error: \
invalid network iface id '1'",
);
assert_eq!(err_string, expected_err);
},

View File

@@ -89,11 +89,19 @@ pub mod blk_dev_mgr;
#[cfg(any(feature = "virtio-blk", feature = "vhost-user-blk"))]
use self::blk_dev_mgr::BlockDeviceMgr;
#[cfg(feature = "virtio-net")]
/// Device manager for virtio-net devices.
pub mod virtio_net_dev_mgr;
#[cfg(feature = "virtio-net")]
use self::virtio_net_dev_mgr::VirtioNetDeviceMgr;
#[cfg(any(
feature = "virtio-net",
feature = "vhost-net",
feature = "vhost-user-net"
))]
/// Device manager for all network device backends.
pub mod net_dev_mgr;
#[cfg(any(
feature = "virtio-net",
feature = "vhost-net",
feature = "vhost-user-net"
))]
use self::net_dev_mgr::NetworkDeviceMgr;
#[cfg(any(feature = "virtio-fs", feature = "vhost-user-fs"))]
/// virtio-block device manager
@@ -117,23 +125,12 @@ pub mod balloon_dev_mgr;
#[cfg(feature = "virtio-balloon")]
use self::balloon_dev_mgr::BalloonDeviceMgr;
#[cfg(feature = "vhost-net")]
/// Device manager for vhost-net devices.
pub mod vhost_net_dev_mgr;
#[cfg(feature = "vhost-net")]
use self::vhost_net_dev_mgr::VhostNetDeviceMgr;
#[cfg(feature = "host-device")]
/// Device manager for PCI/MMIO VFIO devices.
pub mod vfio_dev_mgr;
#[cfg(feature = "host-device")]
use self::vfio_dev_mgr::VfioDeviceMgr;
#[cfg(feature = "vhost-user-net")]
/// Device manager for vhost-user-net devices.
pub mod vhost_user_net_dev_mgr;
#[cfg(feature = "vhost-user-net")]
use self::vhost_user_net_dev_mgr::VhostUserNetDeviceMgr;
macro_rules! info(
($l:expr, $($args:tt)+) => {
slog::info!($l, $($args)+; slog::o!("subsystem" => "device_manager"))
@@ -656,8 +653,12 @@ pub struct DeviceManager {
// This is necessary because we want the root to always be mounted on /dev/vda.
pub(crate) block_manager: BlockDeviceMgr,
#[cfg(feature = "virtio-net")]
pub(crate) virtio_net_manager: VirtioNetDeviceMgr,
#[cfg(any(
feature = "virtio-net",
feature = "vhost-net",
feature = "vhost-user-net"
))]
pub(crate) net_manager: NetworkDeviceMgr,
#[cfg(any(feature = "virtio-fs", feature = "vhost-user-fs"))]
fs_manager: Arc<Mutex<FsDeviceMgr>>,
@@ -667,12 +668,6 @@ pub struct DeviceManager {
#[cfg(feature = "virtio-balloon")]
pub(crate) balloon_manager: BalloonDeviceMgr,
#[cfg(feature = "vhost-net")]
vhost_net_manager: VhostNetDeviceMgr,
#[cfg(feature = "vhost-user-net")]
vhost_user_net_manager: VhostUserNetDeviceMgr,
#[cfg(feature = "host-device")]
pub(crate) vfio_manager: Arc<Mutex<VfioDeviceMgr>>,
#[cfg(feature = "host-device")]
@@ -737,18 +732,18 @@ impl DeviceManager {
vsock_manager: VsockDeviceMgr::default(),
#[cfg(any(feature = "virtio-blk", feature = "vhost-user-blk"))]
block_manager: BlockDeviceMgr::default(),
#[cfg(feature = "virtio-net")]
virtio_net_manager: VirtioNetDeviceMgr::default(),
#[cfg(any(
feature = "virtio-net",
feature = "vhost-net",
feature = "vhost-user-net"
))]
net_manager: NetworkDeviceMgr::default(),
#[cfg(any(feature = "virtio-fs", feature = "vhost-user-fs"))]
fs_manager: Arc::new(Mutex::new(FsDeviceMgr::default())),
#[cfg(feature = "virtio-mem")]
mem_manager: MemDeviceMgr::default(),
#[cfg(feature = "virtio-balloon")]
balloon_manager: BalloonDeviceMgr::default(),
#[cfg(feature = "vhost-net")]
vhost_net_manager: VhostNetDeviceMgr::default(),
#[cfg(feature = "vhost-user-net")]
vhost_user_net_manager: VhostUserNetDeviceMgr::default(),
#[cfg(feature = "host-device")]
vfio_manager: Arc::new(Mutex::new(VfioDeviceMgr::new(
vm_fd,
@@ -919,10 +914,14 @@ impl DeviceManager {
.map_err(StartMicroVmError::FsDeviceError)?;
}
#[cfg(feature = "virtio-net")]
self.virtio_net_manager
#[cfg(any(
feature = "virtio-net",
feature = "vhost-net",
feature = "vhost-user-net"
))]
self.net_manager
.attach_devices(&mut ctx)
.map_err(StartMicroVmError::VirtioNetDeviceError)?;
.map_err(StartMicroVmError::NetworkDeviceError)?;
#[cfg(feature = "virtio-vsock")]
self.vsock_manager.attach_devices(&mut ctx)?;
@@ -932,16 +931,6 @@ impl DeviceManager {
.generate_kernel_boot_args(kernel_config)
.map_err(StartMicroVmError::DeviceManager)?;
#[cfg(feature = "vhost-net")]
self.vhost_net_manager
.attach_devices(&mut ctx)
.map_err(StartMicroVmError::VhostNetDeviceError)?;
#[cfg(feature = "vhost-user-net")]
self.vhost_user_net_manager
.attach_devices(&mut ctx)
.map_err(StartMicroVmError::VhostUserNetDeviceError)?;
#[cfg(feature = "host-device")]
{
// It is safe bacause we don't expect poison lock.
@@ -1005,14 +994,15 @@ impl DeviceManager {
// FIXME: To acquire the full abilities for gracefully removing
// virtio-net and virtio-vsock devices, updating dragonball-sandbox
// is required.
#[cfg(feature = "virtio-net")]
self.virtio_net_manager.remove_devices(&mut ctx)?;
#[cfg(any(
feature = "virtio-net",
feature = "vhost-net",
feature = "vhost-user-net"
))]
self.net_manager.remove_devices(&mut ctx)?;
#[cfg(feature = "virtio-vsock")]
self.vsock_manager.remove_devices(&mut ctx)?;
#[cfg(feature = "vhost-net")]
self.vhost_net_manager.remove_devices(&mut ctx)?;
Ok(())
}
}
@@ -1663,8 +1653,12 @@ mod tests {
block_manager: BlockDeviceMgr::default(),
#[cfg(any(feature = "virtio-fs", feature = "vhost-user-fs"))]
fs_manager: Arc::new(Mutex::new(FsDeviceMgr::default())),
#[cfg(feature = "virtio-net")]
virtio_net_manager: VirtioNetDeviceMgr::default(),
#[cfg(any(
feature = "virtio-net",
feature = "vhost-net",
feature = "vhost-user-net"
))]
net_manager: NetworkDeviceMgr::default(),
#[cfg(feature = "virtio-vsock")]
vsock_manager: VsockDeviceMgr::default(),
#[cfg(feature = "virtio-mem")]
@@ -1673,10 +1667,6 @@ mod tests {
balloon_manager: BalloonDeviceMgr::default(),
#[cfg(target_arch = "aarch64")]
mmio_device_info: HashMap::new(),
#[cfg(feature = "vhost-net")]
vhost_net_manager: VhostNetDeviceMgr::default(),
#[cfg(feature = "vhost-user-net")]
vhost_user_net_manager: VhostUserNetDeviceMgr::default(),
#[cfg(feature = "host-device")]
vfio_manager: Arc::new(Mutex::new(VfioDeviceMgr::new(
vm_fd,

File diff suppressed because it is too large Load Diff

View File

@@ -1,643 +0,0 @@
// Copyright (C) 2019-2023 Alibaba Cloud. All rights reserved.
// Copyright (C) 2019-2023 Ant Group. All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0
use std::result::Result;
use std::sync::Arc;
use dbs_utils::net::MacAddr;
use dbs_virtio_devices::vhost::vhost_kern::net::Net;
use dbs_virtio_devices::Error as VirtioError;
use serde::{Deserialize, Serialize};
use virtio_queue::QueueSync;
use super::{DeviceManager, DeviceMgrError, DeviceOpContext};
use crate::address_space_manager::{GuestAddressSpaceImpl, GuestRegionImpl};
use crate::config_manager::{ConfigItem, DeviceConfigInfos};
/// Default number of virtio queues, one rx/tx pair.
pub const DEFAULT_NUM_QUEUES: usize = 2;
/// Default size of virtio queues.
pub const DEFAULT_QUEUE_SIZE: u16 = 256;
// The flag of whether to use the shared irq.
const USE_SHARED_IRQ: bool = true;
// The flag of whether to use the generic irq.
const USE_GENERIC_IRQ: bool = true;
#[derive(Debug, thiserror::Error)]
/// Errors associated with vhost-net device operations
pub enum VhostNetDeviceError {
/// The Context Identifier is already in use.
#[error("the device id {0} already exists")]
DeviceIdAlreadyExist(String),
/// The MAC address is already in use.
#[error("the guest Mac address {0} is already in use")]
GuestMacAddressInUse(String),
/// The host device name is already in use.
#[error("the host device name {0} is already in use")]
HostDeviceNameInUse(String),
/// The update isn't allowed after booting the mircovm.
#[error("update operation is not allowed after booting")]
UpdateNotAllowedPostBoot,
/// Invalid queue number for vhost-net device.
#[error("invalid queue number {0} for vhost-net device")]
InvalidQueueNum(usize),
/// Failure from device manager.
#[error("failure in device manager operations: {0:?}")]
DeviceManager(#[source] DeviceMgrError),
/// Failure from virtio subsystem.
#[error("virtio error: {0:?}")]
Virtio(VirtioError),
/// Split this at some point.
/// Internal errors are due to resource exhaustion.
/// Users errors are due to invalid permissions.
#[error("cannot create a vhost-net device: {0}")]
CreateNetDevice(#[source] VirtioError),
/// Cannot initialize a MMIO Network Device or add a device to the MMIO Bus.
#[error("failure while registering vhost-net device: {0}")]
RegisterNetDevice(#[source] DeviceMgrError),
}
/// Configuration information for vhost net devices.
/// TODO: https://github.com/kata-containers/kata-containers/issues/8382.
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct VhostNetDeviceConfigInfo {
/// Id of the guest network interface.
pub iface_id: String,
/// Host level path for the guest network interface.
pub host_dev_name: String,
/// Number of virtqueues to use.
pub num_queues: usize,
/// Size of each virtqueue.
pub queue_size: u16,
/// Guest MAC address.
pub guest_mac: Option<MacAddr>,
/// allow duplicate mac
pub allow_duplicate_mac: bool,
/// Use shared irq
pub use_shared_irq: Option<bool>,
/// Use shared irq
pub use_generic_irq: Option<bool>,
}
impl VhostNetDeviceConfigInfo {
/// Returns a reference to the mac address. Its mac address is not
/// configured, it returns None.
pub fn guest_mac(&self) -> Option<&MacAddr> {
self.guest_mac.as_ref()
}
/// Returns rx and tx queue sizes, the length is num_queues, each value is
/// queue_size.
pub fn queue_sizes(&self) -> Vec<u16> {
let queue_size = if self.queue_size > 0 {
self.queue_size
} else {
DEFAULT_QUEUE_SIZE
};
let num_queues = if self.num_queues > 0 {
self.num_queues
} else {
DEFAULT_NUM_QUEUES
};
(0..num_queues).map(|_| queue_size).collect()
}
}
impl ConfigItem for VhostNetDeviceConfigInfo {
type Err = VhostNetDeviceError;
fn id(&self) -> &str {
&self.iface_id
}
fn check_conflicts(&self, other: &Self) -> Result<(), Self::Err> {
if self.iface_id == other.iface_id {
Err(VhostNetDeviceError::DeviceIdAlreadyExist(
self.iface_id.clone(),
))
} else if !other.allow_duplicate_mac
&& self.guest_mac.is_some()
&& self.guest_mac == other.guest_mac
{
Err(VhostNetDeviceError::GuestMacAddressInUse(
self.guest_mac.as_ref().unwrap().to_string(),
))
} else if self.host_dev_name == other.host_dev_name {
Err(VhostNetDeviceError::HostDeviceNameInUse(
self.host_dev_name.clone(),
))
} else {
Ok(())
}
}
}
/// Device manager to manage all vhost net devices.
pub struct VhostNetDeviceMgr {
info_list: DeviceConfigInfos<VhostNetDeviceConfigInfo>,
use_shared_irq: bool,
}
impl VhostNetDeviceMgr {
/// Create a `vhost_kern::net::Net` struct representing a vhost-net device.
fn create_device(
cfg: &VhostNetDeviceConfigInfo,
ctx: &mut DeviceOpContext,
) -> Result<Box<Net<GuestAddressSpaceImpl, QueueSync, GuestRegionImpl>>, VirtioError> {
slog::info!(
ctx.logger(),
"create a vhost-net device";
"subsystem" => "vhost_net_dev_mgr",
"id" => &cfg.iface_id,
"host_dev_name" => &cfg.host_dev_name,
);
let epoll_mgr = ctx.epoll_mgr.clone().ok_or(VirtioError::InvalidInput)?;
Ok(Box::new(Net::new(
cfg.host_dev_name.clone(),
cfg.guest_mac(),
Arc::new(cfg.queue_sizes()),
epoll_mgr,
)?))
}
/// Insert or update a vhost-net device into the device manager. If it is a
/// hotplug device, then it will be attached to the hypervisor.
pub fn insert_device(
device_mgr: &mut DeviceManager,
mut ctx: DeviceOpContext,
config: VhostNetDeviceConfigInfo,
) -> Result<(), VhostNetDeviceError> {
if !config.num_queues.is_multiple_of(2) {
return Err(VhostNetDeviceError::InvalidQueueNum(config.num_queues));
}
if !cfg!(feature = "hotplug") && ctx.is_hotplug {
return Err(VhostNetDeviceError::UpdateNotAllowedPostBoot);
}
slog::info!(
ctx.logger(),
"add vhost-net device configuration";
"subsystem" => "vhost_net_dev_mgr",
"id" => &config.iface_id,
"host_dev_name" => &config.host_dev_name,
);
let mgr = &mut device_mgr.vhost_net_manager;
let device_index = mgr.info_list.insert_or_update(&config)?;
// If it is a hotplug device, then it will be attached immediately.
if ctx.is_hotplug {
slog::info!(
ctx.logger(),
"attach vhost-net device";
"subsystem" => "vhost_net_dev_mgr",
"id" => &config.iface_id,
"host_dev_name" => &config.host_dev_name,
);
match Self::create_device(&config, &mut ctx) {
Ok(device) => {
let mmio_dev = DeviceManager::create_mmio_virtio_device(
device,
&mut ctx,
config.use_shared_irq.unwrap_or(mgr.use_shared_irq),
config.use_generic_irq.unwrap_or(USE_GENERIC_IRQ),
)
.map_err(VhostNetDeviceError::RegisterNetDevice)?;
ctx.insert_hotplug_mmio_device(&mmio_dev, None)
.map_err(VhostNetDeviceError::DeviceManager)?;
// live-upgrade need save/restore device from info.device.
mgr.info_list[device_index].set_device(mmio_dev);
}
Err(err) => {
mgr.info_list.remove(device_index);
return Err(VhostNetDeviceError::Virtio(err));
}
}
}
Ok(())
}
/// Attach all configured vhost-net device to the virtual machine instance.
pub fn attach_devices(&mut self, ctx: &mut DeviceOpContext) -> Result<(), VhostNetDeviceError> {
for info in self.info_list.iter_mut() {
slog::info!(
ctx.logger(),
"attach vhost-net device";
"subsystem" => "vhost_net_dev_mgr",
"id" => &info.config.iface_id,
"host_dev_name" => &info.config.host_dev_name,
);
let device = Self::create_device(&info.config, ctx)
.map_err(VhostNetDeviceError::CreateNetDevice)?;
let mmio_dev = DeviceManager::create_mmio_virtio_device(
device,
ctx,
info.config.use_shared_irq.unwrap_or(self.use_shared_irq),
info.config.use_generic_irq.unwrap_or(USE_GENERIC_IRQ),
)
.map_err(VhostNetDeviceError::RegisterNetDevice)?;
info.set_device(mmio_dev);
}
Ok(())
}
/// Remove all vhost-net devices.
pub fn remove_devices(&mut self, ctx: &mut DeviceOpContext) -> Result<(), DeviceMgrError> {
while let Some(mut info) = self.info_list.pop() {
slog::info!(
ctx.logger(),
"remove virtio-net device: {}",
info.config.iface_id
);
if let Some(device) = info.device.take() {
DeviceManager::destroy_mmio_device(device, ctx)?;
}
}
Ok(())
}
}
impl Default for VhostNetDeviceMgr {
fn default() -> Self {
Self {
info_list: DeviceConfigInfos::new(),
use_shared_irq: USE_SHARED_IRQ,
}
}
}
#[cfg(test)]
mod tests {
use dbs_utils::net::MacAddr;
use dbs_virtio_devices::Error as VirtioError;
use test_utils::skip_if_kvm_unaccessable;
use crate::{
device_manager::{
vhost_net_dev_mgr::{VhostNetDeviceConfigInfo, VhostNetDeviceError, VhostNetDeviceMgr},
DeviceManager, DeviceMgrError, DeviceOpContext,
},
test_utils::tests::create_vm_for_test,
vm::VmConfigInfo,
};
#[test]
fn test_create_vhost_net_device() {
skip_if_kvm_unaccessable!();
let vm = create_vm_for_test();
let mgr = DeviceManager::new_test_mgr();
let id_1 = String::from("id_1");
let host_dev_name_1 = String::from("dev1");
let guest_mac_1 = "01:23:45:67:89:0a";
let netif_1 = VhostNetDeviceConfigInfo {
iface_id: id_1,
host_dev_name: host_dev_name_1,
num_queues: 2,
queue_size: 128,
guest_mac: Some(MacAddr::parse_str(guest_mac_1).unwrap()),
allow_duplicate_mac: false,
use_shared_irq: None,
use_generic_irq: None,
};
// no epoll manager
let mut ctx = DeviceOpContext::new(
None,
&mgr,
None,
None,
false,
Some(VmConfigInfo::default()),
vm.shared_info().clone(),
);
assert!(VhostNetDeviceMgr::create_device(&netif_1, &mut ctx).is_err());
}
#[test]
fn test_attach_vhost_net_device() {
skip_if_kvm_unaccessable!();
// Init vm for test.
let mut vm = create_vm_for_test();
let device_op_ctx = DeviceOpContext::new(
Some(vm.epoll_manager().clone()),
vm.device_manager(),
Some(vm.vm_as().unwrap().clone()),
vm.vm_address_space().cloned(),
false,
Some(VmConfigInfo::default()),
vm.shared_info().clone(),
);
let id_1 = String::from("id_1");
let host_dev_name_1 = String::from("dev1");
let guest_mac_1 = "01:23:45:67:89:0a";
let netif_1 = VhostNetDeviceConfigInfo {
iface_id: id_1,
host_dev_name: host_dev_name_1,
num_queues: 2,
queue_size: 128,
guest_mac: Some(MacAddr::parse_str(guest_mac_1).unwrap()),
allow_duplicate_mac: false,
use_shared_irq: None,
use_generic_irq: None,
};
assert!(
VhostNetDeviceMgr::insert_device(vm.device_manager_mut(), device_op_ctx, netif_1)
.is_ok()
);
assert_eq!(vm.device_manager().vhost_net_manager.info_list.len(), 1);
let mut device_op_ctx = DeviceOpContext::new(
Some(vm.epoll_manager().clone()),
vm.device_manager(),
Some(vm.vm_as().unwrap().clone()),
vm.vm_address_space().cloned(),
false,
Some(VmConfigInfo::default()),
vm.shared_info().clone(),
);
assert!(vm
.device_manager_mut()
.vhost_net_manager
.attach_devices(&mut device_op_ctx)
.is_ok());
}
#[test]
fn test_insert_vhost_net_device() {
skip_if_kvm_unaccessable!();
let vm = create_vm_for_test();
let mut mgr = DeviceManager::new_test_mgr();
let id_1 = String::from("id_1");
let mut host_dev_name_1 = String::from("dev1");
let mut guest_mac_1 = "01:23:45:67:89:0a";
// Test create.
let mut netif_1 = VhostNetDeviceConfigInfo {
iface_id: id_1,
host_dev_name: host_dev_name_1,
num_queues: 2,
queue_size: 128,
guest_mac: Some(MacAddr::parse_str(guest_mac_1).unwrap()),
allow_duplicate_mac: false,
use_shared_irq: None,
use_generic_irq: None,
};
let ctx = DeviceOpContext::new(
None,
&mgr,
None,
None,
false,
Some(VmConfigInfo::default()),
vm.shared_info().clone(),
);
assert!(VhostNetDeviceMgr::insert_device(&mut mgr, ctx, netif_1.clone()).is_ok());
assert_eq!(mgr.vhost_net_manager.info_list.len(), 1);
// Test update mac address (this test does not modify the tap).
guest_mac_1 = "01:23:45:67:89:0b";
netif_1.guest_mac = Some(MacAddr::parse_str(guest_mac_1).unwrap());
let ctx = DeviceOpContext::new(
None,
&mgr,
None,
None,
false,
Some(VmConfigInfo::default()),
vm.shared_info().clone(),
);
assert!(VhostNetDeviceMgr::insert_device(&mut mgr, ctx, netif_1.clone()).is_ok());
assert_eq!(mgr.vhost_net_manager.info_list.len(), 1);
// Test update host_dev_name (the tap will be updated).
host_dev_name_1 = String::from("dev2");
netif_1.host_dev_name = host_dev_name_1;
let ctx = DeviceOpContext::new(
None,
&mgr,
None,
None,
false,
Some(VmConfigInfo::default()),
vm.shared_info().clone(),
);
assert!(VhostNetDeviceMgr::insert_device(&mut mgr, ctx, netif_1).is_ok());
assert_eq!(mgr.vhost_net_manager.info_list.len(), 1);
}
#[test]
fn test_vhost_net_insert_error_cases() {
skip_if_kvm_unaccessable!();
let vm = create_vm_for_test();
let mut mgr = DeviceManager::new_test_mgr();
let guest_mac_1 = "01:23:45:67:89:0a";
let guest_mac_2 = "11:45:45:67:89:0b";
let ctx = DeviceOpContext::new(
None,
&mgr,
None,
None,
false,
Some(VmConfigInfo::default()),
vm.shared_info().clone(),
);
// invalid queue num
let mut netif_1 = VhostNetDeviceConfigInfo {
iface_id: String::from("id_1"),
host_dev_name: String::from("dev_1"),
num_queues: 1,
queue_size: 128,
guest_mac: Some(MacAddr::parse_str(guest_mac_1).unwrap()),
allow_duplicate_mac: false,
use_shared_irq: None,
use_generic_irq: None,
};
let res = VhostNetDeviceMgr::insert_device(&mut mgr, ctx, netif_1.clone());
if let Err(VhostNetDeviceError::InvalidQueueNum(1)) = res {
assert_eq!(mgr.vhost_net_manager.info_list.len(), 0);
} else {
panic!();
}
// Adding the first valid network config.
netif_1.num_queues = 2;
let ctx = DeviceOpContext::new(
None,
&mgr,
None,
None,
false,
Some(VmConfigInfo::default()),
vm.shared_info().clone(),
);
assert!(VhostNetDeviceMgr::insert_device(&mut mgr, ctx, netif_1.clone()).is_ok());
assert_eq!(mgr.vhost_net_manager.info_list.len(), 1);
// Error Cases for CREATE
// Error Case: Add new network config with the same host_dev_name
netif_1.iface_id = String::from("id_2");
netif_1.guest_mac = Some(MacAddr::parse_str(guest_mac_2).unwrap());
let ctx = DeviceOpContext::new(
None,
&mgr,
None,
None,
false,
Some(VmConfigInfo::default()),
vm.shared_info().clone(),
);
let res = VhostNetDeviceMgr::insert_device(&mut mgr, ctx, netif_1.clone());
if let Err(VhostNetDeviceError::HostDeviceNameInUse(_)) = res {
assert_eq!(mgr.vhost_net_manager.info_list.len(), 1);
} else {
panic!();
}
// Error Cases for CREATE
// Error Case: Add new network config with the same guest_address
netif_1.iface_id = String::from("id_2");
netif_1.host_dev_name = String::from("dev_2");
netif_1.guest_mac = Some(MacAddr::parse_str(guest_mac_1).unwrap());
let ctx = DeviceOpContext::new(
None,
&mgr,
None,
None,
false,
Some(VmConfigInfo::default()),
vm.shared_info().clone(),
);
let res = VhostNetDeviceMgr::insert_device(&mut mgr, ctx, netif_1);
if let Err(VhostNetDeviceError::GuestMacAddressInUse(_)) = res {
assert_eq!(mgr.vhost_net_manager.info_list.len(), 1);
} else {
panic!();
}
// Adding the second valid network config.
let mut netif_2 = VhostNetDeviceConfigInfo {
iface_id: String::from("id_2"),
host_dev_name: String::from("dev_2"),
num_queues: 2,
queue_size: 128,
guest_mac: Some(MacAddr::parse_str(guest_mac_2).unwrap()),
allow_duplicate_mac: false,
use_shared_irq: None,
use_generic_irq: None,
};
let ctx = DeviceOpContext::new(
None,
&mgr,
None,
None,
false,
Some(VmConfigInfo::default()),
vm.shared_info().clone(),
);
assert!(VhostNetDeviceMgr::insert_device(&mut mgr, ctx, netif_2.clone()).is_ok());
assert_eq!(mgr.vhost_net_manager.info_list.len(), 2);
// Error Cases for UPDATE
// Error Case: update netif_2 network config with the same host_dev_name as netif_1
netif_2.host_dev_name = String::from("dev_1");
let ctx = DeviceOpContext::new(
None,
&mgr,
None,
None,
false,
Some(VmConfigInfo::default()),
vm.shared_info().clone(),
);
let res = VhostNetDeviceMgr::insert_device(&mut mgr, ctx, netif_2.clone());
if let Err(VhostNetDeviceError::HostDeviceNameInUse(_)) = res {
assert_eq!(mgr.vhost_net_manager.info_list.len(), 2);
} else {
panic!();
}
// Error Cases for UPDATE
// Error Case: update netif_2 network config with the same guest_address as netif_
netif_2.guest_mac = Some(MacAddr::parse_str(guest_mac_1).unwrap());
let ctx = DeviceOpContext::new(
None,
&mgr,
None,
None,
false,
Some(VmConfigInfo::default()),
vm.shared_info().clone(),
);
let res = VhostNetDeviceMgr::insert_device(&mut mgr, ctx, netif_2);
if let Err(VhostNetDeviceError::GuestMacAddressInUse(_)) = res {
assert_eq!(mgr.vhost_net_manager.info_list.len(), 2);
} else {
panic!();
}
// Adding the third valid network config with same mac.
let netif_3 = VhostNetDeviceConfigInfo {
iface_id: String::from("id_3"),
host_dev_name: String::from("dev_3"),
num_queues: 2,
queue_size: 128,
guest_mac: Some(MacAddr::parse_str(guest_mac_1).unwrap()),
allow_duplicate_mac: true,
use_shared_irq: None,
use_generic_irq: None,
};
let ctx = DeviceOpContext::new(
None,
&mgr,
None,
None,
false,
Some(VmConfigInfo::default()),
vm.shared_info().clone(),
);
assert!(VhostNetDeviceMgr::insert_device(&mut mgr, ctx, netif_3).is_ok());
assert_eq!(mgr.vhost_net_manager.info_list.len(), 3);
}
#[test]
fn test_vhost_net_error_display() {
let err = VhostNetDeviceError::InvalidQueueNum(0);
let _ = format!("{err}{err:?}");
let err = VhostNetDeviceError::DeviceManager(DeviceMgrError::GetDeviceResource);
let _ = format!("{err}{err:?}");
let err = VhostNetDeviceError::DeviceIdAlreadyExist(String::from("1"));
let _ = format!("{err}{err:?}");
let err = VhostNetDeviceError::GuestMacAddressInUse(String::from("1"));
let _ = format!("{err}{err:?}");
let err = VhostNetDeviceError::HostDeviceNameInUse(String::from("1"));
let _ = format!("{err}{err:?}");
let err = VhostNetDeviceError::Virtio(VirtioError::DescriptorChainTooShort);
let _ = format!("{err}{err:?}");
let err = VhostNetDeviceError::UpdateNotAllowedPostBoot;
let _ = format!("{err}{err:?}");
}
}

View File

@@ -1,329 +0,0 @@
// Copyright (C) 2019-2023 Alibaba Cloud. All rights reserved.
// Copyright (C) 2019-2023 Ant Group. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
use std::result::Result;
use std::sync::Arc;
use dbs_utils::net::MacAddr;
use dbs_virtio_devices::vhost::vhost_user::net::VhostUserNet;
use dbs_virtio_devices::Error as VirtioError;
use serde::{Deserialize, Serialize};
use super::{DeviceManager, DeviceMgrError, DeviceOpContext};
use crate::address_space_manager::GuestAddressSpaceImpl;
use crate::config_manager::{ConfigItem, DeviceConfigInfos};
/// Default number of virtio queues, one rx/tx pair.
pub const DEFAULT_NUM_QUEUES: usize = 2;
/// Default size of virtio queues.
pub const DEFAULT_QUEUE_SIZE: u16 = 256;
// The flag of whether to use the shared irq.
const USE_SHARED_IRQ: bool = true;
// The flag of whether to use the generic irq.
const USE_GENERIC_IRQ: bool = false;
/// Errors associated with vhost user net devices.
#[derive(Debug, thiserror::Error)]
pub enum VhostUserNetDeviceError {
/// The virtual machine instance ID is invalid.
#[error("the virtual machine instance ID is invalid")]
InvalidVmId,
/// Internal error. Invalid queue number configuration for vhost_user_net device.
#[error("invalid queue number {0} for vhost_user_net device")]
InvalidQueueNum(usize),
/// Failure from device manager,
#[error("failure in device manager operations: {0:?}")]
DeviceManager(DeviceMgrError),
/// Duplicated Unix domain socket path for vhost_user_net device.
#[error("duplicated Unix domain socket path {0} for vhost_user_net device")]
DuplicatedUdsPath(String),
/// Failure from Virtio subsystem.
#[error(transparent)]
Virtio(VirtioError),
/// The update is not allowed after booting the microvm.
#[error("update operation is not allowed after boot")]
UpdateNotAllowedPostBoot,
/// Split this at some point.
/// Internal errors are due to resource exhaustion.
/// Users errors are due to invalid permissions.
#[error("cannot create a vhost-user-net device: {0:?}")]
CreateNetDevice(VirtioError),
/// Cannot initialize a MMIO Network Device or add a device to the MMIO Bus.
#[error("failure while registering network device: {0:?}")]
RegisterNetDevice(DeviceMgrError),
}
/// Configuration information for vhost user net devices.
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
pub struct VhostUserNetDeviceConfigInfo {
/// vhost-user socket path.
pub sock_path: String,
/// Number of virtqueues to use.
pub num_queues: usize,
/// Size of each virtqueue.
pub queue_size: u16,
/// mac of the interface.
pub guest_mac: Option<MacAddr>,
/// Use shared irq
pub use_shared_irq: Option<bool>,
/// Use generic irq
pub use_generic_irq: Option<bool>,
}
impl VhostUserNetDeviceConfigInfo {
/// Returns a reference to the mac address. If the mac address is not configured, it
/// returns None.
pub fn guest_mac(&self) -> Option<&MacAddr> {
self.guest_mac.as_ref()
}
///Rx and Tx queue and max queue sizes
pub fn queue_sizes(&self) -> Vec<u16> {
let mut queue_size = self.queue_size;
if queue_size == 0 {
queue_size = DEFAULT_QUEUE_SIZE;
}
let num_queues = if self.num_queues > 0 {
self.num_queues
} else {
DEFAULT_NUM_QUEUES
};
(0..num_queues).map(|_| queue_size).collect::<Vec<u16>>()
}
}
impl ConfigItem for VhostUserNetDeviceConfigInfo {
type Err = VhostUserNetDeviceError;
fn check_conflicts(&self, _other: &Self) -> std::result::Result<(), Self::Err> {
Ok(())
}
fn id(&self) -> &str {
&self.sock_path
}
}
/// Device manager to manage all vhost user net devices.
pub struct VhostUserNetDeviceMgr {
pub(crate) configs: DeviceConfigInfos<VhostUserNetDeviceConfigInfo>,
}
impl VhostUserNetDeviceMgr {
fn create_device(
cfg: &VhostUserNetDeviceConfigInfo,
ctx: &mut DeviceOpContext,
) -> Result<Box<VhostUserNet<GuestAddressSpaceImpl>>, VirtioError> {
match ctx.epoll_mgr.as_ref() {
Some(epoll_mgr) => Ok(Box::new(VhostUserNet::new_server(
&cfg.sock_path,
cfg.guest_mac(),
Arc::new(cfg.queue_sizes()),
epoll_mgr.clone(),
)?)),
None => Err(VirtioError::InvalidInput),
}
}
/// Insert or update a vhost user net device into the manager.
pub fn insert_device(
device_mgr: &mut DeviceManager,
mut ctx: DeviceOpContext,
config: VhostUserNetDeviceConfigInfo,
) -> Result<(), VhostUserNetDeviceError> {
// Validate device configuration first.
if !config.num_queues.is_multiple_of(2) {
return Err(VhostUserNetDeviceError::InvalidQueueNum(config.num_queues));
}
if !cfg!(feature = "hotplug") && ctx.is_hotplug {
return Err(VhostUserNetDeviceError::UpdateNotAllowedPostBoot);
}
slog::info!(
ctx.logger(),
"add vhost-user-net device configuration";
"subsystem" => "vhost_net_dev_mgr",
"sock_path" => &config.sock_path,
);
let device_index = device_mgr
.vhost_user_net_manager
.configs
.insert_or_update(&config)?;
if ctx.is_hotplug {
slog::info!(
ctx.logger(),
"attach virtio-net device";
"subsystem" => "vhost_net_dev_mgr",
"sock_path" => &config.sock_path,
);
match Self::create_device(&config, &mut ctx) {
Ok(device) => {
let dev = DeviceManager::create_mmio_virtio_device(
device,
&mut ctx,
config.use_shared_irq.unwrap_or(USE_SHARED_IRQ),
config.use_generic_irq.unwrap_or(USE_GENERIC_IRQ),
)
.map_err(VhostUserNetDeviceError::DeviceManager)?;
ctx.insert_hotplug_mmio_device(&dev, None)
.map_err(VhostUserNetDeviceError::DeviceManager)?;
}
Err(err) => {
device_mgr
.vhost_user_net_manager
.configs
.remove(device_index);
return Err(VhostUserNetDeviceError::Virtio(err));
}
}
}
Ok(())
}
/// Attach all configured vhost user net device to the virtual machine
/// instance.
pub fn attach_devices(
&mut self,
ctx: &mut DeviceOpContext,
) -> Result<(), VhostUserNetDeviceError> {
for info in self.configs.iter() {
slog::info!(
ctx.logger(),
"attach vhost-user-net device";
"subsystem" => "vhost_net_dev_mgr",
"sock_path" => &info.config.sock_path,
);
let device = Self::create_device(&info.config, ctx)
.map_err(VhostUserNetDeviceError::CreateNetDevice)?;
DeviceManager::create_mmio_virtio_device(
device,
ctx,
info.config.use_shared_irq.unwrap_or(USE_SHARED_IRQ),
info.config.use_generic_irq.unwrap_or(USE_GENERIC_IRQ),
)
.map_err(VhostUserNetDeviceError::RegisterNetDevice)?;
}
Ok(())
}
}
impl Default for VhostUserNetDeviceMgr {
/// Create a new vhost user net device manager.
fn default() -> Self {
VhostUserNetDeviceMgr {
configs: DeviceConfigInfos::<VhostUserNetDeviceConfigInfo>::new(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::tests::create_vm_for_test;
use test_utils::skip_if_kvm_unaccessable;
#[test]
fn test_create_vhost_user_net_device() {
skip_if_kvm_unaccessable!();
let vm = create_vm_for_test();
let mgr = DeviceManager::new_test_mgr();
let sock_1 = String::from("id_1");
let guest_mac_1 = "01:23:45:67:89:0a";
let netif_1 = VhostUserNetDeviceConfigInfo {
sock_path: sock_1,
num_queues: 2,
queue_size: 128,
guest_mac: Some(MacAddr::parse_str(guest_mac_1).unwrap()),
use_shared_irq: None,
use_generic_irq: None,
};
// no epoll manager
let mut ctx = DeviceOpContext::new(
None,
&mgr,
None,
None,
false,
Some(vm.vm_config().clone()),
vm.shared_info().clone(),
);
assert!(VhostUserNetDeviceMgr::create_device(&netif_1, &mut ctx).is_err());
}
#[test]
fn test_insert_vhost_user_net_device() {
skip_if_kvm_unaccessable!();
let vm = create_vm_for_test();
let mut mgr = DeviceManager::new_test_mgr();
let sock_1 = String::from("id_1");
let guest_mac_1 = "01:23:45:67:89:0a";
// Test create.
let netif_1 = VhostUserNetDeviceConfigInfo {
sock_path: sock_1,
num_queues: 2,
queue_size: 128,
guest_mac: Some(MacAddr::parse_str(guest_mac_1).unwrap()),
use_shared_irq: None,
use_generic_irq: None,
};
let ctx = DeviceOpContext::new(
None,
&mgr,
None,
None,
false,
Some(vm.vm_config().clone()),
vm.shared_info().clone(),
);
assert!(VhostUserNetDeviceMgr::insert_device(&mut mgr, ctx, netif_1).is_ok());
assert_eq!(mgr.vhost_user_net_manager.configs.len(), 1);
}
#[test]
fn test_vhost_user_net_insert_error_cases() {
skip_if_kvm_unaccessable!();
let vm = create_vm_for_test();
let mut mgr = DeviceManager::new_test_mgr();
let sock_1 = String::from("id_1");
let guest_mac_1 = "01:23:45:67:89:0a";
// invalid queue num.
let netif_1 = VhostUserNetDeviceConfigInfo {
sock_path: sock_1,
num_queues: 1,
queue_size: 128,
guest_mac: Some(MacAddr::parse_str(guest_mac_1).unwrap()),
use_shared_irq: None,
use_generic_irq: None,
};
let ctx = DeviceOpContext::new(
None,
&mgr,
None,
None,
false,
Some(vm.vm_config().clone()),
vm.shared_info().clone(),
);
let res = VhostUserNetDeviceMgr::insert_device(&mut mgr, ctx, netif_1);
if let Err(VhostUserNetDeviceError::InvalidQueueNum(1)) = res {
assert_eq!(mgr.vhost_user_net_manager.configs.len(), 0);
} else {
panic!()
}
}
#[test]
fn test_vhost_user_net_error_display() {
let err = VhostUserNetDeviceError::InvalidVmId;
let _ = format!("{err}{err:?}");
let err = VhostUserNetDeviceError::InvalidQueueNum(0);
let _ = format!("{err}{err:?}");
let err = VhostUserNetDeviceError::DeviceManager(DeviceMgrError::GetDeviceResource);
let _ = format!("{err}{err:?}");
let err = VhostUserNetDeviceError::DuplicatedUdsPath(String::from("1"));
let _ = format!("{err}{err:?}");
let err = VhostUserNetDeviceError::Virtio(VirtioError::DescriptorChainTooShort);
let _ = format!("{err}{err:?}");
let err = VhostUserNetDeviceError::UpdateNotAllowedPostBoot;
let _ = format!("{err}{err:?}");
}
}

View File

@@ -1,400 +0,0 @@
// Copyright 2020-2022 Alibaba, Inc. or its affiliates. All Rights Reserved.
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the THIRD-PARTY file.
use std::convert::TryInto;
use std::sync::Arc;
use dbs_utils::net::{MacAddr, Tap, TapError};
use dbs_utils::rate_limiter::BucketUpdate;
use dbs_virtio_devices as virtio;
use dbs_virtio_devices::net::Net;
use dbs_virtio_devices::Error as VirtioError;
use serde_derive::{Deserialize, Serialize};
use crate::address_space_manager::GuestAddressSpaceImpl;
use crate::config_manager::{
ConfigItem, DeviceConfigInfo, DeviceConfigInfos, RateLimiterConfigInfo,
};
use crate::device_manager::{DeviceManager, DeviceMgrError, DeviceOpContext};
use crate::get_bucket_update;
use super::DbsMmioV2Device;
/// Default number of virtio queues, one rx/tx pair.
pub const DEFAULT_NUM_QUEUES: usize = 2;
/// Default size of virtio queues.
pub const DEFAULT_QUEUE_SIZE: u16 = 256;
// The flag of whether to use the shared irq.
const USE_SHARED_IRQ: bool = true;
// The flag of whether to use the generic irq.
const USE_GENERIC_IRQ: bool = true;
/// Errors associated with virtio net device operations.
#[derive(Debug, thiserror::Error)]
pub enum VirtioNetDeviceError {
/// The virtual machine instance ID is invalid.
#[error("the virtual machine instance ID is invalid")]
InvalidVMID,
/// The iface ID is invalid.
#[error("invalid virtio-net iface id '{0}'")]
InvalidIfaceId(String),
/// Invalid queue number configuration for virtio_net device.
#[error("invalid queue number {0} for virtio-net device")]
InvalidQueueNum(usize),
/// Failure from device manager,
#[error("failure in device manager operations, {0}")]
DeviceManager(#[source] DeviceMgrError),
/// The Context Identifier is already in use.
#[error("the device ID {0} already exists")]
DeviceIDAlreadyExist(String),
/// The MAC address is already in use.
#[error("the guest MAC address {0} is already in use")]
GuestMacAddressInUse(String),
/// The host device name is already in use.
#[error("the host device name {0} is already in use")]
HostDeviceNameInUse(String),
/// Cannot open/create tap device.
#[error("cannot open TAP device")]
OpenTap(#[source] TapError),
/// Failure from virtio subsystem.
#[error(transparent)]
Virtio(VirtioError),
/// Failed to send patch message to net epoll handler.
#[error("could not send patch message to the net epoll handler")]
NetEpollHanderSendFail,
/// The update is not allowed after booting the microvm.
#[error("update operation is not allowed after boot")]
UpdateNotAllowedPostBoot,
/// Split this at some point.
/// Internal errors are due to resource exhaustion.
/// Users errors are due to invalid permissions.
#[error("cannot create network device: {0}")]
CreateNetDevice(#[source] VirtioError),
/// Cannot initialize a MMIO Network Device or add a device to the MMIO Bus.
#[error("failure while registering network device: {0}")]
RegisterNetDevice(#[source] DeviceMgrError),
}
/// Configuration information for virtio net devices.
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
pub struct VirtioNetDeviceConfigUpdateInfo {
/// ID of the guest network interface.
pub iface_id: String,
/// Rate Limiter for received packages.
pub rx_rate_limiter: Option<RateLimiterConfigInfo>,
/// Rate Limiter for transmitted packages.
pub tx_rate_limiter: Option<RateLimiterConfigInfo>,
}
impl VirtioNetDeviceConfigUpdateInfo {
/// Provides a `BucketUpdate` description for the RX bandwidth rate limiter.
pub fn rx_bytes(&self) -> BucketUpdate {
get_bucket_update!(self, rx_rate_limiter, bandwidth)
}
/// Provides a `BucketUpdate` description for the RX ops rate limiter.
pub fn rx_ops(&self) -> BucketUpdate {
get_bucket_update!(self, rx_rate_limiter, ops)
}
/// Provides a `BucketUpdate` description for the TX bandwidth rate limiter.
pub fn tx_bytes(&self) -> BucketUpdate {
get_bucket_update!(self, tx_rate_limiter, bandwidth)
}
/// Provides a `BucketUpdate` description for the TX ops rate limiter.
pub fn tx_ops(&self) -> BucketUpdate {
get_bucket_update!(self, tx_rate_limiter, ops)
}
}
/// Configuration information for virtio net devices.
/// TODO: https://github.com/kata-containers/kata-containers/issues/8382.
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, Default)]
pub struct VirtioNetDeviceConfigInfo {
/// ID of the guest network interface.
pub iface_id: String,
/// Host level path for the guest network interface.
pub host_dev_name: String,
/// Number of virtqueues to use.
pub num_queues: usize,
/// Size of each virtqueue. Unit: byte.
pub queue_size: u16,
/// Guest MAC address.
pub guest_mac: Option<MacAddr>,
/// Rate Limiter for received packages.
pub rx_rate_limiter: Option<RateLimiterConfigInfo>,
/// Rate Limiter for transmitted packages.
pub tx_rate_limiter: Option<RateLimiterConfigInfo>,
/// allow duplicate mac
pub allow_duplicate_mac: bool,
/// Use shared irq
pub use_shared_irq: Option<bool>,
/// Use generic irq
pub use_generic_irq: Option<bool>,
}
impl VirtioNetDeviceConfigInfo {
/// Returns the tap device that `host_dev_name` refers to.
pub fn open_tap(&self) -> std::result::Result<Tap, VirtioNetDeviceError> {
Tap::open_named(self.host_dev_name.as_str(), false).map_err(VirtioNetDeviceError::OpenTap)
}
/// Returns a reference to the mac address. It the mac address is not configured, it
/// return None.
pub fn guest_mac(&self) -> Option<&MacAddr> {
self.guest_mac.as_ref()
}
///Rx and Tx queue and max queue sizes
pub fn queue_sizes(&self) -> Vec<u16> {
let mut queue_size = self.queue_size;
if queue_size == 0 {
queue_size = DEFAULT_QUEUE_SIZE;
}
let num_queues = if self.num_queues > 0 {
self.num_queues
} else {
DEFAULT_NUM_QUEUES
};
(0..num_queues).map(|_| queue_size).collect::<Vec<u16>>()
}
}
impl ConfigItem for VirtioNetDeviceConfigInfo {
type Err = VirtioNetDeviceError;
fn id(&self) -> &str {
&self.iface_id
}
fn check_conflicts(&self, other: &Self) -> Result<(), VirtioNetDeviceError> {
if self.iface_id == other.iface_id {
Err(VirtioNetDeviceError::DeviceIDAlreadyExist(
self.iface_id.clone(),
))
} else if !other.allow_duplicate_mac
&& self.guest_mac.is_some()
&& self.guest_mac == other.guest_mac
{
Err(VirtioNetDeviceError::GuestMacAddressInUse(
self.guest_mac.as_ref().unwrap().to_string(),
))
} else if self.host_dev_name == other.host_dev_name {
Err(VirtioNetDeviceError::HostDeviceNameInUse(
self.host_dev_name.clone(),
))
} else {
Ok(())
}
}
}
/// Virtio Net Device Info
pub type VirtioNetDeviceInfo = DeviceConfigInfo<VirtioNetDeviceConfigInfo>;
/// Device manager to manage all virtio net devices.
pub struct VirtioNetDeviceMgr {
pub(crate) info_list: DeviceConfigInfos<VirtioNetDeviceConfigInfo>,
pub(crate) use_shared_irq: bool,
}
impl VirtioNetDeviceMgr {
/// Gets the index of the device with the specified `drive_id` if it exists in the list.
pub fn get_index_of_iface_id(&self, if_id: &str) -> Option<usize> {
self.info_list
.iter()
.position(|info| info.config.iface_id.eq(if_id))
}
/// Insert or update a virtio net device into the manager.
pub fn insert_device(
&mut self,
mut ctx: DeviceOpContext,
config: VirtioNetDeviceConfigInfo,
) -> std::result::Result<(), VirtioNetDeviceError> {
if !config.num_queues.is_multiple_of(2) {
return Err(VirtioNetDeviceError::InvalidQueueNum(config.num_queues));
}
if !cfg!(feature = "hotplug") && ctx.is_hotplug {
return Err(VirtioNetDeviceError::UpdateNotAllowedPostBoot);
}
slog::info!(
ctx.logger(),
"add virtio-net device configuration";
"subsystem" => "net_dev_mgr",
"id" => &config.iface_id,
"host_dev_name" => &config.host_dev_name,
);
let device_index = self.info_list.insert_or_update(&config)?;
if ctx.is_hotplug {
slog::info!(
ctx.logger(),
"attach virtio-net device";
"subsystem" => "net_dev_mgr",
"id" => &config.iface_id,
"host_dev_name" => &config.host_dev_name,
);
match Self::create_device(&config, &mut ctx) {
Ok(device) => {
let dev = DeviceManager::create_mmio_virtio_device(
device,
&mut ctx,
config.use_shared_irq.unwrap_or(self.use_shared_irq),
config.use_generic_irq.unwrap_or(USE_GENERIC_IRQ),
)
.map_err(VirtioNetDeviceError::DeviceManager)?;
ctx.insert_hotplug_mmio_device(&dev, None)
.map_err(VirtioNetDeviceError::DeviceManager)?;
// live-upgrade need save/restore device from info.device.
self.info_list[device_index].set_device(dev);
}
Err(e) => {
self.info_list.remove(device_index);
return Err(VirtioNetDeviceError::Virtio(e));
}
}
}
Ok(())
}
/// Update the ratelimiter settings of a virtio net device.
pub fn update_device_ratelimiters(
&mut self,
new_cfg: VirtioNetDeviceConfigUpdateInfo,
) -> std::result::Result<(), VirtioNetDeviceError> {
match self.get_index_of_iface_id(&new_cfg.iface_id) {
Some(index) => {
let config = &mut self.info_list[index].config;
config.rx_rate_limiter = new_cfg.rx_rate_limiter.clone();
config.tx_rate_limiter = new_cfg.tx_rate_limiter.clone();
let device = self.info_list[index].device.as_mut().ok_or_else(|| {
VirtioNetDeviceError::InvalidIfaceId(new_cfg.iface_id.clone())
})?;
if let Some(mmio_dev) = device.as_any().downcast_ref::<DbsMmioV2Device>() {
let guard = mmio_dev.state();
let inner_dev = guard.get_inner_device();
if let Some(net_dev) = inner_dev
.as_any()
.downcast_ref::<virtio::net::Net<GuestAddressSpaceImpl>>()
{
return net_dev
.set_patch_rate_limiters(
new_cfg.rx_bytes(),
new_cfg.rx_ops(),
new_cfg.tx_bytes(),
new_cfg.tx_ops(),
)
.map(|_p| ())
.map_err(|_e| VirtioNetDeviceError::NetEpollHanderSendFail);
}
}
Ok(())
}
None => Err(VirtioNetDeviceError::InvalidIfaceId(
new_cfg.iface_id.clone(),
)),
}
}
/// Attach all configured net device to the virtual machine instance.
pub fn attach_devices(
&mut self,
ctx: &mut DeviceOpContext,
) -> std::result::Result<(), VirtioNetDeviceError> {
for info in self.info_list.iter_mut() {
slog::info!(
ctx.logger(),
"attach virtio-net device";
"subsystem" => "net_dev_mgr",
"id" => &info.config.iface_id,
"host_dev_name" => &info.config.host_dev_name,
);
let device = Self::create_device(&info.config, ctx)
.map_err(VirtioNetDeviceError::CreateNetDevice)?;
let device = DeviceManager::create_mmio_virtio_device(
device,
ctx,
info.config.use_shared_irq.unwrap_or(self.use_shared_irq),
info.config.use_generic_irq.unwrap_or(USE_GENERIC_IRQ),
)
.map_err(VirtioNetDeviceError::RegisterNetDevice)?;
info.set_device(device);
}
Ok(())
}
fn create_device(
cfg: &VirtioNetDeviceConfigInfo,
ctx: &mut DeviceOpContext,
) -> std::result::Result<Box<Net<GuestAddressSpaceImpl>>, virtio::Error> {
let epoll_mgr = ctx.epoll_mgr.clone().ok_or(virtio::Error::InvalidInput)?;
let rx_rate_limiter = match cfg.rx_rate_limiter.as_ref() {
Some(rl) => Some(rl.try_into().map_err(virtio::Error::IOError)?),
None => None,
};
let tx_rate_limiter = match cfg.tx_rate_limiter.as_ref() {
Some(rl) => Some(rl.try_into().map_err(virtio::Error::IOError)?),
None => None,
};
let net_device = Net::new(
cfg.host_dev_name.clone(),
cfg.guest_mac(),
Arc::new(cfg.queue_sizes()),
epoll_mgr,
rx_rate_limiter,
tx_rate_limiter,
)?;
Ok(Box::new(net_device))
}
/// Remove all virtio-net devices.
pub fn remove_devices(&mut self, ctx: &mut DeviceOpContext) -> Result<(), DeviceMgrError> {
while let Some(mut info) = self.info_list.pop() {
slog::info!(
ctx.logger(),
"remove virtio-net device: {}",
info.config.iface_id
);
if let Some(device) = info.device.take() {
DeviceManager::destroy_mmio_device(device, ctx)?;
}
}
Ok(())
}
}
impl Default for VirtioNetDeviceMgr {
/// Create a new virtio net device manager.
fn default() -> Self {
VirtioNetDeviceMgr {
info_list: DeviceConfigInfos::new(),
use_shared_irq: USE_SHARED_IRQ,
}
}
}

View File

@@ -195,10 +195,14 @@ pub enum StartMicroVmError {
#[error("virtio-blk errors: {0}")]
BlockDeviceError(#[source] device_manager::blk_dev_mgr::BlockDeviceError),
#[cfg(feature = "virtio-net")]
/// Virtio-net errors.
#[error("virtio-net errors: {0}")]
VirtioNetDeviceError(#[source] device_manager::virtio_net_dev_mgr::VirtioNetDeviceError),
#[cfg(any(
feature = "virtio-net",
feature = "vhost-net",
feature = "vhost-user-net"
))]
/// Network device errors.
#[error("network device errors: {0}")]
NetworkDeviceError(#[source] device_manager::net_dev_mgr::NetworkDeviceError),
#[cfg(any(feature = "virtio-fs", feature = "vhost-user-fs"))]
/// Virtio-fs errors.
@@ -209,18 +213,6 @@ pub enum StartMicroVmError {
/// Virtio-balloon errors.
#[error("virtio-balloon errors: {0}")]
BalloonDeviceError(#[source] device_manager::balloon_dev_mgr::BalloonDeviceError),
/// Vhost-net device errors.
#[cfg(feature = "vhost-net")]
#[error("vhost-net errors: {0:?}")]
VhostNetDeviceError(#[source] device_manager::vhost_net_dev_mgr::VhostNetDeviceError),
/// Vhost-user-net device errors.
#[cfg(feature = "vhost-user-net")]
#[error("vhost-user-net errors: {0:?}")]
VhostUserNetDeviceError(
#[source] device_manager::vhost_user_net_dev_mgr::VhostUserNetDeviceError,
),
#[cfg(feature = "host-device")]
/// Failed to create VFIO device
#[error("cannot create VFIO device {0:?}")]

View File

@@ -358,6 +358,7 @@ impl DragonballInner {
num_queues: Some(num_queues),
queue_size: Some(config.queue_size as u16),
backend: dragonball::api::v1::Backend::VhostUser(DragonballVhostUserConfig {
iface_id: config.dev_id.clone(),
sock_path: config.socket_path.clone(),
}),
guest_mac,

View File

@@ -58,6 +58,10 @@ impl VhostUserEndpoint {
fn get_network_config(&self) -> VhostUserConfig {
VhostUserConfig {
// Name the device after the virt interface, as the virtio-net and
// vhost-net paths do, so the hypervisor identifies it by a name
// the runtime knows rather than by its socket path.
dev_id: self.name.clone(),
socket_path: self.socket_path.clone(),
mac_address: self.guest_mac.clone(),
device_type: VhostUserType::Net,