mirror of
https://github.com/aljazceru/kata-containers.git
synced 2026-01-04 23:14:19 +01:00
Merge pull request #7675 from justxuewei/vhost-net
This commit is contained in:
13
src/dragonball/Cargo.lock
generated
13
src/dragonball/Cargo.lock
generated
@@ -396,6 +396,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"threadpool",
|
||||
"vhost",
|
||||
"virtio-bindings",
|
||||
"virtio-queue",
|
||||
"vm-memory",
|
||||
@@ -2071,6 +2072,18 @@ version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "vhost"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6769e8dbf5276b4376439fbf36bb880d203bf614bf7ef444198edc24b5a9f35"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"libc",
|
||||
"vm-memory",
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "virtio-bindings"
|
||||
version = "0.1.0"
|
||||
|
||||
@@ -63,3 +63,4 @@ virtio-net = ["dbs-virtio-devices/virtio-net", "virtio-queue"]
|
||||
virtio-fs = ["dbs-virtio-devices/virtio-fs-pro", "virtio-queue", "atomic-guest-memory"]
|
||||
virtio-mem = ["dbs-virtio-devices/virtio-mem", "virtio-queue", "atomic-guest-memory"]
|
||||
virtio-balloon = ["dbs-virtio-devices/virtio-balloon", "virtio-queue"]
|
||||
vhost-net = ["dbs-virtio-devices/vhost-net"]
|
||||
|
||||
@@ -17,3 +17,9 @@ pub use self::instance_info::{InstanceInfo, InstanceState};
|
||||
/// Wrapper for configuring the memory and CPU of the microVM.
|
||||
mod machine_config;
|
||||
pub use self::machine_config::{VmConfigError, MAX_SUPPORTED_VCPUS};
|
||||
|
||||
/// Wrapper for configuring the virtio networking
|
||||
#[cfg(any(feature = "virtio-net", feature = "vhost-net"))]
|
||||
mod virtio_net;
|
||||
#[cfg(any(feature = "virtio-net", feature = "vhost-net"))]
|
||||
pub use virtio_net::{Backend, NetworkInterfaceConfig, NetworkInterfaceUpdateConfig, VirtioConfig};
|
||||
|
||||
217
src/dragonball/src/api/v1/virtio_net.rs
Normal file
217
src/dragonball/src/api/v1/virtio_net.rs
Normal file
@@ -0,0 +1,217 @@
|
||||
// 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 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::{self, VhostNetDeviceConfigInfo};
|
||||
#[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),
|
||||
}
|
||||
|
||||
impl Default for Backend {
|
||||
fn default() -> Self {
|
||||
Self::Virtio(VirtioConfig::default())
|
||||
}
|
||||
}
|
||||
|
||||
/// Virtio network config, working for virtio-net and 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,
|
||||
}
|
||||
|
||||
/// 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.
|
||||
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
|
||||
.unwrap_or(vhost_net_dev_mgr::DEFAULT_NUM_QUEUES);
|
||||
let queue_size = value
|
||||
.queue_size
|
||||
.unwrap_or(vhost_net_dev_mgr::DEFAULT_QUEUE_SIZE);
|
||||
|
||||
// It is safe because we tested the type of config before.
|
||||
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,
|
||||
vq_pairs: num_queues / 2,
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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(test)]
|
||||
mod tests {
|
||||
use dbs_utils::net::MacAddr;
|
||||
|
||||
use crate::api::v1::Backend;
|
||||
|
||||
use super::NetworkInterfaceConfig;
|
||||
|
||||
#[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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,10 @@ pub use crate::device_manager::fs_dev_mgr::{
|
||||
};
|
||||
#[cfg(feature = "virtio-mem")]
|
||||
pub use crate::device_manager::mem_dev_mgr::{MemDeviceConfigInfo, MemDeviceError};
|
||||
#[cfg(feature = "vhost-net")]
|
||||
pub use crate::device_manager::vhost_net_dev_mgr::{
|
||||
VhostNetDeviceConfigInfo, VhostNetDeviceError, VhostNetDeviceMgr,
|
||||
};
|
||||
#[cfg(feature = "virtio-net")]
|
||||
pub use crate::device_manager::virtio_net_dev_mgr::{
|
||||
VirtioNetDeviceConfigInfo, VirtioNetDeviceConfigUpdateInfo, VirtioNetDeviceError,
|
||||
@@ -101,6 +105,11 @@ pub enum VmmActionError {
|
||||
#[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),
|
||||
|
||||
#[cfg(feature = "virtio-fs")]
|
||||
/// The action `InsertFsDevice` failed either because of bad user input or an internal error.
|
||||
#[error("virtio-fs device error: {0}")]
|
||||
@@ -182,11 +191,11 @@ pub enum VmmAction {
|
||||
/// are the RX and TX rate limiters.
|
||||
UpdateBlockDevice(BlockDeviceConfigUpdateInfo),
|
||||
|
||||
#[cfg(feature = "virtio-net")]
|
||||
#[cfg(any(feature = "virtio-net", feature = "vhost-net"))]
|
||||
/// Add a new network interface config or update one that already exists using the
|
||||
/// `NetworkInterfaceConfig` as input. This action can only be called before the microVM has
|
||||
/// booted. The response is sent using the `OutcomeSender`.
|
||||
InsertNetworkDevice(VirtioNetDeviceConfigInfo),
|
||||
InsertNetworkDevice(NetworkInterfaceConfig),
|
||||
|
||||
#[cfg(feature = "virtio-net")]
|
||||
/// Update a network interface, after microVM start. Currently, the only updatable properties
|
||||
@@ -309,10 +318,12 @@ impl VmmService {
|
||||
VmmAction::RemoveBlockDevice(drive_id) => {
|
||||
self.remove_block_device(vmm, event_mgr, &drive_id)
|
||||
}
|
||||
VmmAction::InsertNetworkDevice(config) => match config.backend {
|
||||
#[cfg(feature = "virtio-net")]
|
||||
VmmAction::InsertNetworkDevice(virtio_net_cfg) => {
|
||||
self.add_virtio_net_device(vmm, event_mgr, virtio_net_cfg)
|
||||
}
|
||||
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 = "virtio-net")]
|
||||
VmmAction::UpdateNetworkInterface(netif_update) => {
|
||||
self.update_net_rate_limiters(vmm, netif_update)
|
||||
@@ -676,6 +687,28 @@ impl VmmService {
|
||||
.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 = "virtio-fs")]
|
||||
#[instrument(skip(self))]
|
||||
fn add_fs_device(&mut self, vmm: &mut Vmm, config: FsDeviceConfigInfo) -> VmmRequestResult {
|
||||
@@ -1497,7 +1530,7 @@ mod tests {
|
||||
let tests = &mut [
|
||||
// hotplug unready
|
||||
TestData::new(
|
||||
VmmAction::InsertNetworkDevice(VirtioNetDeviceConfigInfo::default()),
|
||||
VmmAction::InsertNetworkDevice(NetworkInterfaceConfig::default()),
|
||||
InstanceState::Running,
|
||||
&|result| {
|
||||
assert!(matches!(
|
||||
@@ -1516,7 +1549,7 @@ mod tests {
|
||||
),
|
||||
// success
|
||||
TestData::new(
|
||||
VmmAction::InsertNetworkDevice(VirtioNetDeviceConfigInfo::default()),
|
||||
VmmAction::InsertNetworkDevice(NetworkInterfaceConfig::default()),
|
||||
InstanceState::Uninitialized,
|
||||
&|result| {
|
||||
assert!(result.is_ok());
|
||||
|
||||
@@ -37,6 +37,7 @@ virtio-queue = "0.7.0"
|
||||
vmm-sys-util = "0.11.0"
|
||||
vm-memory = { version = "0.10.0", features = [ "backend-mmap" ] }
|
||||
sendfd = "0.4.3"
|
||||
vhost-rs = { version = "0.6.1", package = "vhost", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
vm-memory = { version = "0.10.0", features = [ "backend-mmap", "backend-atomic" ] }
|
||||
@@ -50,3 +51,5 @@ virtio-fs = ["virtio-mmio", "fuse-backend-rs/virtiofs", "nydus-rafs/virtio-fs"]
|
||||
virtio-fs-pro = ["virtio-fs", "nydus-storage/backend-registry", "nydus-storage/backend-oss"]
|
||||
virtio-mem = ["virtio-mmio"]
|
||||
virtio-balloon = ["virtio-mmio"]
|
||||
vhost = ["virtio-mmio", "vhost-rs/vhost-user-master", "vhost-rs/vhost-kern"]
|
||||
vhost-net = ["virtio-net", "vhost", "vhost-rs/vhost-net"]
|
||||
|
||||
@@ -41,8 +41,12 @@ pub mod mem;
|
||||
#[cfg(feature = "virtio-balloon")]
|
||||
pub mod balloon;
|
||||
|
||||
#[cfg(feature = "vhost")]
|
||||
pub mod vhost;
|
||||
|
||||
use std::io::Error as IOError;
|
||||
|
||||
use net::NetError;
|
||||
use virtio_queue::Error as VqError;
|
||||
use vm_memory::{GuestAddress, GuestAddressSpace, GuestMemoryError};
|
||||
|
||||
@@ -201,15 +205,20 @@ pub enum Error {
|
||||
#[error("virtio-vsock error: {0}")]
|
||||
VirtioVsockError(#[from] self::vsock::VsockError),
|
||||
|
||||
#[cfg(feature = "virtio-net")]
|
||||
#[error("Virtio-net error: {0}")]
|
||||
VirtioNetError(#[from] crate::net::NetError),
|
||||
|
||||
#[cfg(feature = "virtio-fs")]
|
||||
/// Error from Virtio fs.
|
||||
#[error("virtio-fs error: {0}")]
|
||||
VirtioFs(fs::Error),
|
||||
|
||||
#[cfg(feature = "virtio-net")]
|
||||
#[error("virtio-net error: {0:?}")]
|
||||
VirtioNet(NetError),
|
||||
|
||||
#[cfg(feature = "vhost-net")]
|
||||
#[error("vhost-net error: {0:?}")]
|
||||
/// Error from vhost-net.
|
||||
VhostNet(vhost::vhost_kern::net::Error),
|
||||
|
||||
#[cfg(feature = "virtio-mem")]
|
||||
#[error("Virtio-mem error: {0}")]
|
||||
VirtioMemError(#[from] mem::MemError),
|
||||
@@ -219,6 +228,26 @@ pub enum Error {
|
||||
VirtioBalloonError(#[from] balloon::BalloonError),
|
||||
}
|
||||
|
||||
// Error for tap devices
|
||||
#[cfg(feature = "virtio-net")]
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum TapError {
|
||||
#[error("missing {0} flags")]
|
||||
MissingFlags(String),
|
||||
|
||||
#[error("failed to set offload: {0:?}")]
|
||||
SetOffload(#[source] dbs_utils::net::TapError),
|
||||
|
||||
#[error("failed to set vnet_hdr_size: {0}")]
|
||||
SetVnetHdrSize(#[source] dbs_utils::net::TapError),
|
||||
|
||||
#[error("failed to open a tap device: {0}")]
|
||||
Open(#[source] dbs_utils::net::TapError),
|
||||
|
||||
#[error("failed to enable a tap device: {0}")]
|
||||
Enable(#[source] dbs_utils::net::TapError),
|
||||
}
|
||||
|
||||
/// Specialized std::result::Result for Virtio device operations.
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
|
||||
@@ -31,8 +31,8 @@ use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
use crate::device::{VirtioDeviceConfig, VirtioDeviceInfo};
|
||||
use crate::{
|
||||
ActivateError, ActivateResult, ConfigResult, DbsGuestAddressSpace, Error, Result, VirtioDevice,
|
||||
VirtioQueueConfig, TYPE_NET,
|
||||
ActivateError, ActivateResult, ConfigResult, DbsGuestAddressSpace, Error, Result, TapError,
|
||||
VirtioDevice, VirtioQueueConfig, TYPE_NET,
|
||||
};
|
||||
|
||||
const NET_DRIVER_NAME: &str = "virtio-net";
|
||||
@@ -60,15 +60,8 @@ pub const NET_EVENTS_COUNT: u32 = 6;
|
||||
/// Error for virtio-net devices to handle requests from guests.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum NetError {
|
||||
/// Open tap device failed.
|
||||
#[error("open tap device failed: {0}")]
|
||||
TapOpen(#[source] dbs_utils::net::TapError),
|
||||
/// Setting tap interface offload flags failed.
|
||||
#[error("set tap device vnet header size failed: {0}")]
|
||||
TapSetOffload(#[source] dbs_utils::net::TapError),
|
||||
/// Setting vnet header size failed.
|
||||
#[error("set tap device vnet header size failed: {0}")]
|
||||
TapSetVnetHdrSize(#[source] dbs_utils::net::TapError),
|
||||
#[error("tap device operation error: {0:?}")]
|
||||
TapError(#[source] TapError),
|
||||
}
|
||||
|
||||
/// Metrics specific to the net device.
|
||||
@@ -150,7 +143,7 @@ impl<Q: QueueT> RxVirtio<Q> {
|
||||
}
|
||||
}
|
||||
|
||||
fn vnet_hdr_len() -> usize {
|
||||
pub fn vnet_hdr_len() -> usize {
|
||||
mem::size_of::<virtio_net_hdr_v1>()
|
||||
}
|
||||
|
||||
@@ -665,11 +658,11 @@ impl<AS: GuestAddressSpace> Net<AS> {
|
||||
tap.set_offload(
|
||||
net_gen::TUN_F_CSUM | net_gen::TUN_F_UFO | net_gen::TUN_F_TSO4 | net_gen::TUN_F_TSO6,
|
||||
)
|
||||
.map_err(NetError::TapSetOffload)?;
|
||||
.map_err(|err| Error::VirtioNet(NetError::TapError(TapError::SetOffload(err))))?;
|
||||
|
||||
let vnet_hdr_size = vnet_hdr_len() as i32;
|
||||
tap.set_vnet_hdr_size(vnet_hdr_size)
|
||||
.map_err(NetError::TapSetVnetHdrSize)?;
|
||||
.map_err(|err| Error::VirtioNet(NetError::TapError(TapError::SetVnetHdrSize(err))))?;
|
||||
info!("net tap set finished");
|
||||
|
||||
let mut avail_features = 1u64 << VIRTIO_NET_F_GUEST_CSUM
|
||||
@@ -722,7 +715,8 @@ impl<AS: GuestAddressSpace> Net<AS> {
|
||||
tx_rate_limiter: Option<RateLimiter>,
|
||||
) -> Result<Self> {
|
||||
info!("open net tap {}", host_dev_name);
|
||||
let tap = Tap::open_named(host_dev_name.as_str(), false).map_err(NetError::TapOpen)?;
|
||||
let tap = Tap::open_named(host_dev_name.as_str(), false)
|
||||
.map_err(|err| Error::VirtioNet(NetError::TapError(TapError::Open(err))))?;
|
||||
info!("net tap opened");
|
||||
|
||||
Self::new_with_tap(
|
||||
|
||||
8
src/dragonball/src/dbs_virtio_devices/src/vhost/mod.rs
Normal file
8
src/dragonball/src/dbs_virtio_devices/src/vhost/mod.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
// Copyright (C) 2019-2023 Alibaba Cloud. All rights reserved.
|
||||
// Copyright (C) 2019-2023 Ant Group. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//! Vhost-based virtio device backend implementations.
|
||||
|
||||
#[cfg(feature = "vhost-net")]
|
||||
pub mod vhost_kern;
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright (C) 2019-2023 Alibaba Cloud. All rights reserved.
|
||||
// Copyright (C) 2019-2023 Ant Group. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//! Vhost-based virtio device backend implementations.
|
||||
|
||||
#[cfg(feature = "vhost-net")]
|
||||
pub mod net;
|
||||
|
||||
#[cfg(all(feature = "vhost-net", test))]
|
||||
pub mod test_utils;
|
||||
@@ -0,0 +1,965 @@
|
||||
// Copyright (C) 2019-2023 Alibaba Cloud. All rights reserved.
|
||||
// Copyright (C) 2019-2023 Ant Group. 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::marker::PhantomData;
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
|
||||
use dbs_device::resources::ResourceConstraint;
|
||||
use dbs_utils::epoll_manager::{
|
||||
EpollManager, EventOps, EventSet, Events, MutEventSubscriber, SubscriberId,
|
||||
};
|
||||
use dbs_utils::metric::IncMetric;
|
||||
use dbs_utils::net::{net_gen, MacAddr, Tap};
|
||||
use log::{debug, error, info, trace, warn};
|
||||
#[cfg(not(test))]
|
||||
use vhost_rs::net::VhostNet as VhostNetTrait;
|
||||
#[cfg(not(test))]
|
||||
use vhost_rs::vhost_kern::net::Net as VhostNet;
|
||||
use vhost_rs::vhost_user::message::VhostUserVringAddrFlags;
|
||||
#[cfg(not(test))]
|
||||
use vhost_rs::VhostBackend;
|
||||
use vhost_rs::{VhostUserMemoryRegionInfo, VringConfigData};
|
||||
use virtio_bindings::bindings::virtio_net::*;
|
||||
use virtio_bindings::bindings::virtio_ring::*;
|
||||
use virtio_queue::{Descriptor, DescriptorChain, QueueT};
|
||||
use vm_memory::{Address, Bytes, GuestMemory, GuestMemoryRegion, MemoryRegionAddress};
|
||||
|
||||
use crate::net::{vnet_hdr_len, NetDeviceMetrics};
|
||||
#[cfg(test)]
|
||||
use crate::vhost::vhost_kern::test_utils::{
|
||||
MockVhostBackend as VhostBackend, MockVhostNet as VhostNet,
|
||||
};
|
||||
use crate::{
|
||||
ActivateError, ConfigResult, DbsGuestAddressSpace, Error as VirtioError,
|
||||
Result as VirtioResult, TapError, VirtioDevice, VirtioDeviceConfig, VirtioDeviceInfo, TYPE_NET,
|
||||
};
|
||||
|
||||
const NET_DRIVER_NAME: &str = "vhost-net";
|
||||
// Epoll token for control queue
|
||||
const CTRL_SLOT: u32 = 0;
|
||||
// Control queue size
|
||||
const CTRL_QUEUE_SIZE: u16 = 64;
|
||||
|
||||
/// Error for vhost-net devices to handle requests from guests.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error("tap device operation error: {0:?}")]
|
||||
TapError(#[source] TapError),
|
||||
#[error("vhost error: {0}")]
|
||||
VhostError(#[source] vhost_rs::Error),
|
||||
}
|
||||
|
||||
/// Vhost-net device implementation
|
||||
pub struct Net<AS, Q, R>
|
||||
where
|
||||
AS: DbsGuestAddressSpace,
|
||||
Q: QueueT + Send + 'static,
|
||||
R: GuestMemoryRegion + Sync + Send + 'static,
|
||||
{
|
||||
taps: Vec<Tap>,
|
||||
vq_pairs: usize,
|
||||
handles: Vec<VhostNet<AS>>,
|
||||
device_info: VirtioDeviceInfo,
|
||||
queue_sizes: Arc<Vec<u16>>,
|
||||
ctrl_queue_size: u16,
|
||||
subscriber_id: Option<SubscriberId>,
|
||||
id: String,
|
||||
kernel_vring_bases: Option<Vec<(u32, u32)>>,
|
||||
metrics: Arc<NetDeviceMetrics>,
|
||||
|
||||
// Type-Q placeholder
|
||||
_mark_q: PhantomData<Q>,
|
||||
// Type-R placeholder
|
||||
_mark_r: PhantomData<R>,
|
||||
}
|
||||
|
||||
/// Ensure that the tap interface has the correct flags and sets the
|
||||
/// offload and VNET header size to the appropriate values.
|
||||
fn validate_and_configure_tap(tap: &Tap, vq_pairs: usize) -> VirtioResult<()> {
|
||||
// Check if there are missing flags。
|
||||
let flags = tap.if_flags();
|
||||
let mut required_flags = vec![
|
||||
(net_gen::IFF_TAP, "IFF_TAP"),
|
||||
(net_gen::IFF_NO_PI, "IFF_NO_PI"),
|
||||
(net_gen::IFF_VNET_HDR, "IFF_VNET_HDR"),
|
||||
];
|
||||
if vq_pairs > 1 {
|
||||
required_flags.push((net_gen::IFF_MULTI_QUEUE, "IFF_MULTI_QUEUE"));
|
||||
}
|
||||
let missing_flags = required_flags
|
||||
.iter()
|
||||
.filter_map(
|
||||
|(value, name)| {
|
||||
if value & flags == 0 {
|
||||
Some(name)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
)
|
||||
.collect::<Vec<_>>();
|
||||
if !missing_flags.is_empty() {
|
||||
return Err(VirtioError::VhostNet(Error::TapError(
|
||||
TapError::MissingFlags(
|
||||
missing_flags
|
||||
.into_iter()
|
||||
.map(|flag| *flag)
|
||||
.collect::<Vec<&str>>()
|
||||
.join(", "),
|
||||
),
|
||||
)));
|
||||
}
|
||||
|
||||
let vnet_hdr_size = vnet_hdr_len() as i32;
|
||||
tap.set_vnet_hdr_size(vnet_hdr_size)
|
||||
.map_err(|err| VirtioError::VhostNet(Error::TapError(TapError::SetVnetHdrSize(err))))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl<AS, Q, R> Net<AS, Q, R>
|
||||
where
|
||||
AS: DbsGuestAddressSpace,
|
||||
Q: QueueT + Send + 'static,
|
||||
R: GuestMemoryRegion + Sync + Send + 'static,
|
||||
{
|
||||
/// Create a new vhost-net device with a given tap interface.
|
||||
pub fn new_with_tap(
|
||||
tap: Tap,
|
||||
vq_pairs: usize,
|
||||
guest_mac: Option<&MacAddr>,
|
||||
queue_sizes: Arc<Vec<u16>>,
|
||||
event_mgr: EpollManager,
|
||||
) -> VirtioResult<Self> {
|
||||
trace!(target: "vhost-net", "{}: Net::new_with_tap()", NET_DRIVER_NAME);
|
||||
|
||||
let taps = tap
|
||||
.into_mq_taps(vq_pairs)
|
||||
.map_err(|err| VirtioError::VhostNet(Error::TapError(TapError::Open(err))))?;
|
||||
for tap in taps.iter() {
|
||||
validate_and_configure_tap(tap, vq_pairs)?;
|
||||
}
|
||||
|
||||
let mut avail_features = 1u64 << VIRTIO_NET_F_GUEST_CSUM
|
||||
| 1u64 << VIRTIO_NET_F_CSUM
|
||||
| 1u64 << VIRTIO_NET_F_GUEST_TSO4
|
||||
| 1u64 << VIRTIO_NET_F_GUEST_UFO
|
||||
| 1u64 << VIRTIO_NET_F_HOST_TSO4
|
||||
| 1u64 << VIRTIO_NET_F_HOST_UFO
|
||||
| 1u64 << VIRTIO_NET_F_MRG_RXBUF
|
||||
| 1u64 << VIRTIO_RING_F_INDIRECT_DESC
|
||||
| 1u64 << VIRTIO_RING_F_EVENT_IDX
|
||||
| 1u64 << VIRTIO_F_NOTIFY_ON_EMPTY
|
||||
| 1u64 << VIRTIO_F_VERSION_1;
|
||||
|
||||
if vq_pairs > 1 {
|
||||
avail_features |= (1 << VIRTIO_NET_F_MQ | 1 << VIRTIO_NET_F_CTRL_VQ) as u64;
|
||||
}
|
||||
// Network device configuration layout:
|
||||
// https://docs.oasis-open.org/virtio/virtio/v1.1/csprd01/virtio-v1.1-csprd01.html#x1-2000004
|
||||
// - [u8; 6]: mac address
|
||||
// - u16: status
|
||||
// - u16: max_virtqueue_pairs
|
||||
// - u16: mtu
|
||||
// - u32: speed
|
||||
// - u8: duplex
|
||||
let mut config_space = vec![0u8; 17];
|
||||
if let Some(mac) = guest_mac {
|
||||
// When this feature isn't available, the driver generates a random
|
||||
// MAC address. Otherwise, it should attempt to read the device MAC
|
||||
// address from the config space.
|
||||
avail_features |= 1u64 << VIRTIO_NET_F_MAC;
|
||||
config_space[0..6].copy_from_slice(mac.get_bytes());
|
||||
} else {
|
||||
avail_features &= !(1 << VIRTIO_NET_F_MAC) as u64;
|
||||
}
|
||||
|
||||
// status: mark link as up
|
||||
config_space[6] = VIRTIO_NET_S_LINK_UP as u8;
|
||||
config_space[7] = 0;
|
||||
// max_virtqueue_pairs: only support one rx/tx pair
|
||||
config_space[8] = vq_pairs as u8;
|
||||
config_space[9] = 0;
|
||||
// mtu: 1500 = 1536 - vxlan header?
|
||||
config_space[10] = 220;
|
||||
config_space[11] = 5;
|
||||
// speed: 1000Mb
|
||||
config_space[12] = 232;
|
||||
config_space[13] = 3;
|
||||
config_space[14] = 0;
|
||||
config_space[15] = 0;
|
||||
// duplex: full duplex: 0x01
|
||||
config_space[16] = 1;
|
||||
|
||||
let device_info = VirtioDeviceInfo::new(
|
||||
NET_DRIVER_NAME.to_owned(),
|
||||
avail_features,
|
||||
queue_sizes.clone(),
|
||||
config_space,
|
||||
event_mgr,
|
||||
);
|
||||
let id = device_info.driver_name.clone();
|
||||
|
||||
Ok(Net {
|
||||
taps,
|
||||
vq_pairs,
|
||||
handles: Vec::new(),
|
||||
device_info,
|
||||
queue_sizes,
|
||||
subscriber_id: None,
|
||||
ctrl_queue_size: {
|
||||
if vq_pairs > 1 {
|
||||
CTRL_QUEUE_SIZE
|
||||
} else {
|
||||
0
|
||||
}
|
||||
},
|
||||
id,
|
||||
kernel_vring_bases: None,
|
||||
_mark_r: PhantomData,
|
||||
_mark_q: PhantomData,
|
||||
metrics: Arc::new(NetDeviceMetrics::default()),
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a vhost network with the Tap name
|
||||
pub fn new(
|
||||
host_dev_name: String,
|
||||
vq_pairs: usize,
|
||||
guest_mac: Option<&MacAddr>,
|
||||
queue_sizes: Arc<Vec<u16>>,
|
||||
event_mgr: EpollManager,
|
||||
) -> VirtioResult<Self> {
|
||||
// Open a TAP interface
|
||||
let tap = Tap::open_named(&host_dev_name, vq_pairs > 1)
|
||||
.map_err(|err| VirtioError::VhostNet(Error::TapError(TapError::Open(err))))?;
|
||||
tap.enable()
|
||||
.map_err(|err| VirtioError::VhostNet(Error::TapError(TapError::Enable(err))))?;
|
||||
|
||||
Self::new_with_tap(tap, vq_pairs, guest_mac, queue_sizes, event_mgr)
|
||||
}
|
||||
|
||||
fn do_device_activate(
|
||||
&mut self,
|
||||
config: &VirtioDeviceConfig<AS, Q, R>,
|
||||
vq_pairs: usize,
|
||||
) -> VirtioResult<()>
|
||||
where
|
||||
Q: QueueT + Send + 'static,
|
||||
R: GuestMemoryRegion + Sync + Send + 'static,
|
||||
{
|
||||
let guard = config.lock_guest_memory();
|
||||
let mem = guard.deref();
|
||||
|
||||
if self.handles.is_empty() {
|
||||
for _ in 0..vq_pairs {
|
||||
self.handles.push(
|
||||
VhostNet::<AS>::new(config.vm_as.clone())
|
||||
.map_err(|err| VirtioError::VhostNet(Error::VhostError(err)))?,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if self.kernel_vring_bases.is_none() {
|
||||
self.setup_vhost_backend(config, mem)?
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup_vhost_backend(
|
||||
&mut self,
|
||||
config: &VirtioDeviceConfig<AS, Q, R>,
|
||||
mem: &AS::M,
|
||||
) -> VirtioResult<()>
|
||||
where
|
||||
Q: QueueT + Send + 'static,
|
||||
R: GuestMemoryRegion + Sync + Send + 'static,
|
||||
{
|
||||
trace!(target: "vhost-net", "{}: Net::setup_vhost_backend(vq_pairs: {})", NET_DRIVER_NAME, self.vq_pairs);
|
||||
|
||||
if self.vq_pairs < 1 {
|
||||
error!(
|
||||
"{}: Invalid virtio queue pairs, expected a value greater than 0, but got {}",
|
||||
NET_DRIVER_NAME, self.vq_pairs
|
||||
);
|
||||
return Err(VirtioError::ActivateError(ActivateError::InvalidParam));
|
||||
}
|
||||
|
||||
if self.handles.len() != self.vq_pairs || self.taps.len() != self.vq_pairs {
|
||||
error!("{}: Invalid handlers or taps, handlers length {}, taps length {}, virtio queue pairs = {}",
|
||||
NET_DRIVER_NAME,
|
||||
self.handles.len(),
|
||||
self.taps.len(),
|
||||
self.vq_pairs);
|
||||
return Err(VirtioError::ActivateError(ActivateError::InternalError));
|
||||
}
|
||||
|
||||
for idx in 0..self.vq_pairs {
|
||||
self.init_vhost_dev(idx, config, mem)?;
|
||||
}
|
||||
|
||||
self.kernel_vring_bases = None;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init_vhost_dev(
|
||||
&mut self,
|
||||
pair_index: usize,
|
||||
config: &VirtioDeviceConfig<AS, Q, R>,
|
||||
mem: &AS::M,
|
||||
) -> VirtioResult<()>
|
||||
where
|
||||
Q: QueueT + Send + 'static,
|
||||
R: GuestMemoryRegion + Sync + Send + 'static,
|
||||
{
|
||||
trace!(target: "vhost-net", "{}: Net::init_vhost_dev(pair_index: {})", NET_DRIVER_NAME, pair_index);
|
||||
|
||||
let handle = &mut self.handles[pair_index];
|
||||
handle
|
||||
.set_owner()
|
||||
.map_err(|err| VirtioError::VhostNet(Error::VhostError(err)))?;
|
||||
|
||||
let avail_features = handle
|
||||
.get_features()
|
||||
.map_err(|err| VirtioError::VhostNet(Error::VhostError(err)))?;
|
||||
let features = self.device_info.acked_features() & avail_features;
|
||||
handle
|
||||
.set_features(features)
|
||||
.map_err(|err| VirtioError::VhostNet(Error::VhostError(err)))?;
|
||||
|
||||
let tap = &self.taps[pair_index];
|
||||
tap.set_offload(Self::virtio_features_to_tap_offload(
|
||||
self.device_info.acked_features(),
|
||||
))
|
||||
.map_err(|err| VirtioError::VhostNet(Error::TapError(TapError::SetOffload(err))))?;
|
||||
|
||||
self.setup_mem_table(pair_index, mem)?;
|
||||
|
||||
self.init_vhost_queues(pair_index, config)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup_mem_table(&mut self, pair_index: usize, mem: &AS::M) -> VirtioResult<()> {
|
||||
let handle = &mut self.handles[pair_index];
|
||||
|
||||
let mut regions = Vec::new();
|
||||
for region in mem.iter() {
|
||||
let guest_phys_addr = region.start_addr();
|
||||
|
||||
let userspace_addr = region
|
||||
.get_host_address(MemoryRegionAddress(0))
|
||||
.map_err(|_| VirtioError::InvalidGuestAddress(guest_phys_addr))?;
|
||||
|
||||
regions.push(VhostUserMemoryRegionInfo {
|
||||
guest_phys_addr: guest_phys_addr.raw_value(),
|
||||
memory_size: region.len(),
|
||||
userspace_addr: userspace_addr as *const u8 as u64,
|
||||
mmap_offset: 0,
|
||||
mmap_handle: -1,
|
||||
});
|
||||
}
|
||||
|
||||
handle
|
||||
.set_mem_table(®ions)
|
||||
.map_err(|err| VirtioError::VhostNet(Error::VhostError(err)))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init_vhost_queues(
|
||||
&mut self,
|
||||
pair_index: usize,
|
||||
config: &VirtioDeviceConfig<AS, Q, R>,
|
||||
) -> VirtioResult<()>
|
||||
where
|
||||
Q: QueueT + Send + 'static,
|
||||
R: GuestMemoryRegion + Sync + Send + 'static,
|
||||
{
|
||||
trace!(target: "vhost-net", "{}: Net::init_vhost_queues(pair_index: {})", NET_DRIVER_NAME, pair_index);
|
||||
|
||||
let handle = &mut self.handles[pair_index];
|
||||
let tap = &self.taps[pair_index];
|
||||
|
||||
let intr_evts = config.get_queue_interrupt_eventfds();
|
||||
assert_eq!(config.queues.len(), intr_evts.len());
|
||||
|
||||
let vq_pair = vec![
|
||||
&config.queues[2 * pair_index],
|
||||
&config.queues[2 * pair_index + 1],
|
||||
];
|
||||
|
||||
for queue_cfg in vq_pair.iter() {
|
||||
let queue = &queue_cfg.queue;
|
||||
let queue_index = queue_cfg.index() as usize;
|
||||
let vq_index = queue_index % 2;
|
||||
|
||||
handle
|
||||
.set_vring_num(vq_index, queue_cfg.queue.size())
|
||||
.map_err(|err| VirtioError::VhostNet(Error::VhostError(err)))?;
|
||||
|
||||
if let Some(vring_base) = &self.kernel_vring_bases {
|
||||
let base = if vq_index == 0 {
|
||||
vring_base[pair_index].0
|
||||
} else {
|
||||
vring_base[pair_index].1
|
||||
};
|
||||
handle
|
||||
.set_vring_base(vq_index, base as u16)
|
||||
.map_err(|err| VirtioError::VhostNet(Error::VhostError(err)))?;
|
||||
} else {
|
||||
handle
|
||||
.set_vring_base(vq_index, 0)
|
||||
.map_err(|err| VirtioError::VhostNet(Error::VhostError(err)))?;
|
||||
}
|
||||
|
||||
let config_data = &VringConfigData {
|
||||
queue_max_size: queue.max_size(),
|
||||
queue_size: queue.size(),
|
||||
flags: VhostUserVringAddrFlags::empty().bits(),
|
||||
desc_table_addr: queue.desc_table(),
|
||||
used_ring_addr: queue.used_ring(),
|
||||
avail_ring_addr: queue.avail_ring(),
|
||||
log_addr: None,
|
||||
};
|
||||
|
||||
handle
|
||||
.set_vring_addr(vq_index, config_data)
|
||||
.map_err(|err| VirtioError::VhostNet(Error::VhostError(err)))?;
|
||||
|
||||
handle
|
||||
.set_vring_call(vq_index, intr_evts[queue_index])
|
||||
.map_err(|err| VirtioError::VhostNet(Error::VhostError(err)))?;
|
||||
|
||||
handle
|
||||
.set_vring_kick(vq_index, &queue_cfg.eventfd)
|
||||
.map_err(|err| VirtioError::VhostNet(Error::VhostError(err)))?;
|
||||
|
||||
handle
|
||||
.set_backend(vq_index, Some(&tap.tap_file))
|
||||
.map_err(|err| VirtioError::VhostNet(Error::VhostError(err)))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn virtio_features_to_tap_offload(features: u64) -> u32 {
|
||||
let mut tap_offloads: u32 = 0;
|
||||
|
||||
if features & (1 << VIRTIO_NET_F_GUEST_CSUM) != 0 {
|
||||
tap_offloads |= net_gen::TUN_F_CSUM;
|
||||
}
|
||||
if features & (1 << VIRTIO_NET_F_GUEST_TSO4) != 0 {
|
||||
tap_offloads |= net_gen::TUN_F_TSO4;
|
||||
}
|
||||
if features & (1 << VIRTIO_NET_F_GUEST_TSO6) != 0 {
|
||||
tap_offloads |= net_gen::TUN_F_TSO6;
|
||||
}
|
||||
if features & (1 << VIRTIO_NET_F_GUEST_ECN) != 0 {
|
||||
tap_offloads |= net_gen::TUN_F_TSO_ECN;
|
||||
}
|
||||
if features & (1 << VIRTIO_NET_F_GUEST_UFO) != 0 {
|
||||
tap_offloads |= net_gen::TUN_F_UFO;
|
||||
}
|
||||
|
||||
tap_offloads
|
||||
}
|
||||
}
|
||||
|
||||
impl<AS, Q, R> VirtioDevice<AS, Q, R> for Net<AS, Q, R>
|
||||
where
|
||||
AS: DbsGuestAddressSpace,
|
||||
Q: QueueT + Send + 'static,
|
||||
R: GuestMemoryRegion + Sync + Send + 'static,
|
||||
{
|
||||
fn device_type(&self) -> u32 {
|
||||
TYPE_NET
|
||||
}
|
||||
|
||||
fn queue_max_sizes(&self) -> &[u16] {
|
||||
&self.queue_sizes
|
||||
}
|
||||
|
||||
fn ctrl_queue_max_sizes(&self) -> u16 {
|
||||
self.ctrl_queue_size
|
||||
}
|
||||
|
||||
fn get_avail_features(&self, page: u32) -> u32 {
|
||||
self.device_info.get_avail_features(page)
|
||||
}
|
||||
|
||||
fn set_acked_features(&mut self, page: u32, value: u32) {
|
||||
trace!(target: "vhost-net", "{}: Net::set_acked_features({}, 0x{:x})",
|
||||
self.id, page, value);
|
||||
self.device_info.set_acked_features(page, value);
|
||||
}
|
||||
|
||||
fn read_config(&mut self, offset: u64, data: &mut [u8]) -> ConfigResult {
|
||||
trace!(target: "vhost-net", "{}: Net::read_config(0x{:x}, {:?})",
|
||||
self.id, offset, data);
|
||||
self.device_info.read_config(offset, data).map_err(|e| {
|
||||
self.metrics.cfg_fails.inc();
|
||||
e
|
||||
})
|
||||
}
|
||||
|
||||
fn write_config(&mut self, offset: u64, data: &[u8]) -> ConfigResult {
|
||||
trace!(target: "vhost-net", "{}: Net::write_config(0x{:x}, {:?})",
|
||||
self.id, offset, data);
|
||||
self.device_info.write_config(offset, data).map_err(|e| {
|
||||
self.metrics.cfg_fails.inc();
|
||||
e
|
||||
})
|
||||
}
|
||||
|
||||
fn activate(&mut self, config: crate::VirtioDeviceConfig<AS, Q, R>) -> crate::ActivateResult {
|
||||
trace!(target: "vhost-net", "{}: Net::activate()", self.id);
|
||||
|
||||
// Do not support control queue and multi-queue.
|
||||
let vq_pairs = config.queues.len() / 2;
|
||||
if config.queues.len() % 2 != 0 || self.taps.len() != vq_pairs {
|
||||
self.metrics.activate_fails.inc();
|
||||
return Err(crate::ActivateError::InvalidParam);
|
||||
}
|
||||
|
||||
self.device_info
|
||||
.check_queue_sizes(&config.queues)
|
||||
.map_err(|err| {
|
||||
self.metrics.activate_fails.inc();
|
||||
err
|
||||
})?;
|
||||
|
||||
if let Err(err) = self.do_device_activate(&config, vq_pairs) {
|
||||
error!(target: "vhost-net", "device {:?} activate failed: {:?}", self.id, err);
|
||||
panic!("vhost-net device {:?} activate failed: {:?}", self.id, err);
|
||||
}
|
||||
|
||||
let handler = Box::new(NetEpollHandler {
|
||||
config,
|
||||
id: self.id.clone(),
|
||||
});
|
||||
self.subscriber_id = Some(self.device_info.register_event_handler(handler));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_resource_requirements(
|
||||
&self,
|
||||
requests: &mut Vec<dbs_device::resources::ResourceConstraint>,
|
||||
use_generic_irq: bool,
|
||||
) {
|
||||
trace!(target: "vhost-net", "{}: Net::get_resource_requirements()", self.id);
|
||||
|
||||
requests.push(ResourceConstraint::LegacyIrq { irq: None });
|
||||
if use_generic_irq {
|
||||
requests.push(ResourceConstraint::GenericIrq {
|
||||
size: (self.queue_sizes.len() + 1) as u32,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn remove(&mut self) {
|
||||
self.taps.clear();
|
||||
|
||||
let subscriber_id = self.subscriber_id.take();
|
||||
if let Some(subscriber_id) = subscriber_id {
|
||||
match self.device_info.remove_event_handler(subscriber_id) {
|
||||
Ok(_) => debug!("vhost-net: removed subscriber_id {:?}", self.subscriber_id),
|
||||
Err(err) => warn!("vhost-net: failed to remove event handler: {:?}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
trait FromNetCtrl<T> {
|
||||
fn from_net_ctrl_st<M: GuestMemory>(mem: &M, desc: &Descriptor) -> VirtioResult<T> {
|
||||
let mut buf = vec![0u8; std::mem::size_of::<T>()];
|
||||
match mem.read_slice(&mut buf, desc.addr()) {
|
||||
Ok(_) => unsafe { Ok(std::ptr::read_volatile(&buf[..] as *const _ as *const T)) },
|
||||
Err(err) => {
|
||||
error!("Failed to read from memory, {}", err);
|
||||
Err(VirtioError::InternalError)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromNetCtrl<virtio_net_ctrl_hdr> for virtio_net_ctrl_hdr {}
|
||||
impl FromNetCtrl<virtio_net_ctrl_mq> for virtio_net_ctrl_mq {}
|
||||
|
||||
pub(crate) struct NetEpollHandler<AS, Q, R>
|
||||
where
|
||||
AS: DbsGuestAddressSpace,
|
||||
Q: QueueT + Send + 'static,
|
||||
R: GuestMemoryRegion + Sync + Send + 'static,
|
||||
{
|
||||
pub(crate) config: VirtioDeviceConfig<AS, Q, R>,
|
||||
id: String,
|
||||
}
|
||||
|
||||
impl<AS, Q, R> NetEpollHandler<AS, Q, R>
|
||||
where
|
||||
AS: DbsGuestAddressSpace,
|
||||
Q: QueueT + Send + 'static,
|
||||
R: GuestMemoryRegion + Sync + Send + 'static,
|
||||
{
|
||||
fn process_ctrl_request(&mut self) -> VirtioResult<()> {
|
||||
let guard = self.config.lock_guest_memory();
|
||||
let mem = guard.deref();
|
||||
let cvq = self.config.ctrl_queue.as_mut().unwrap();
|
||||
|
||||
while let Some(mut desc_chain) = cvq.get_next_descriptor(mem)? {
|
||||
let len = match Self::process_ctrl_desc(&mut desc_chain, mem) {
|
||||
Ok(len) => {
|
||||
debug!("{}: process ctrl desc succeed!", self.id);
|
||||
len
|
||||
}
|
||||
Err(err) => {
|
||||
debug!(
|
||||
"{}: failed to process control queue request, {}",
|
||||
self.id, err
|
||||
);
|
||||
0
|
||||
}
|
||||
};
|
||||
cvq.add_used(mem, desc_chain.head_index(), len);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_ctrl_desc(
|
||||
desc_chain: &mut DescriptorChain<&AS::M>,
|
||||
mem: &AS::M,
|
||||
) -> VirtioResult<u32> {
|
||||
if let Some(header) = desc_chain.next() {
|
||||
let ctrl_hdr = virtio_net_ctrl_hdr::from_net_ctrl_st(mem, &header)?;
|
||||
match ctrl_hdr.class as u32 {
|
||||
VIRTIO_NET_CTRL_MQ => {
|
||||
Self::virtio_handle_ctrl_mq(desc_chain, ctrl_hdr.cmd, mem)?;
|
||||
return Self::virtio_handle_ctrl_status(desc_chain, VIRTIO_NET_OK as u8, mem);
|
||||
}
|
||||
_ => error!(
|
||||
"{}: unknown net control request class: 0x{:x}",
|
||||
NET_DRIVER_NAME, ctrl_hdr.class
|
||||
),
|
||||
}
|
||||
}
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn virtio_handle_ctrl_mq(
|
||||
desc_chain: &mut DescriptorChain<&AS::M>,
|
||||
cmd: u8,
|
||||
mem: &AS::M,
|
||||
) -> VirtioResult<()> {
|
||||
if cmd == VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET as u8 {
|
||||
if let Some(next) = desc_chain.next() {
|
||||
if let Ok(ctrl_mq) = virtio_net_ctrl_mq::from_net_ctrl_st(mem, &next) {
|
||||
let curr_queues = ctrl_mq.virtqueue_pairs;
|
||||
info!("{}: vq pairs: {}", NET_DRIVER_NAME, curr_queues);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn virtio_handle_ctrl_status(
|
||||
desc_chain: &mut DescriptorChain<&AS::M>,
|
||||
status: u8,
|
||||
mem: &AS::M,
|
||||
) -> VirtioResult<u32> {
|
||||
let buf = vec![status];
|
||||
let mut total = 0;
|
||||
|
||||
for next in desc_chain {
|
||||
if next.is_write_only() {
|
||||
match mem.write_slice(&buf, next.addr()) {
|
||||
Ok(_) => {
|
||||
debug!("{}: succeed to update virtio ctrl status!", NET_DRIVER_NAME);
|
||||
total += 1;
|
||||
}
|
||||
Err(_) => warn!("{}: failed to update ctrl status!", NET_DRIVER_NAME),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(total)
|
||||
}
|
||||
}
|
||||
|
||||
impl<AS, Q, R> MutEventSubscriber for NetEpollHandler<AS, Q, R>
|
||||
where
|
||||
AS: DbsGuestAddressSpace,
|
||||
Q: QueueT + Send + 'static,
|
||||
R: GuestMemoryRegion + Sync + Send + 'static,
|
||||
{
|
||||
fn process(&mut self, events: Events, _ops: &mut EventOps) {
|
||||
match events.data() {
|
||||
CTRL_SLOT => {
|
||||
if let Some(config) = self.config.ctrl_queue.as_ref() {
|
||||
if let Err(err) = config.consume_event() {
|
||||
error!("{}: failed to read eventfd, {:?}", self.id, err);
|
||||
} else if let Err(err) = self.process_ctrl_request() {
|
||||
error!(
|
||||
"{}: failed to handle control queue request, {:?}",
|
||||
self.id, err
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => error!("{}: unknown epoll event slot {}", self.id, events.data()),
|
||||
}
|
||||
}
|
||||
|
||||
fn init(&mut self, ops: &mut EventOps) {
|
||||
trace!(target: "vhost-net", "{}: NetEpollHandler::init()", self.id);
|
||||
|
||||
if let Some(config) = self.config.ctrl_queue.as_ref() {
|
||||
let event = Events::with_data(&config.eventfd, CTRL_SLOT, EventSet::IN);
|
||||
if let Err(err) = ops.add(event) {
|
||||
error!(
|
||||
"{}: failed to register epoll event for control queue, {:?}",
|
||||
self.id, err
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use dbs_device::resources::DeviceResources;
|
||||
use dbs_interrupt::{
|
||||
InterruptIndex, InterruptManager, InterruptSourceType, InterruptStatusRegister32,
|
||||
NoopNotifier,
|
||||
};
|
||||
use dbs_utils::epoll_manager::SubscriberOps;
|
||||
use kvm_ioctls::Kvm;
|
||||
use virtio_queue::{Queue, QueueSync};
|
||||
use vm_memory::{GuestAddress, GuestMemoryMmap, GuestRegionMmap};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
use super::*;
|
||||
use crate::tests::create_vm_and_irq_manager;
|
||||
use crate::{create_queue_notifier, VirtioQueueConfig};
|
||||
|
||||
fn create_vhost_kern_net_epoll_handler(
|
||||
id: String,
|
||||
) -> NetEpollHandler<Arc<GuestMemoryMmap>, QueueSync, GuestRegionMmap> {
|
||||
let queues = vec![
|
||||
VirtioQueueConfig::create(128, 0).unwrap(),
|
||||
VirtioQueueConfig::create(128, 0).unwrap(),
|
||||
];
|
||||
let ctrl_queue = VirtioQueueConfig::create(128, 0).unwrap();
|
||||
let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
|
||||
let kvm = Arc::new(Kvm::new().unwrap());
|
||||
let vm_fd = Arc::new(kvm.create_vm().unwrap());
|
||||
let resources = DeviceResources::new();
|
||||
let config = VirtioDeviceConfig::new(
|
||||
Arc::new(mem),
|
||||
vm_fd,
|
||||
resources,
|
||||
queues,
|
||||
Some(ctrl_queue),
|
||||
Arc::new(NoopNotifier::default()),
|
||||
);
|
||||
|
||||
NetEpollHandler { config, id }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vhost_kern_net_virtio_normal() {
|
||||
let guest_mac_str = "11:22:33:44:55:66";
|
||||
let guest_mac = MacAddr::parse_str(guest_mac_str).unwrap();
|
||||
let queue_sizes = Arc::new(vec![128]);
|
||||
let epoll_mgr = EpollManager::default();
|
||||
let mut dev: Net<Arc<GuestMemoryMmap>, QueueSync, GuestRegionMmap> = Net::new(
|
||||
String::from("test_vhosttap"),
|
||||
2,
|
||||
Some(&guest_mac),
|
||||
queue_sizes,
|
||||
epoll_mgr,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(dev.device_type(), TYPE_NET);
|
||||
|
||||
let queue_size = vec![128];
|
||||
assert_eq!(dev.queue_max_sizes(), &queue_size[..]);
|
||||
assert_eq!(
|
||||
dev.get_avail_features(0),
|
||||
dev.device_info.get_avail_features(0)
|
||||
);
|
||||
assert_eq!(
|
||||
dev.get_avail_features(1),
|
||||
dev.device_info.get_avail_features(1)
|
||||
);
|
||||
assert_eq!(
|
||||
dev.get_avail_features(2),
|
||||
dev.device_info.get_avail_features(2)
|
||||
);
|
||||
dev.set_acked_features(2, 0);
|
||||
assert_eq!(dev.get_avail_features(2), 0);
|
||||
let config: [u8; 8] = [0; 8];
|
||||
dev.write_config(0, &config).unwrap();
|
||||
let mut data: [u8; 8] = [1; 8];
|
||||
dev.read_config(0, &mut data).unwrap();
|
||||
assert_eq!(config, data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vhost_kern_net_virtio_activate() {
|
||||
let guest_mac_str = "11:22:33:44:55:66";
|
||||
let guest_mac = MacAddr::parse_str(guest_mac_str).unwrap();
|
||||
// Invalid vq_pairs
|
||||
{
|
||||
let queue_sizes = Arc::new(vec![128, 128]);
|
||||
let epoll_mgr = EpollManager::default();
|
||||
let mut dev: Net<Arc<GuestMemoryMmap>, QueueSync, GuestRegionMmap> = Net::new(
|
||||
String::from("test_vhosttap"),
|
||||
2,
|
||||
Some(&guest_mac),
|
||||
queue_sizes,
|
||||
epoll_mgr,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// The length of queues should be 4.
|
||||
let queues = vec![
|
||||
VirtioQueueConfig::create(128, 0).unwrap(),
|
||||
VirtioQueueConfig::create(128, 0).unwrap(),
|
||||
];
|
||||
|
||||
let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
|
||||
let kvm = Kvm::new().unwrap();
|
||||
let vm_fd = Arc::new(kvm.create_vm().unwrap());
|
||||
let resources = DeviceResources::new();
|
||||
let config = VirtioDeviceConfig::new(
|
||||
Arc::new(mem),
|
||||
vm_fd,
|
||||
resources,
|
||||
queues,
|
||||
None,
|
||||
Arc::new(NoopNotifier::default()),
|
||||
);
|
||||
|
||||
assert!(dev.activate(config).is_err())
|
||||
}
|
||||
// Invalid queue sizes
|
||||
{
|
||||
let queue_sizes = Arc::new(vec![128]);
|
||||
let epoll_mgr = EpollManager::default();
|
||||
let mut dev: Net<Arc<GuestMemoryMmap>, QueueSync, GuestRegionMmap> = Net::new(
|
||||
String::from("test_vhosttap"),
|
||||
1,
|
||||
Some(&guest_mac),
|
||||
queue_sizes,
|
||||
epoll_mgr,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let queues = vec![
|
||||
VirtioQueueConfig::create(128, 0).unwrap(),
|
||||
VirtioQueueConfig::create(128, 0).unwrap(),
|
||||
];
|
||||
|
||||
let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
|
||||
let kvm = Kvm::new().unwrap();
|
||||
let vm_fd = Arc::new(kvm.create_vm().unwrap());
|
||||
let resources = DeviceResources::new();
|
||||
let config = VirtioDeviceConfig::new(
|
||||
Arc::new(mem),
|
||||
vm_fd,
|
||||
resources,
|
||||
queues,
|
||||
None,
|
||||
Arc::new(NoopNotifier::default()),
|
||||
);
|
||||
|
||||
assert!(dev.activate(config).is_err());
|
||||
}
|
||||
// Success
|
||||
{
|
||||
let (_vmfd, irq_manager) = create_vm_and_irq_manager();
|
||||
let group = irq_manager
|
||||
.create_group(InterruptSourceType::LegacyIrq, 0, 1)
|
||||
.unwrap();
|
||||
let status = Arc::new(InterruptStatusRegister32::new());
|
||||
let notifier = create_queue_notifier(group, status, 0u32 as InterruptIndex);
|
||||
let queue: Queue = Queue::new(1024).unwrap();
|
||||
let queue2 = Queue::new(1024).unwrap();
|
||||
let queue_eventfd = Arc::new(EventFd::new(0).unwrap());
|
||||
let queue_eventfd2 = Arc::new(EventFd::new(0).unwrap());
|
||||
let queue_sizes = Arc::new(vec![128, 128]);
|
||||
let epoll_mgr = EpollManager::default();
|
||||
let mut dev: Net<Arc<GuestMemoryMmap>, Queue, GuestRegionMmap> = Net::new(
|
||||
String::from("test_vhosttap"),
|
||||
1,
|
||||
Some(&guest_mac),
|
||||
queue_sizes,
|
||||
epoll_mgr,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let queues = vec![
|
||||
VirtioQueueConfig::new(queue, queue_eventfd, notifier.clone(), 1),
|
||||
VirtioQueueConfig::new(queue2, queue_eventfd2, notifier, 1),
|
||||
];
|
||||
|
||||
let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
|
||||
let kvm = Kvm::new().unwrap();
|
||||
let vm_fd = Arc::new(kvm.create_vm().unwrap());
|
||||
let resources = DeviceResources::new();
|
||||
let config = VirtioDeviceConfig::new(
|
||||
Arc::new(mem),
|
||||
vm_fd,
|
||||
resources,
|
||||
queues,
|
||||
None,
|
||||
Arc::new(NoopNotifier::default()),
|
||||
);
|
||||
|
||||
assert!(dev.activate(config).is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vhost_kern_net_epoll_handler_handle_event() {
|
||||
let handler = create_vhost_kern_net_epoll_handler("test_1".to_string());
|
||||
let event_fd = EventFd::new(0).unwrap();
|
||||
let mgr = EpollManager::default();
|
||||
let id = mgr.add_subscriber(Box::new(handler));
|
||||
let mut inner_mgr = mgr.mgr.lock().unwrap();
|
||||
let mut event_op = inner_mgr.event_ops(id).unwrap();
|
||||
let event_set = EventSet::EDGE_TRIGGERED;
|
||||
let mut handler = create_vhost_kern_net_epoll_handler("test_2".to_string());
|
||||
|
||||
// test for CTRL_SLOT
|
||||
let events = Events::with_data(&event_fd, CTRL_SLOT, event_set);
|
||||
handler.process(events, &mut event_op);
|
||||
handler.config.queues[0].generate_event().unwrap();
|
||||
handler.process(events, &mut event_op);
|
||||
|
||||
// test for unknown event
|
||||
let events = Events::with_data(&event_fd, CTRL_SLOT + 1, event_set);
|
||||
handler.process(events, &mut event_op);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
// Copyright (C) 2021-2023 Alibaba Cloud Computing. All rights reserved.
|
||||
// Copyright (C) 2021-2023 Ant Group. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
|
||||
|
||||
use std::fs::File;
|
||||
use std::os::fd::{AsRawFd, RawFd};
|
||||
|
||||
use vhost_rs::vhost_kern::VhostKernBackend;
|
||||
use vhost_rs::{Error as VhostError, VhostUserMemoryRegionInfo, VringConfigData};
|
||||
use vm_memory::GuestAddressSpace;
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, VhostError>;
|
||||
|
||||
pub struct MockVhostNet<AS: GuestAddressSpace> {
|
||||
pub(crate) fd: i32,
|
||||
pub(crate) mem: AS,
|
||||
}
|
||||
|
||||
impl<AS: GuestAddressSpace> MockVhostNet<AS> {
|
||||
pub fn new(mem: AS) -> Result<Self> {
|
||||
Ok(MockVhostNet { fd: 0, mem })
|
||||
}
|
||||
|
||||
pub fn set_backend(&self, _queue_index: usize, _fd: Option<&File>) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<AS: GuestAddressSpace> VhostKernBackend for MockVhostNet<AS> {
|
||||
type AS = AS;
|
||||
|
||||
fn mem(&self) -> &Self::AS {
|
||||
&self.mem
|
||||
}
|
||||
}
|
||||
|
||||
impl<AS: GuestAddressSpace> AsRawFd for MockVhostNet<AS> {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.fd
|
||||
}
|
||||
}
|
||||
|
||||
pub trait MockVhostBackend: std::marker::Sized {
|
||||
fn get_features(&mut self) -> Result<u64>;
|
||||
fn set_features(&mut self, features: u64) -> Result<()>;
|
||||
fn set_owner(&mut self) -> Result<()>;
|
||||
fn reset_owner(&mut self) -> Result<()>;
|
||||
fn set_mem_table(&mut self, regions: &[VhostUserMemoryRegionInfo]) -> Result<()>;
|
||||
fn set_log_base(&mut self, base: u64, fd: Option<RawFd>) -> Result<()>;
|
||||
fn set_log_fd(&mut self, fd: RawFd) -> Result<()>;
|
||||
fn set_vring_num(&mut self, queue_index: usize, num: u16) -> Result<()>;
|
||||
fn set_vring_addr(&mut self, queue_index: usize, config_data: &VringConfigData) -> Result<()>;
|
||||
fn set_vring_base(&mut self, queue_index: usize, base: u16) -> Result<()>;
|
||||
fn get_vring_base(&mut self, queue_index: usize) -> Result<u32>;
|
||||
fn set_vring_call(&mut self, queue_index: usize, fd: &EventFd) -> Result<()>;
|
||||
fn set_vring_kick(&mut self, queue_index: usize, fd: &EventFd) -> Result<()>;
|
||||
fn set_vring_err(&mut self, queue_index: usize, fd: &EventFd) -> Result<()>;
|
||||
}
|
||||
|
||||
impl<T: VhostKernBackend> MockVhostBackend for T {
|
||||
fn set_owner(&mut self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reset_owner(&mut self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_features(&mut self) -> Result<u64> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn set_features(&mut self, _features: u64) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_mem_table(&mut self, _regions: &[VhostUserMemoryRegionInfo]) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_log_base(&mut self, _base: u64, _fd: Option<RawFd>) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_log_fd(&mut self, _fd: RawFd) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_vring_num(&mut self, _queue_index: usize, _num: u16) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_vring_addr(
|
||||
&mut self,
|
||||
_queue_index: usize,
|
||||
_config_data: &VringConfigData,
|
||||
) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_vring_base(&mut self, _queue_index: usize, _base: u16) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_vring_base(&mut self, _queue_index: usize) -> Result<u32> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn set_vring_call(&mut self, _queue_index: usize, _fd: &EventFd) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_vring_kick(&mut self, _queue_index: usize, _fd: &EventFd) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_vring_err(&mut self, _queue_index: usize, _fd: &EventFd) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -102,6 +102,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;
|
||||
|
||||
macro_rules! info(
|
||||
($l:expr, $($args:tt)+) => {
|
||||
slog::info!($l, $($args)+; slog::o!("subsystem" => "device_manager"))
|
||||
@@ -537,6 +543,9 @@ pub struct DeviceManager {
|
||||
|
||||
#[cfg(feature = "virtio-balloon")]
|
||||
pub(crate) balloon_manager: BalloonDeviceMgr,
|
||||
|
||||
#[cfg(feature = "vhost-net")]
|
||||
vhost_net_manager: VhostNetDeviceMgr,
|
||||
}
|
||||
|
||||
impl DeviceManager {
|
||||
@@ -573,6 +582,8 @@ impl DeviceManager {
|
||||
mem_manager: MemDeviceMgr::default(),
|
||||
#[cfg(feature = "virtio-balloon")]
|
||||
balloon_manager: BalloonDeviceMgr::default(),
|
||||
#[cfg(feature = "vhost-net")]
|
||||
vhost_net_manager: VhostNetDeviceMgr::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -742,6 +753,14 @@ impl DeviceManager {
|
||||
self.block_manager
|
||||
.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)?;
|
||||
|
||||
// Ensure that all devices are attached before kernel boot args are
|
||||
// generated.
|
||||
ctx.generate_kernel_boot_args(kernel_config)
|
||||
.map_err(StartMicroVmError::DeviceManager)?;
|
||||
|
||||
@@ -790,6 +809,9 @@ impl DeviceManager {
|
||||
#[cfg(feature = "virtio-vsock")]
|
||||
self.vsock_manager.remove_devices(&mut ctx)?;
|
||||
|
||||
#[cfg(feature = "vhost-net")]
|
||||
self.vhost_net_manager.remove_devices(&mut ctx)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1144,6 +1166,8 @@ mod tests {
|
||||
balloon_manager: BalloonDeviceMgr::default(),
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
mmio_device_info: HashMap::new(),
|
||||
#[cfg(feature = "vhost-net")]
|
||||
vhost_net_manager: VhostNetDeviceMgr::default(),
|
||||
|
||||
logger,
|
||||
shared_info,
|
||||
|
||||
647
src/dragonball/src/device_manager/vhost_net_dev_mgr.rs
Normal file
647
src/dragonball/src/device_manager/vhost_net_dev_mgr.rs
Normal file
@@ -0,0 +1,647 @@
|
||||
// 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,
|
||||
/// Number of vq pairs to use.
|
||||
pub vq_pairs: 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.vq_pairs,
|
||||
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 % 2 != 0 {
|
||||
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_virtio_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 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() {
|
||||
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,
|
||||
vq_pairs: 0,
|
||||
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() {
|
||||
// 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,
|
||||
vq_pairs: 0,
|
||||
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() {
|
||||
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,
|
||||
vq_pairs: 0,
|
||||
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() {
|
||||
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,
|
||||
vq_pairs: 0,
|
||||
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,
|
||||
vq_pairs: 0,
|
||||
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,
|
||||
vq_pairs: 0,
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -26,9 +26,9 @@ use crate::get_bucket_update;
|
||||
use super::DbsMmioV2Device;
|
||||
|
||||
/// Default number of virtio queues, one rx/tx pair.
|
||||
pub const NUM_QUEUES: usize = 2;
|
||||
pub const DEFAULT_NUM_QUEUES: usize = 2;
|
||||
/// Default size of virtio queues.
|
||||
pub const QUEUE_SIZE: u16 = 256;
|
||||
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.
|
||||
@@ -123,6 +123,7 @@ impl VirtioNetDeviceConfigUpdateInfo {
|
||||
}
|
||||
|
||||
/// 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.
|
||||
@@ -163,12 +164,12 @@ impl VirtioNetDeviceConfigInfo {
|
||||
pub fn queue_sizes(&self) -> Vec<u16> {
|
||||
let mut queue_size = self.queue_size;
|
||||
if queue_size == 0 {
|
||||
queue_size = QUEUE_SIZE;
|
||||
queue_size = DEFAULT_QUEUE_SIZE;
|
||||
}
|
||||
let num_queues = if self.num_queues > 0 {
|
||||
self.num_queues
|
||||
} else {
|
||||
NUM_QUEUES
|
||||
DEFAULT_NUM_QUEUES
|
||||
};
|
||||
|
||||
(0..num_queues).map(|_| queue_size).collect::<Vec<u16>>()
|
||||
|
||||
@@ -193,6 +193,11 @@ 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),
|
||||
}
|
||||
|
||||
/// Errors associated with starting the instance.
|
||||
|
||||
13
src/runtime-rs/Cargo.lock
generated
13
src/runtime-rs/Cargo.lock
generated
@@ -797,6 +797,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"threadpool",
|
||||
"vhost",
|
||||
"virtio-bindings",
|
||||
"virtio-queue",
|
||||
"vm-memory",
|
||||
@@ -4049,6 +4050,18 @@ version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "vhost"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6769e8dbf5276b4376439fbf36bb880d203bf614bf7ef444198edc24b5a9f35"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"libc",
|
||||
"vm-memory",
|
||||
"vmm-sys-util 0.11.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "virt_container"
|
||||
version = "0.1.0"
|
||||
|
||||
@@ -36,7 +36,7 @@ kata-types = { path = "../../../libs/kata-types" }
|
||||
logging = { path = "../../../libs/logging" }
|
||||
shim-interface = { path = "../../../libs/shim-interface" }
|
||||
|
||||
dragonball = { path = "../../../dragonball", features = ["atomic-guest-memory", "virtio-vsock", "hotplug", "virtio-blk", "virtio-net", "virtio-fs", "dbs-upcall"] }
|
||||
dragonball = { path = "../../../dragonball", features = ["atomic-guest-memory", "virtio-vsock", "hotplug", "virtio-blk", "virtio-net", "virtio-fs", "vhost-net", "dbs-upcall"] }
|
||||
|
||||
ch-config = { path = "ch-config", optional = true }
|
||||
tests_utils = { path = "../../tests/utils" }
|
||||
|
||||
@@ -413,7 +413,7 @@ impl TryFrom<ShareFsSettings> for FsConfig {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::Address;
|
||||
use crate::{Address, Backend};
|
||||
|
||||
#[test]
|
||||
fn test_networkconfig_to_netconfig() {
|
||||
@@ -424,6 +424,10 @@ mod tests {
|
||||
queue_num: 2,
|
||||
guest_mac: None,
|
||||
index: 1,
|
||||
allow_duplicate_mac: false,
|
||||
use_generic_irq: None,
|
||||
use_shared_irq: None,
|
||||
backend: Backend::default(),
|
||||
};
|
||||
|
||||
let net = NetConfig::try_from(cfg.clone());
|
||||
|
||||
@@ -307,11 +307,12 @@ impl DeviceManager {
|
||||
}
|
||||
DeviceConfig::NetworkCfg(config) => {
|
||||
// try to find the device, found and just return id.
|
||||
if let Some(dev_id_matched) = self.find_device(config.host_dev_name.clone()).await {
|
||||
let host_path = config.host_dev_name.as_str();
|
||||
if let Some(dev_id_matched) = self.find_device(host_path.to_owned()).await {
|
||||
info!(
|
||||
sl!(),
|
||||
"network device with path:{:?} found. return network device id: {:?}",
|
||||
config.host_dev_name.clone(),
|
||||
host_path,
|
||||
dev_id_matched
|
||||
);
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ pub use virtio_fs::{
|
||||
ShareFsDevice, ShareFsDeviceConfig, ShareFsMountConfig, ShareFsMountDevice, ShareFsMountType,
|
||||
ShareFsOperation,
|
||||
};
|
||||
pub use virtio_net::{Address, NetworkConfig, NetworkDevice};
|
||||
pub use virtio_net::{Address, Backend, NetworkConfig, NetworkDevice};
|
||||
pub use virtio_vsock::{
|
||||
HybridVsockConfig, HybridVsockDevice, VsockConfig, VsockDevice, DEFAULT_GUEST_VSOCK_CID,
|
||||
};
|
||||
|
||||
@@ -9,10 +9,8 @@ use std::fmt;
|
||||
use anyhow::{Context, Result};
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
device::{Device, DeviceType},
|
||||
Hypervisor as hypervisor,
|
||||
};
|
||||
use crate::device::{Device, DeviceType};
|
||||
use crate::Hypervisor as hypervisor;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Address(pub [u8; 6]);
|
||||
@@ -28,25 +26,36 @@ impl fmt::Debug for Address {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub enum Backend {
|
||||
#[default]
|
||||
Virtio,
|
||||
Vhost,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct NetworkConfig {
|
||||
/// for detach, now it's default value 0.
|
||||
pub index: u64,
|
||||
|
||||
/// Network device backend
|
||||
pub backend: Backend,
|
||||
/// Host level path for the guest network interface.
|
||||
pub host_dev_name: String,
|
||||
|
||||
/// Guest iface name for the guest network interface.
|
||||
pub virt_iface_name: String,
|
||||
|
||||
/// Guest MAC address.
|
||||
pub guest_mac: Option<Address>,
|
||||
|
||||
/// Virtio queue size
|
||||
pub queue_size: usize,
|
||||
|
||||
/// Virtio queue num
|
||||
pub queue_num: usize,
|
||||
/// Use shared irq
|
||||
pub use_shared_irq: Option<bool>,
|
||||
/// Use generic irq
|
||||
pub use_generic_irq: Option<bool>,
|
||||
/// Allow duplicate mac
|
||||
pub allow_duplicate_mac: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
|
||||
@@ -7,14 +7,10 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use dbs_utils::net::MacAddr;
|
||||
use dragonball::{
|
||||
api::v1::{
|
||||
BlockDeviceConfigInfo, FsDeviceConfigInfo, FsMountConfigInfo, VirtioNetDeviceConfigInfo,
|
||||
VsockDeviceConfigInfo,
|
||||
},
|
||||
device_manager::blk_dev_mgr::BlockDeviceType,
|
||||
use dragonball::api::v1::{
|
||||
BlockDeviceConfigInfo, FsDeviceConfigInfo, FsMountConfigInfo, VsockDeviceConfigInfo,
|
||||
};
|
||||
use dragonball::device_manager::blk_dev_mgr::BlockDeviceType;
|
||||
|
||||
use super::DragonballInner;
|
||||
use crate::{
|
||||
@@ -85,7 +81,7 @@ impl DragonballInner {
|
||||
// Dragonball doesn't support remove network device, just print message.
|
||||
info!(
|
||||
sl!(),
|
||||
"dragonball remove network device: {:?}.", network.config.virt_iface_name
|
||||
"dragonball remove network device: {:?}.", network.config
|
||||
);
|
||||
|
||||
Ok(())
|
||||
@@ -204,25 +200,8 @@ impl DragonballInner {
|
||||
}
|
||||
|
||||
fn add_net_device(&mut self, config: &NetworkConfig) -> Result<()> {
|
||||
let iface_cfg = VirtioNetDeviceConfigInfo {
|
||||
iface_id: config.virt_iface_name.clone(),
|
||||
host_dev_name: config.host_dev_name.clone(),
|
||||
guest_mac: match &config.guest_mac {
|
||||
Some(mac) => MacAddr::from_bytes(&mac.0).ok(),
|
||||
None => None,
|
||||
},
|
||||
num_queues: config.queue_num,
|
||||
queue_size: config.queue_size as u16,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
info!(
|
||||
sl!(),
|
||||
"add {} endpoint to {}", iface_cfg.host_dev_name, iface_cfg.iface_id
|
||||
);
|
||||
|
||||
self.vmm_instance
|
||||
.insert_network_device(iface_cfg)
|
||||
.insert_network_device(config.into())
|
||||
.context("insert network device")
|
||||
}
|
||||
|
||||
|
||||
@@ -16,12 +16,17 @@ use std::sync::Arc;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use dbs_utils::net::MacAddr as DragonballMacAddr;
|
||||
use dragonball::api::v1::{
|
||||
Backend as DragonballBackend, NetworkInterfaceConfig as DragonballNetworkConfig,
|
||||
VirtioConfig as DragonballVirtioConfig,
|
||||
};
|
||||
use kata_types::capabilities::Capabilities;
|
||||
use kata_types::config::hypervisor::Hypervisor as HypervisorConfig;
|
||||
use tokio::sync::RwLock;
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::{DeviceType, Hypervisor, VcpuThreadIds};
|
||||
use crate::{Backend, DeviceType, Hypervisor, NetworkConfig, VcpuThreadIds};
|
||||
|
||||
pub struct Dragonball {
|
||||
inner: Arc<RwLock<DragonballInner>>,
|
||||
@@ -190,3 +195,42 @@ impl Persist for Dragonball {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NetworkConfig> for DragonballNetworkConfig {
|
||||
fn from(value: NetworkConfig) -> Self {
|
||||
let r = &value;
|
||||
r.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&NetworkConfig> for DragonballNetworkConfig {
|
||||
fn from(value: &NetworkConfig) -> Self {
|
||||
let virtio_config = DragonballVirtioConfig {
|
||||
iface_id: value.virt_iface_name.clone(),
|
||||
host_dev_name: value.host_dev_name.clone(),
|
||||
// TODO(justxuewei): rx_rate_limiter is not supported, see:
|
||||
// https://github.com/kata-containers/kata-containers/issues/8327.
|
||||
rx_rate_limiter: None,
|
||||
// TODO(justxuewei): tx_rate_limiter is not supported, see:
|
||||
// https://github.com/kata-containers/kata-containers/issues/8327.
|
||||
tx_rate_limiter: None,
|
||||
allow_duplicate_mac: value.allow_duplicate_mac,
|
||||
};
|
||||
let backend = match value.backend {
|
||||
Backend::Virtio => DragonballBackend::Virtio(virtio_config),
|
||||
Backend::Vhost => DragonballBackend::Vhost(virtio_config),
|
||||
};
|
||||
|
||||
Self {
|
||||
num_queues: Some(value.queue_num),
|
||||
queue_size: Some(value.queue_size as u16),
|
||||
backend,
|
||||
guest_mac: value.guest_mac.clone().map(|mac| {
|
||||
// We are safety since mac address is checked by endpoints.
|
||||
DragonballMacAddr::from_bytes(&mac.0).unwrap()
|
||||
}),
|
||||
use_shared_irq: value.use_shared_irq,
|
||||
use_generic_irq: value.use_generic_irq,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ use crossbeam_channel::{unbounded, Receiver, Sender};
|
||||
use dragonball::{
|
||||
api::v1::{
|
||||
BlockDeviceConfigInfo, BootSourceConfig, FsDeviceConfigInfo, FsMountConfigInfo,
|
||||
InstanceInfo, InstanceState, VcpuResizeInfo, VirtioNetDeviceConfigInfo, VmmAction,
|
||||
InstanceInfo, InstanceState, NetworkInterfaceConfig, VcpuResizeInfo, VmmAction,
|
||||
VmmActionError, VmmData, VmmRequest, VmmResponse, VmmService, VsockDeviceConfigInfo,
|
||||
},
|
||||
vm::VmConfigInfo,
|
||||
@@ -216,7 +216,7 @@ impl VmmInstance {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn insert_network_device(&self, net_cfg: VirtioNetDeviceConfigInfo) -> Result<()> {
|
||||
pub fn insert_network_device(&self, net_cfg: NetworkInterfaceConfig) -> Result<()> {
|
||||
self.handle_request_with_retry(Request::Sync(VmmAction::InsertNetworkDevice(
|
||||
net_cfg.clone(),
|
||||
)))
|
||||
|
||||
@@ -4,28 +4,19 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
use std::{
|
||||
io::{self, Error},
|
||||
sync::Arc,
|
||||
};
|
||||
use std::io::{self, Error};
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use hypervisor::device::device_manager::{do_handle_device, DeviceManager};
|
||||
use hypervisor::device::driver::NetworkConfig;
|
||||
use hypervisor::device::{DeviceConfig, DeviceType};
|
||||
use hypervisor::{Backend, Hypervisor, NetworkDevice};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use hypervisor::{
|
||||
device::{
|
||||
device_manager::{do_handle_device, DeviceManager},
|
||||
driver::NetworkConfig,
|
||||
DeviceConfig, DeviceType,
|
||||
},
|
||||
Hypervisor, NetworkDevice,
|
||||
};
|
||||
|
||||
use super::{
|
||||
endpoint_persist::{EndpointState, IpVlanEndpointState},
|
||||
Endpoint,
|
||||
};
|
||||
use super::endpoint_persist::{EndpointState, IpVlanEndpointState};
|
||||
use super::Endpoint;
|
||||
use crate::network::{network_model::TC_FILTER_NET_MODEL_STR, utils, NetworkPair};
|
||||
|
||||
// IPVlanEndpoint is the endpoint bridged to VM
|
||||
@@ -66,6 +57,7 @@ impl IPVlanEndpoint {
|
||||
Ok(NetworkConfig {
|
||||
host_dev_name: iface.name.clone(),
|
||||
virt_iface_name: self.net_pair.virt_iface.name.clone(),
|
||||
backend: Backend::Virtio,
|
||||
guest_mac: Some(guest_mac),
|
||||
..Default::default()
|
||||
})
|
||||
|
||||
@@ -4,28 +4,19 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
use std::{
|
||||
io::{self, Error},
|
||||
sync::Arc,
|
||||
};
|
||||
use std::io::{self, Error};
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use hypervisor::device::device_manager::{do_handle_device, DeviceManager};
|
||||
use hypervisor::device::driver::NetworkConfig;
|
||||
use hypervisor::device::{DeviceConfig, DeviceType};
|
||||
use hypervisor::{Backend, Hypervisor, NetworkDevice};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use hypervisor::{
|
||||
device::{
|
||||
device_manager::{do_handle_device, DeviceManager},
|
||||
driver::NetworkConfig,
|
||||
DeviceConfig, DeviceType,
|
||||
},
|
||||
Hypervisor, NetworkDevice,
|
||||
};
|
||||
|
||||
use super::{
|
||||
endpoint_persist::{EndpointState, MacvlanEndpointState},
|
||||
Endpoint,
|
||||
};
|
||||
use super::endpoint_persist::{EndpointState, MacvlanEndpointState};
|
||||
use super::Endpoint;
|
||||
use crate::network::{utils, NetworkPair};
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -65,6 +56,7 @@ impl MacVlanEndpoint {
|
||||
Ok(NetworkConfig {
|
||||
host_dev_name: iface.name.clone(),
|
||||
virt_iface_name: self.net_pair.virt_iface.name.clone(),
|
||||
backend: Backend::Virtio,
|
||||
guest_mac: Some(guest_mac),
|
||||
..Default::default()
|
||||
})
|
||||
|
||||
@@ -10,7 +10,7 @@ use anyhow::{Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use hypervisor::device::device_manager::{do_handle_device, DeviceManager};
|
||||
use hypervisor::device::{DeviceConfig, DeviceType};
|
||||
use hypervisor::{Hypervisor, NetworkConfig, NetworkDevice};
|
||||
use hypervisor::{Backend, Hypervisor, NetworkConfig, NetworkDevice};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use super::endpoint_persist::TapEndpointState;
|
||||
@@ -76,6 +76,7 @@ impl TapEndpoint {
|
||||
Ok(NetworkConfig {
|
||||
host_dev_name: self.tap_iface.name.clone(),
|
||||
virt_iface_name: self.name.clone(),
|
||||
backend: Backend::Virtio,
|
||||
guest_mac: Some(guest_mac),
|
||||
queue_num: self.queue_num,
|
||||
queue_size: self.queue_size,
|
||||
|
||||
@@ -4,28 +4,19 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
use std::{
|
||||
io::{self, Error},
|
||||
sync::Arc,
|
||||
};
|
||||
use std::io::{self, Error};
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use hypervisor::device::device_manager::{do_handle_device, DeviceManager};
|
||||
use hypervisor::device::driver::NetworkConfig;
|
||||
use hypervisor::device::{DeviceConfig, DeviceType};
|
||||
use hypervisor::{Backend, Hypervisor, NetworkDevice};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use hypervisor::{
|
||||
device::{
|
||||
device_manager::{do_handle_device, DeviceManager},
|
||||
driver::NetworkConfig,
|
||||
DeviceConfig, DeviceType,
|
||||
},
|
||||
Hypervisor, NetworkDevice,
|
||||
};
|
||||
|
||||
use super::{
|
||||
endpoint_persist::{EndpointState, VethEndpointState},
|
||||
Endpoint,
|
||||
};
|
||||
use super::endpoint_persist::{EndpointState, VethEndpointState};
|
||||
use super::Endpoint;
|
||||
use crate::network::{utils, NetworkPair};
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -65,6 +56,7 @@ impl VethEndpoint {
|
||||
Ok(NetworkConfig {
|
||||
host_dev_name: iface.name.clone(),
|
||||
virt_iface_name: self.net_pair.virt_iface.name.clone(),
|
||||
backend: Backend::Virtio,
|
||||
guest_mac: Some(guest_mac),
|
||||
..Default::default()
|
||||
})
|
||||
|
||||
@@ -4,29 +4,21 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
use std::{
|
||||
io::{self, Error},
|
||||
sync::Arc,
|
||||
};
|
||||
use std::io::{self, Error};
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use hypervisor::device::device_manager::{do_handle_device, DeviceManager};
|
||||
use hypervisor::device::driver::NetworkConfig;
|
||||
use hypervisor::device::{DeviceConfig, DeviceType};
|
||||
use hypervisor::{Backend, Hypervisor, NetworkDevice};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use hypervisor::{
|
||||
device::{
|
||||
device_manager::{do_handle_device, DeviceManager},
|
||||
driver::NetworkConfig,
|
||||
DeviceConfig, DeviceType,
|
||||
},
|
||||
Hypervisor, NetworkDevice,
|
||||
};
|
||||
|
||||
use super::{
|
||||
endpoint_persist::{EndpointState, VlanEndpointState},
|
||||
Endpoint,
|
||||
};
|
||||
use crate::network::{network_model::TC_FILTER_NET_MODEL_STR, utils, NetworkPair};
|
||||
use super::endpoint_persist::{EndpointState, VlanEndpointState};
|
||||
use super::Endpoint;
|
||||
use crate::network::network_model::TC_FILTER_NET_MODEL_STR;
|
||||
use crate::network::{utils, NetworkPair};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct VlanEndpoint {
|
||||
@@ -64,6 +56,7 @@ impl VlanEndpoint {
|
||||
Ok(NetworkConfig {
|
||||
host_dev_name: iface.name.clone(),
|
||||
virt_iface_name: self.net_pair.virt_iface.name.clone(),
|
||||
backend: Backend::Virtio,
|
||||
guest_mac: Some(guest_mac),
|
||||
..Default::default()
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user