mirror of
https://github.com/kata-containers/kata-containers.git
synced 2026-07-25 22:49:59 +00:00
runtime-rs: Introduce queues for DanConfig to make it configurable
As there's no field to map the configuration's network_queues item in the `DanConfig`, this commit introduces a network_queues to do this. And accordingly, we also make it passed down from sandbox layer to Dan network configurations. To make it work well, it make it more robust for queues and queue_size settings with checking logics. And related UT is added. Signed-off-by: Alex Lyn <alex.lyn@antgroup.com>
This commit is contained in:
@@ -68,8 +68,8 @@ impl DanInner {
|
||||
let json_str = fs::read_to_string(&config.dan_conf_path)
|
||||
.await
|
||||
.context("Read DAN config from file")?;
|
||||
let config: DanConfig = serde_json::from_str(&json_str).context("Invalid DAN config")?;
|
||||
info!(sl!(), "Dan config is loaded = {:?}", config);
|
||||
let dan_config: DanConfig = serde_json::from_str(&json_str).context("Invalid DAN config")?;
|
||||
info!(sl!(), "Dan config is loaded = {:?}", dan_config);
|
||||
|
||||
let (connection, handle, _) = rtnetlink::new_connection().context("New connection")?;
|
||||
let thread_handler = tokio::spawn(connection);
|
||||
@@ -77,43 +77,45 @@ impl DanInner {
|
||||
thread_handler.abort();
|
||||
});
|
||||
|
||||
let mut entity_list = Vec::with_capacity(config.devices.len());
|
||||
for (idx, device) in config.devices.iter().enumerate() {
|
||||
let mut entity_list = Vec::with_capacity(dan_config.devices.len());
|
||||
for (idx, device) in dan_config.devices.iter().enumerate() {
|
||||
let name = format!("eth{idx}");
|
||||
// The `network_queues` is a queue *pair* count.
|
||||
// Keep `queue_num` as a pair count and the hypervisor backend converts pairs into the actual virtqueue count.
|
||||
// A JSON-provided non-zero `queue_num` (also a pair count) with a higher priority always wins.
|
||||
let (qnum, qsize) = device
|
||||
.device
|
||||
.get_effective_queues(config.network_queues);
|
||||
let endpoint: Arc<dyn Endpoint> = match &device.device {
|
||||
Device::VhostUser {
|
||||
path,
|
||||
queue_num,
|
||||
queue_size,
|
||||
} => Arc::new(
|
||||
VhostUserEndpoint::new(
|
||||
dev_mgr,
|
||||
&name,
|
||||
&device.guest_mac,
|
||||
path,
|
||||
*queue_num,
|
||||
*queue_size,
|
||||
Device::VhostUser { path, .. } => {
|
||||
Arc::new(
|
||||
VhostUserEndpoint::new(
|
||||
dev_mgr,
|
||||
&name,
|
||||
&device.guest_mac,
|
||||
path,
|
||||
qnum,
|
||||
qsize,
|
||||
)
|
||||
.await
|
||||
.with_context(|| format!("create a vhost user endpoint, path: {path}"))?,
|
||||
)
|
||||
.await
|
||||
.with_context(|| format!("create a vhost user endpoint, path: {path}"))?,
|
||||
),
|
||||
Device::HostTap {
|
||||
tap_name,
|
||||
queue_num,
|
||||
queue_size,
|
||||
} => Arc::new(
|
||||
TapEndpoint::new(
|
||||
&handle,
|
||||
&name,
|
||||
tap_name,
|
||||
&device.guest_mac,
|
||||
*queue_num,
|
||||
*queue_size,
|
||||
dev_mgr,
|
||||
}
|
||||
Device::HostTap { tap_name, .. } => {
|
||||
Arc::new(
|
||||
TapEndpoint::new(
|
||||
&handle,
|
||||
&name,
|
||||
tap_name,
|
||||
&device.guest_mac,
|
||||
qnum,
|
||||
qsize,
|
||||
dev_mgr,
|
||||
)
|
||||
.await
|
||||
.with_context(|| format!("create a {tap_name} tap endpoint"))?,
|
||||
)
|
||||
.await
|
||||
.with_context(|| format!("create a {tap_name} tap endpoint"))?,
|
||||
),
|
||||
}
|
||||
};
|
||||
|
||||
let network_info = Arc::new(
|
||||
@@ -129,7 +131,7 @@ impl DanInner {
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
netns: config.netns,
|
||||
netns: dan_config.netns,
|
||||
entity_list,
|
||||
})
|
||||
}
|
||||
@@ -211,6 +213,9 @@ impl Network for Dan {
|
||||
#[derive(Debug)]
|
||||
pub struct DanNetworkConfig {
|
||||
pub dan_conf_path: PathBuf,
|
||||
/// Number of virtio queue pairs (each pair = 1 RX + 1 TX).
|
||||
/// Derived from `network_queues` in the hypervisor TOML config.
|
||||
pub network_queues: usize,
|
||||
}
|
||||
|
||||
/// Directly attachable network config written by CNI plugins
|
||||
@@ -259,6 +264,34 @@ pub(crate) enum Device {
|
||||
},
|
||||
}
|
||||
|
||||
impl Device {
|
||||
/// get the effective queue-pair count and queue size.
|
||||
pub(crate) fn get_effective_queues(&self, network_queues: usize) -> (usize, usize) {
|
||||
// The `network_queues` comes from hypervisor configurations, and we need to ensure that it is at least 1,
|
||||
// otherwise the network device will not work.
|
||||
let network_queues = network_queues.max(1);
|
||||
let (queue_num, queue_size) = match self {
|
||||
Device::VhostUser {
|
||||
queue_num,
|
||||
queue_size,
|
||||
..
|
||||
}
|
||||
| Device::HostTap {
|
||||
queue_num,
|
||||
queue_size,
|
||||
..
|
||||
} => (queue_num, queue_size),
|
||||
};
|
||||
let qnum = if *queue_num == 0 {
|
||||
network_queues
|
||||
} else {
|
||||
*queue_num
|
||||
};
|
||||
let qsize = if *queue_size == 0 { 256 } else { *queue_size };
|
||||
(qnum, qsize)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub(crate) struct NetworkInfo {
|
||||
pub(crate) interface: Interface,
|
||||
|
||||
@@ -17,6 +17,7 @@ mod tests {
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::network::{
|
||||
dan::Device,
|
||||
endpoint::{IPVlanEndpoint, MacVlanEndpoint, VlanEndpoint},
|
||||
network_model::{
|
||||
self,
|
||||
@@ -410,4 +411,55 @@ mod tests {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DAN regression test for the minimum-of-1 requirement.
|
||||
#[test]
|
||||
fn test_dan_device_get_effective_queues_min_one() {
|
||||
// Test `network_queues` of 0 to the minimum of 1.
|
||||
let default_network_queues = 0_usize;
|
||||
|
||||
// VhostUser effective pair count falls back to default (1),
|
||||
// queue size falls back to 256.
|
||||
let vhost = Device::VhostUser {
|
||||
path: "/tmp/test".to_owned(),
|
||||
queue_num: 0,
|
||||
queue_size: 0,
|
||||
};
|
||||
assert_eq!(
|
||||
vhost.get_effective_queues(default_network_queues),
|
||||
(1, 256)
|
||||
);
|
||||
|
||||
// HostTap fallback default behaviour.
|
||||
let tap = Device::HostTap {
|
||||
tap_name: "tap0".to_owned(),
|
||||
queue_num: 0,
|
||||
queue_size: 0,
|
||||
};
|
||||
assert_eq!(tap.get_effective_queues(default_network_queues), (1, 256));
|
||||
|
||||
// This catches a regression that would always return 1.
|
||||
let vhost_default4 = Device::VhostUser {
|
||||
path: "/tmp/test".to_owned(),
|
||||
queue_num: 0,
|
||||
queue_size: 0,
|
||||
};
|
||||
assert_eq!(vhost_default4.get_effective_queues(4), (4, 256));
|
||||
|
||||
// A non-zero `queue_num` from the JSON always wins over the default,
|
||||
// even when it equals the minimum — and is never reported as 0.
|
||||
let vhost_nonzero = Device::VhostUser {
|
||||
path: "/tmp/test".to_owned(),
|
||||
queue_num: 1,
|
||||
queue_size: 0,
|
||||
};
|
||||
assert_eq!(vhost_nonzero.get_effective_queues(default_network_queues), (1, 256));
|
||||
|
||||
let tap_explicit = Device::HostTap {
|
||||
tap_name: "tap0".to_owned(),
|
||||
queue_num: 7,
|
||||
queue_size: 512,
|
||||
};
|
||||
assert_eq!(tap_explicit.get_effective_queues(default_network_queues), (7, 512));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -549,6 +549,12 @@ impl VirtSandbox {
|
||||
Some(ResourceConfig::Network(NetworkConfig::Dan(
|
||||
DanNetworkConfig {
|
||||
dan_conf_path: dan_path,
|
||||
network_queues: self
|
||||
.hypervisor
|
||||
.hypervisor_config()
|
||||
.await
|
||||
.network_info
|
||||
.network_queues as usize,
|
||||
},
|
||||
)))
|
||||
} else if let Some(netns_path) = network_env.netns.as_ref() {
|
||||
|
||||
Reference in New Issue
Block a user