1
0
mirror of https://github.com/kata-containers/kata-containers.git synced 2025-05-07 16:07:37 +00:00

agent: Wait for interface in update_interface

For nerdctl and docker runtimes, network is hot-plugged instead of
cold-plugged. While this change was made in the runtime,
we did not have the agent waiting for the device to be ready.
On some systems, the device hotplug could take some time causing
the update_interface rpc call to fail as the interface is not available.

Add a watcher for the network interface based on the pci-path of the
network interface. Note, waiting on the device based on name is really
not reliable especially in case multiple networks are hotplugged.

Signed-off-by: Archana Shinde <archana.m.shinde@intel.com>
This commit is contained in:
Archana Shinde 2024-06-25 00:21:16 -07:00
parent 82a1892d34
commit 49fbae4fb1
3 changed files with 110 additions and 1 deletions

View File

@ -379,6 +379,65 @@ pub async fn wait_for_pci_device(
Ok(addr)
}
#[derive(Debug)]
struct NetPciMatcher {
devpath: String,
}
impl NetPciMatcher {
fn new(relpath: &str) -> NetPciMatcher {
let root_bus = create_pci_root_bus_path();
NetPciMatcher {
devpath: format!("{}{}", root_bus, relpath),
}
}
}
impl UeventMatcher for NetPciMatcher {
fn is_match(&self, uev: &Uevent) -> bool {
uev.devpath.starts_with(self.devpath.as_str())
&& uev.subsystem == "net"
&& !uev.interface.is_empty()
&& uev.action == "add"
}
}
pub async fn wait_for_net_interface(
sandbox: &Arc<Mutex<Sandbox>>,
pcipath: &pci::Path,
) -> Result<()> {
let root_bus_sysfs = format!("{}{}", SYSFS_DIR, create_pci_root_bus_path());
let sysfs_rel_path = pcipath_to_sysfs(&root_bus_sysfs, pcipath)?;
let matcher = NetPciMatcher::new(&sysfs_rel_path);
// Check if the interface is already added in case network is cold-plugged
// or the uevent loop is started before network is added.
// We check for the pci deive in the sysfs directory for network devices.
let pattern = format!(
r"[./]+{}/[a-z0-9/]*net/[a-z0-9/]*",
matcher.devpath.as_str()
);
let re = Regex::new(&pattern).expect("BUG: Failed to compile regex for NetPciMatcher");
for entry in fs::read_dir(SYSFS_NET_PATH)? {
let entry = entry?;
let path = entry.path();
let target_path = fs::read_link(path)?;
let target_path_str = target_path
.to_str()
.ok_or_else(|| anyhow!("Expected symlink in dir {}", SYSFS_NET_PATH))?;
if re.is_match(target_path_str) {
return Ok(());
}
}
let _uev = wait_for_uevent(sandbox, matcher).await?;
Ok(())
}
#[derive(Debug)]
struct VfioMatcher {
syspath: String,
@ -1691,6 +1750,41 @@ mod tests {
assert!(!matcher_a.is_match(&uev_b));
}
#[tokio::test]
#[allow(clippy::redundant_clone)]
async fn test_net_pci_matcher() {
let root_bus = create_pci_root_bus_path();
let relpath_a = "/0000:00:02.0/0000:01:01.0";
let mut uev_a = crate::uevent::Uevent::default();
uev_a.action = crate::linux_abi::U_EVENT_ACTION_ADD.to_string();
uev_a.devpath = format!("{}{}", root_bus, relpath_a);
uev_a.subsystem = String::from("net");
uev_a.interface = String::from("eth0");
let matcher_a = NetPciMatcher::new(relpath_a);
println!("Matcher a : {}", matcher_a.devpath);
let relpath_b = "/0000:00:02.0/0000:01:02.0";
let mut uev_b = uev_a.clone();
uev_b.devpath = format!("{}{}", root_bus, relpath_b);
let matcher_b = NetPciMatcher::new(relpath_b);
assert!(matcher_a.is_match(&uev_a));
assert!(matcher_b.is_match(&uev_b));
assert!(!matcher_b.is_match(&uev_a));
assert!(!matcher_a.is_match(&uev_b));
let relpath_c = "/0000:00:02.0/0000:01:03.0";
let net_substr = "/net/eth0";
let mut uev_c = uev_a.clone();
uev_c.devpath = format!("{}{}{}", root_bus, relpath_c, net_substr);
let matcher_c = NetPciMatcher::new(relpath_c);
assert!(matcher_c.is_match(&uev_c));
assert!(!matcher_a.is_match(&uev_c));
assert!(!matcher_b.is_match(&uev_c));
}
#[tokio::test]
#[allow(clippy::redundant_clone)]
async fn test_mmio_block_matcher() {

View File

@ -89,6 +89,7 @@ pub const SYSFS_MEMORY_HOTPLUG_PROBE_PATH: &str = "/sys/devices/system/memory/pr
pub const SYSFS_MEMORY_ONLINE_PATH: &str = "/sys/devices/system/memory";
pub const SYSFS_SCSI_HOST_PATH: &str = "/sys/class/scsi_host";
pub const SYSFS_NET_PATH: &str = "/sys/class/net";
pub const SYSFS_BUS_PCI_PATH: &str = "/sys/bus/pci";

View File

@ -13,6 +13,7 @@ use std::fmt::Debug;
use std::io;
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use std::str::FromStr;
use std::sync::Arc;
use ttrpc::{
self,
@ -52,7 +53,9 @@ use nix::sys::{stat, statfs};
use nix::unistd::{self, Pid};
use rustjail::process::ProcessOperations;
use crate::device::{add_devices, get_virtio_blk_pci_device_name, update_env_pci};
use crate::device::{
add_devices, get_virtio_blk_pci_device_name, update_env_pci, wait_for_net_interface,
};
use crate::features::get_build_features;
use crate::linux_abi::*;
use crate::metrics::get_metrics;
@ -915,6 +918,17 @@ impl agent_ttrpc::AgentService for AgentService {
"empty update interface request",
)?;
// For network devices passed on the pci bus, check for the network interface
// to be available first.
if !interface.pciPath.is_empty() {
let pcipath = pci::Path::from_str(&interface.pciPath)
.map_ttrpc_err(|e| format!("Unexpected pci-path for network interface: {:?}", e))?;
wait_for_net_interface(&self.sandbox, &pcipath)
.await
.map_ttrpc_err(|e| format!("interface not available: {:?}", e))?;
}
self.sandbox
.lock()
.await