mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-07-19 09:51:29 +00:00
Merge pull request #10213 from ChengyuZhu6/device
Refine device management for kata-agent
This commit is contained in:
commit
3a37652d01
File diff suppressed because it is too large
Load Diff
342
src/agent/src/device/block_device_handler.rs
Normal file
342
src/agent/src/device/block_device_handler.rs
Normal file
@ -0,0 +1,342 @@
|
|||||||
|
// Copyright (c) 2019 Ant Financial
|
||||||
|
// Copyright (c) 2024 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
#[cfg(target_arch = "s390x")]
|
||||||
|
use crate::ccw;
|
||||||
|
use crate::device::{
|
||||||
|
pcipath_to_sysfs, DeviceContext, DeviceHandler, DeviceInfo, SpecUpdate, BLOCK,
|
||||||
|
};
|
||||||
|
#[cfg(target_arch = "s390x")]
|
||||||
|
use crate::linux_abi::CCW_ROOT_BUS_PATH;
|
||||||
|
use crate::linux_abi::{create_pci_root_bus_path, SYSFS_DIR, SYSTEM_DEV_PATH};
|
||||||
|
use crate::pci;
|
||||||
|
use crate::sandbox::Sandbox;
|
||||||
|
use crate::uevent::{wait_for_uevent, Uevent, UeventMatcher};
|
||||||
|
use anyhow::{anyhow, Context, Result};
|
||||||
|
use kata_types::device::{DRIVER_BLK_CCW_TYPE, DRIVER_BLK_MMIO_TYPE, DRIVER_BLK_PCI_TYPE};
|
||||||
|
use protocols::agent::Device;
|
||||||
|
use regex::Regex;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
use tracing::instrument;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct VirtioBlkPciDeviceHandler {}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct VirtioBlkCcwDeviceHandler {}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct VirtioBlkMmioDeviceHandler {}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl DeviceHandler for VirtioBlkPciDeviceHandler {
|
||||||
|
#[instrument]
|
||||||
|
fn driver_types(&self) -> &[&str] {
|
||||||
|
&[DRIVER_BLK_PCI_TYPE]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument]
|
||||||
|
async fn device_handler(&self, device: &Device, ctx: &mut DeviceContext) -> Result<SpecUpdate> {
|
||||||
|
let pcipath = pci::Path::from_str(&device.id)?;
|
||||||
|
let vm_path = get_virtio_blk_pci_device_name(ctx.sandbox, &pcipath).await?;
|
||||||
|
|
||||||
|
Ok(DeviceInfo::new(&vm_path, true)
|
||||||
|
.context("New device info")?
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl DeviceHandler for VirtioBlkCcwDeviceHandler {
|
||||||
|
#[instrument]
|
||||||
|
fn driver_types(&self) -> &[&str] {
|
||||||
|
&[DRIVER_BLK_CCW_TYPE]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "s390x")]
|
||||||
|
#[instrument]
|
||||||
|
async fn device_handler(&self, device: &Device, ctx: &mut DeviceContext) -> Result<SpecUpdate> {
|
||||||
|
let ccw_device = ccw::Device::from_str(&device.id)?;
|
||||||
|
let vm_path = get_virtio_blk_ccw_device_name(ctx.sandbox, &ccw_device).await?;
|
||||||
|
|
||||||
|
Ok(DeviceInfo::new(&vm_path, true)
|
||||||
|
.context("New device info")?
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "s390x"))]
|
||||||
|
async fn device_handler(
|
||||||
|
&self,
|
||||||
|
_device: &Device,
|
||||||
|
_ctx: &mut DeviceContext,
|
||||||
|
) -> Result<SpecUpdate> {
|
||||||
|
Err(anyhow!("CCW is only supported on s390x"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl DeviceHandler for VirtioBlkMmioDeviceHandler {
|
||||||
|
#[instrument]
|
||||||
|
fn driver_types(&self) -> &[&str] {
|
||||||
|
&[DRIVER_BLK_MMIO_TYPE]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument]
|
||||||
|
async fn device_handler(&self, device: &Device, ctx: &mut DeviceContext) -> Result<SpecUpdate> {
|
||||||
|
if device.vm_path.is_empty() {
|
||||||
|
return Err(anyhow!("Invalid path for virtio mmio blk device"));
|
||||||
|
}
|
||||||
|
if !Path::new(&device.vm_path).exists() {
|
||||||
|
get_virtio_blk_mmio_device_name(ctx.sandbox, &device.vm_path.to_string())
|
||||||
|
.await
|
||||||
|
.context("failed to get mmio device name")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(DeviceInfo::new(device.vm_path(), true)
|
||||||
|
.context("New device info")?
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument]
|
||||||
|
pub async fn get_virtio_blk_pci_device_name(
|
||||||
|
sandbox: &Arc<Mutex<Sandbox>>,
|
||||||
|
pcipath: &pci::Path,
|
||||||
|
) -> Result<String> {
|
||||||
|
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 = VirtioBlkPciMatcher::new(&sysfs_rel_path);
|
||||||
|
|
||||||
|
let uev = wait_for_uevent(sandbox, matcher).await?;
|
||||||
|
Ok(format!("{}/{}", SYSTEM_DEV_PATH, &uev.devname))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument]
|
||||||
|
pub async fn get_virtio_blk_mmio_device_name(
|
||||||
|
sandbox: &Arc<Mutex<Sandbox>>,
|
||||||
|
devpath: &str,
|
||||||
|
) -> Result<()> {
|
||||||
|
let devname = devpath
|
||||||
|
.strip_prefix("/dev/")
|
||||||
|
.ok_or_else(|| anyhow!("Storage source '{}' must start with /dev/", devpath))?;
|
||||||
|
|
||||||
|
let matcher = VirtioBlkMmioMatcher::new(devname);
|
||||||
|
let uev = wait_for_uevent(sandbox, matcher)
|
||||||
|
.await
|
||||||
|
.context("failed to wait for uevent")?;
|
||||||
|
if uev.devname != devname {
|
||||||
|
return Err(anyhow!(
|
||||||
|
"Unexpected device name {} for mmio device (expected {})",
|
||||||
|
uev.devname,
|
||||||
|
devname
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "s390x")]
|
||||||
|
#[instrument]
|
||||||
|
pub async fn get_virtio_blk_ccw_device_name(
|
||||||
|
sandbox: &Arc<Mutex<Sandbox>>,
|
||||||
|
device: &ccw::Device,
|
||||||
|
) -> Result<String> {
|
||||||
|
let matcher = VirtioBlkCCWMatcher::new(CCW_ROOT_BUS_PATH, device);
|
||||||
|
let uev = wait_for_uevent(sandbox, matcher).await?;
|
||||||
|
let devname = uev.devname;
|
||||||
|
Path::new(SYSTEM_DEV_PATH)
|
||||||
|
.join(&devname)
|
||||||
|
.to_str()
|
||||||
|
.map(String::from)
|
||||||
|
.ok_or_else(|| anyhow!("CCW device name {} is not valid UTF-8", &devname))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct VirtioBlkPciMatcher {
|
||||||
|
rex: Regex,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VirtioBlkPciMatcher {
|
||||||
|
pub fn new(relpath: &str) -> VirtioBlkPciMatcher {
|
||||||
|
let root_bus = create_pci_root_bus_path();
|
||||||
|
let re = format!(r"^{}{}/virtio[0-9]+/block/", root_bus, relpath);
|
||||||
|
|
||||||
|
VirtioBlkPciMatcher {
|
||||||
|
rex: Regex::new(&re).expect("BUG: failed to compile VirtioBlkPciMatcher regex"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UeventMatcher for VirtioBlkPciMatcher {
|
||||||
|
fn is_match(&self, uev: &Uevent) -> bool {
|
||||||
|
uev.subsystem == BLOCK && self.rex.is_match(&uev.devpath) && !uev.devname.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct VirtioBlkMmioMatcher {
|
||||||
|
suffix: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VirtioBlkMmioMatcher {
|
||||||
|
pub fn new(devname: &str) -> VirtioBlkMmioMatcher {
|
||||||
|
VirtioBlkMmioMatcher {
|
||||||
|
suffix: format!(r"/block/{}", devname),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UeventMatcher for VirtioBlkMmioMatcher {
|
||||||
|
fn is_match(&self, uev: &Uevent) -> bool {
|
||||||
|
uev.subsystem == BLOCK && uev.devpath.ends_with(&self.suffix) && !uev.devname.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "s390x")]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct VirtioBlkCCWMatcher {
|
||||||
|
rex: Regex,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "s390x")]
|
||||||
|
impl VirtioBlkCCWMatcher {
|
||||||
|
pub fn new(root_bus_path: &str, device: &ccw::Device) -> Self {
|
||||||
|
let re = format!(
|
||||||
|
r"^{}/0\.[0-3]\.[0-9a-f]{{1,4}}/{}/virtio[0-9]+/block/",
|
||||||
|
root_bus_path, device
|
||||||
|
);
|
||||||
|
VirtioBlkCCWMatcher {
|
||||||
|
rex: Regex::new(&re).expect("BUG: failed to compile VirtioBlkCCWMatcher regex"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "s390x")]
|
||||||
|
impl UeventMatcher for VirtioBlkCCWMatcher {
|
||||||
|
fn is_match(&self, uev: &Uevent) -> bool {
|
||||||
|
uev.action == "add" && self.rex.is_match(&uev.devpath) && !uev.devname.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[allow(clippy::redundant_clone)]
|
||||||
|
async fn test_virtio_blk_matcher() {
|
||||||
|
let root_bus = create_pci_root_bus_path();
|
||||||
|
let devname = "vda";
|
||||||
|
|
||||||
|
let mut uev_a = crate::uevent::Uevent::default();
|
||||||
|
let relpath_a = "/0000:00:0a.0";
|
||||||
|
uev_a.action = crate::linux_abi::U_EVENT_ACTION_ADD.to_string();
|
||||||
|
uev_a.subsystem = BLOCK.to_string();
|
||||||
|
uev_a.devname = devname.to_string();
|
||||||
|
uev_a.devpath = format!("{}{}/virtio4/block/{}", root_bus, relpath_a, devname);
|
||||||
|
let matcher_a = VirtioBlkPciMatcher::new(relpath_a);
|
||||||
|
|
||||||
|
let mut uev_b = uev_a.clone();
|
||||||
|
let relpath_b = "/0000:00:0a.0/0000:00:0b.0";
|
||||||
|
uev_b.devpath = format!("{}{}/virtio0/block/{}", root_bus, relpath_b, devname);
|
||||||
|
let matcher_b = VirtioBlkPciMatcher::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));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "s390x")]
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_virtio_blk_ccw_matcher() {
|
||||||
|
let root_bus = CCW_ROOT_BUS_PATH;
|
||||||
|
let subsystem = "block";
|
||||||
|
let devname = "vda";
|
||||||
|
let relpath = "0.0.0002";
|
||||||
|
|
||||||
|
let mut uev = crate::uevent::Uevent::default();
|
||||||
|
uev.action = crate::linux_abi::U_EVENT_ACTION_ADD.to_string();
|
||||||
|
uev.subsystem = subsystem.to_string();
|
||||||
|
uev.devname = devname.to_string();
|
||||||
|
uev.devpath = format!(
|
||||||
|
"{}/0.0.0001/{}/virtio1/{}/{}",
|
||||||
|
root_bus, relpath, subsystem, devname
|
||||||
|
);
|
||||||
|
|
||||||
|
// Valid path
|
||||||
|
let device = ccw::Device::from_str(relpath).unwrap();
|
||||||
|
let matcher = VirtioBlkCCWMatcher::new(root_bus, &device);
|
||||||
|
assert!(matcher.is_match(&uev));
|
||||||
|
|
||||||
|
// Invalid paths
|
||||||
|
uev.devpath = format!(
|
||||||
|
"{}/0.0.0001/0.0.0003/virtio1/{}/{}",
|
||||||
|
root_bus, subsystem, devname
|
||||||
|
);
|
||||||
|
assert!(!matcher.is_match(&uev));
|
||||||
|
|
||||||
|
uev.devpath = format!("0.0.0001/{}/virtio1/{}/{}", relpath, subsystem, devname);
|
||||||
|
assert!(!matcher.is_match(&uev));
|
||||||
|
|
||||||
|
uev.devpath = format!(
|
||||||
|
"{}/0.0.0001/{}/virtio/{}/{}",
|
||||||
|
root_bus, relpath, subsystem, devname
|
||||||
|
);
|
||||||
|
assert!(!matcher.is_match(&uev));
|
||||||
|
|
||||||
|
uev.devpath = format!("{}/0.0.0001/{}/virtio1", root_bus, relpath);
|
||||||
|
assert!(!matcher.is_match(&uev));
|
||||||
|
|
||||||
|
uev.devpath = format!(
|
||||||
|
"{}/1.0.0001/{}/virtio1/{}/{}",
|
||||||
|
root_bus, relpath, subsystem, devname
|
||||||
|
);
|
||||||
|
assert!(!matcher.is_match(&uev));
|
||||||
|
|
||||||
|
uev.devpath = format!(
|
||||||
|
"{}/0.4.0001/{}/virtio1/{}/{}",
|
||||||
|
root_bus, relpath, subsystem, devname
|
||||||
|
);
|
||||||
|
assert!(!matcher.is_match(&uev));
|
||||||
|
|
||||||
|
uev.devpath = format!(
|
||||||
|
"{}/0.0.10000/{}/virtio1/{}/{}",
|
||||||
|
root_bus, relpath, subsystem, devname
|
||||||
|
);
|
||||||
|
assert!(!matcher.is_match(&uev));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[allow(clippy::redundant_clone)]
|
||||||
|
async fn test_virtio_blk_mmio_matcher() {
|
||||||
|
let devname_a = "vda";
|
||||||
|
let devname_b = "vdb";
|
||||||
|
let mut uev_a = crate::uevent::Uevent::default();
|
||||||
|
uev_a.action = crate::linux_abi::U_EVENT_ACTION_ADD.to_string();
|
||||||
|
uev_a.subsystem = BLOCK.to_string();
|
||||||
|
uev_a.devname = devname_a.to_string();
|
||||||
|
uev_a.devpath = format!(
|
||||||
|
"/sys/devices/virtio-mmio-cmdline/virtio-mmio.0/virtio0/block/{}",
|
||||||
|
devname_a
|
||||||
|
);
|
||||||
|
let matcher_a = VirtioBlkMmioMatcher::new(devname_a);
|
||||||
|
|
||||||
|
let mut uev_b = uev_a.clone();
|
||||||
|
uev_b.devpath = format!(
|
||||||
|
"/sys/devices/virtio-mmio-cmdline/virtio-mmio.4/virtio4/block/{}",
|
||||||
|
devname_b
|
||||||
|
);
|
||||||
|
let matcher_b = VirtioBlkMmioMatcher::new(devname_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));
|
||||||
|
}
|
||||||
|
}
|
1113
src/agent/src/device/mod.rs
Normal file
1113
src/agent/src/device/mod.rs
Normal file
File diff suppressed because it is too large
Load Diff
114
src/agent/src/device/network_device_handler.rs
Normal file
114
src/agent/src/device/network_device_handler.rs
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
// Copyright (c) 2019 Ant Financial
|
||||||
|
// Copyright (c) 2024 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
use crate::device::pcipath_to_sysfs;
|
||||||
|
use crate::linux_abi::*;
|
||||||
|
use crate::pci;
|
||||||
|
use crate::sandbox::Sandbox;
|
||||||
|
use crate::uevent::{wait_for_uevent, Uevent, UeventMatcher};
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use regex::Regex;
|
||||||
|
use std::fs;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
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)]
|
||||||
|
pub struct NetPciMatcher {
|
||||||
|
devpath: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetPciMatcher {
|
||||||
|
pub 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[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));
|
||||||
|
}
|
||||||
|
}
|
83
src/agent/src/device/nvdimm_device_handler.rs
Normal file
83
src/agent/src/device/nvdimm_device_handler.rs
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
// Copyright (c) 2019 Ant Financial
|
||||||
|
// Copyright (c) 2024 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
use crate::device::{DeviceContext, DeviceHandler, DeviceInfo, SpecUpdate, BLOCK};
|
||||||
|
use crate::linux_abi::ACPI_DEV_PATH;
|
||||||
|
use crate::sandbox::Sandbox;
|
||||||
|
use crate::uevent::{wait_for_uevent, Uevent, UeventMatcher};
|
||||||
|
use anyhow::{anyhow, Context, Result};
|
||||||
|
use kata_types::device::DRIVER_NVDIMM_TYPE;
|
||||||
|
use protocols::agent::Device;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
use tracing::instrument;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct VirtioNvdimmDeviceHandler {}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl DeviceHandler for VirtioNvdimmDeviceHandler {
|
||||||
|
#[instrument]
|
||||||
|
fn driver_types(&self) -> &[&str] {
|
||||||
|
&[DRIVER_NVDIMM_TYPE]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument]
|
||||||
|
async fn device_handler(&self, device: &Device, ctx: &mut DeviceContext) -> Result<SpecUpdate> {
|
||||||
|
if device.vm_path.is_empty() {
|
||||||
|
return Err(anyhow!("Invalid path for nvdimm device"));
|
||||||
|
}
|
||||||
|
Ok(DeviceInfo::new(device.vm_path(), true)
|
||||||
|
.context("New device info")?
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument]
|
||||||
|
pub async fn wait_for_pmem_device(sandbox: &Arc<Mutex<Sandbox>>, devpath: &str) -> Result<()> {
|
||||||
|
let devname = match devpath.strip_prefix("/dev/") {
|
||||||
|
Some(dev) => dev,
|
||||||
|
None => {
|
||||||
|
return Err(anyhow!(
|
||||||
|
"Storage source '{}' must start with /dev/",
|
||||||
|
devpath
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let matcher = PmemBlockMatcher::new(devname);
|
||||||
|
let uev = wait_for_uevent(sandbox, matcher).await?;
|
||||||
|
if uev.devname != devname {
|
||||||
|
return Err(anyhow!(
|
||||||
|
"Unexpected device name {} for pmem device (expected {})",
|
||||||
|
uev.devname,
|
||||||
|
devname
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PmemBlockMatcher {
|
||||||
|
suffix: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PmemBlockMatcher {
|
||||||
|
pub fn new(devname: &str) -> PmemBlockMatcher {
|
||||||
|
let suffix = format!(r"/block/{}", devname);
|
||||||
|
|
||||||
|
PmemBlockMatcher { suffix }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UeventMatcher for PmemBlockMatcher {
|
||||||
|
fn is_match(&self, uev: &Uevent) -> bool {
|
||||||
|
uev.subsystem == BLOCK
|
||||||
|
&& uev.devpath.starts_with(ACPI_DEV_PATH)
|
||||||
|
&& uev.devpath.ends_with(&self.suffix)
|
||||||
|
&& !uev.devname.is_empty()
|
||||||
|
}
|
||||||
|
}
|
140
src/agent/src/device/scsi_device_handler.rs
Normal file
140
src/agent/src/device/scsi_device_handler.rs
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
// Copyright (c) 2019 Ant Financial
|
||||||
|
// Copyright (c) 2024 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
use crate::device::{DeviceContext, DeviceHandler, DeviceInfo, SpecUpdate, BLOCK};
|
||||||
|
use crate::linux_abi::*;
|
||||||
|
use crate::sandbox::Sandbox;
|
||||||
|
use crate::uevent::{wait_for_uevent, Uevent, UeventMatcher};
|
||||||
|
use anyhow::{anyhow, Context, Result};
|
||||||
|
use kata_types::device::DRIVER_SCSI_TYPE;
|
||||||
|
use protocols::agent::Device;
|
||||||
|
use std::fs;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
use tracing::instrument;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ScsiDeviceHandler {}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl DeviceHandler for ScsiDeviceHandler {
|
||||||
|
#[instrument]
|
||||||
|
fn driver_types(&self) -> &[&str] {
|
||||||
|
&[DRIVER_SCSI_TYPE]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument]
|
||||||
|
async fn device_handler(&self, device: &Device, ctx: &mut DeviceContext) -> Result<SpecUpdate> {
|
||||||
|
let vm_path = get_scsi_device_name(ctx.sandbox, &device.id).await?;
|
||||||
|
|
||||||
|
Ok(DeviceInfo::new(&vm_path, true)
|
||||||
|
.context("New device info")?
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument]
|
||||||
|
pub async fn get_scsi_device_name(
|
||||||
|
sandbox: &Arc<Mutex<Sandbox>>,
|
||||||
|
scsi_addr: &str,
|
||||||
|
) -> Result<String> {
|
||||||
|
let matcher = ScsiBlockMatcher::new(scsi_addr);
|
||||||
|
|
||||||
|
scan_scsi_bus(scsi_addr)?;
|
||||||
|
let uev = wait_for_uevent(sandbox, matcher).await?;
|
||||||
|
Ok(format!("{}/{}", SYSTEM_DEV_PATH, &uev.devname))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: This matcher is only correct if the guest has at most one
|
||||||
|
// SCSI host.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ScsiBlockMatcher {
|
||||||
|
search: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScsiBlockMatcher {
|
||||||
|
pub fn new(scsi_addr: &str) -> ScsiBlockMatcher {
|
||||||
|
let search = format!(r"/0:0:{}/block/", scsi_addr);
|
||||||
|
|
||||||
|
ScsiBlockMatcher { search }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UeventMatcher for ScsiBlockMatcher {
|
||||||
|
fn is_match(&self, uev: &Uevent) -> bool {
|
||||||
|
uev.subsystem == BLOCK && uev.devpath.contains(&self.search) && !uev.devname.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Scan SCSI bus for the given SCSI address(SCSI-Id and LUN)
|
||||||
|
#[instrument]
|
||||||
|
fn scan_scsi_bus(scsi_addr: &str) -> Result<()> {
|
||||||
|
let tokens: Vec<&str> = scsi_addr.split(':').collect();
|
||||||
|
if tokens.len() != 2 {
|
||||||
|
return Err(anyhow!(
|
||||||
|
"Unexpected format for SCSI Address: {}, expect SCSIID:LUA",
|
||||||
|
scsi_addr
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan scsi host passing in the channel, SCSI id and LUN.
|
||||||
|
// Channel is always 0 because we have only one SCSI controller.
|
||||||
|
let scan_data = &format!("0 {} {}", tokens[0], tokens[1]);
|
||||||
|
|
||||||
|
for entry in fs::read_dir(SYSFS_SCSI_HOST_PATH)? {
|
||||||
|
let host = entry?.file_name();
|
||||||
|
|
||||||
|
let host_str = host.to_str().ok_or_else(|| {
|
||||||
|
anyhow!(
|
||||||
|
"failed to convert directory entry to unicode for file {:?}",
|
||||||
|
host
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let scan_path = PathBuf::from(&format!("{}/{}/{}", SYSFS_SCSI_HOST_PATH, host_str, "scan"));
|
||||||
|
|
||||||
|
fs::write(scan_path, scan_data)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[allow(clippy::redundant_clone)]
|
||||||
|
async fn test_scsi_block_matcher() {
|
||||||
|
let root_bus = create_pci_root_bus_path();
|
||||||
|
let devname = "sda";
|
||||||
|
|
||||||
|
let mut uev_a = crate::uevent::Uevent::default();
|
||||||
|
let addr_a = "0:0";
|
||||||
|
uev_a.action = crate::linux_abi::U_EVENT_ACTION_ADD.to_string();
|
||||||
|
uev_a.subsystem = BLOCK.to_string();
|
||||||
|
uev_a.devname = devname.to_string();
|
||||||
|
uev_a.devpath = format!(
|
||||||
|
"{}/0000:00:00.0/virtio0/host0/target0:0:0/0:0:{}/block/sda",
|
||||||
|
root_bus, addr_a
|
||||||
|
);
|
||||||
|
let matcher_a = ScsiBlockMatcher::new(addr_a);
|
||||||
|
|
||||||
|
let mut uev_b = uev_a.clone();
|
||||||
|
let addr_b = "2:0";
|
||||||
|
uev_b.devpath = format!(
|
||||||
|
"{}/0000:00:00.0/virtio0/host0/target0:0:2/0:0:{}/block/sdb",
|
||||||
|
root_bus, addr_b
|
||||||
|
);
|
||||||
|
let matcher_b = ScsiBlockMatcher::new(addr_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));
|
||||||
|
}
|
||||||
|
}
|
455
src/agent/src/device/vfio_device_handler.rs
Normal file
455
src/agent/src/device/vfio_device_handler.rs
Normal file
@ -0,0 +1,455 @@
|
|||||||
|
// Copyright (c) 2019 Ant Financial
|
||||||
|
// Copyright (c) 2024 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
#[cfg(target_arch = "s390x")]
|
||||||
|
use crate::ap;
|
||||||
|
use crate::device::{pcipath_to_sysfs, DevUpdate, DeviceContext, DeviceHandler, SpecUpdate};
|
||||||
|
use crate::linux_abi::*;
|
||||||
|
use crate::pci;
|
||||||
|
use crate::sandbox::Sandbox;
|
||||||
|
use crate::uevent::{wait_for_uevent, Uevent, UeventMatcher};
|
||||||
|
use anyhow::{anyhow, Context, Result};
|
||||||
|
use kata_types::device::{DRIVER_VFIO_AP_TYPE, DRIVER_VFIO_PCI_GK_TYPE, DRIVER_VFIO_PCI_TYPE};
|
||||||
|
use protocols::agent::Device;
|
||||||
|
use slog::Logger;
|
||||||
|
use std::ffi::OsStr;
|
||||||
|
use std::fmt;
|
||||||
|
use std::fs;
|
||||||
|
use std::os::unix::ffi::OsStrExt;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
use tracing::instrument;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct VfioPciDeviceHandler {}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct VfioApDeviceHandler {}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl DeviceHandler for VfioPciDeviceHandler {
|
||||||
|
#[instrument]
|
||||||
|
fn driver_types(&self) -> &[&str] {
|
||||||
|
&[DRIVER_VFIO_PCI_GK_TYPE, DRIVER_VFIO_PCI_TYPE]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument]
|
||||||
|
async fn device_handler(&self, device: &Device, ctx: &mut DeviceContext) -> Result<SpecUpdate> {
|
||||||
|
let vfio_in_guest = device.type_ != DRIVER_VFIO_PCI_GK_TYPE;
|
||||||
|
let mut pci_fixups = Vec::<(pci::Address, pci::Address)>::new();
|
||||||
|
let mut group = None;
|
||||||
|
|
||||||
|
for opt in device.options.iter() {
|
||||||
|
let (host, pcipath) = split_vfio_pci_option(opt)
|
||||||
|
.ok_or_else(|| anyhow!("Malformed VFIO PCI option {:?}", opt))?;
|
||||||
|
let host =
|
||||||
|
pci::Address::from_str(host).context("Bad host PCI address in VFIO option {:?}")?;
|
||||||
|
let pcipath = pci::Path::from_str(pcipath)?;
|
||||||
|
|
||||||
|
let guestdev = wait_for_pci_device(ctx.sandbox, &pcipath).await?;
|
||||||
|
if vfio_in_guest {
|
||||||
|
pci_driver_override(ctx.logger, SYSFS_BUS_PCI_PATH, guestdev, "vfio-pci")?;
|
||||||
|
|
||||||
|
// Devices must have an IOMMU group to be usable via VFIO
|
||||||
|
let devgroup = pci_iommu_group(SYSFS_BUS_PCI_PATH, guestdev)?
|
||||||
|
.ok_or_else(|| anyhow!("{} has no IOMMU group", guestdev))?;
|
||||||
|
|
||||||
|
if let Some(g) = group {
|
||||||
|
if g != devgroup {
|
||||||
|
return Err(anyhow!("{} is not in guest IOMMU group {}", guestdev, g));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
group = Some(devgroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
// collect PCI address mapping for both vfio-pci-gk and vfio-pci device
|
||||||
|
pci_fixups.push((host, guestdev));
|
||||||
|
}
|
||||||
|
|
||||||
|
let dev_update = if vfio_in_guest {
|
||||||
|
// If there are any devices at all, logic above ensures that group is not None
|
||||||
|
let group = group.ok_or_else(|| anyhow!("failed to get VFIO group"))?;
|
||||||
|
|
||||||
|
let vm_path = get_vfio_pci_device_name(group, ctx.sandbox).await?;
|
||||||
|
|
||||||
|
Some(DevUpdate::new(&vm_path, &vm_path)?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(SpecUpdate {
|
||||||
|
dev: dev_update,
|
||||||
|
pci: pci_fixups,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl DeviceHandler for VfioApDeviceHandler {
|
||||||
|
#[instrument]
|
||||||
|
fn driver_types(&self) -> &[&str] {
|
||||||
|
&[DRIVER_VFIO_AP_TYPE]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "s390x")]
|
||||||
|
#[instrument]
|
||||||
|
async fn device_handler(&self, device: &Device, ctx: &mut DeviceContext) -> Result<SpecUpdate> {
|
||||||
|
// Force AP bus rescan
|
||||||
|
fs::write(AP_SCANS_PATH, "1")?;
|
||||||
|
for apqn in device.options.iter() {
|
||||||
|
wait_for_ap_device(ctx.sandbox, ap::Address::from_str(apqn)?).await?;
|
||||||
|
}
|
||||||
|
let dev_update = Some(DevUpdate::new(Z9_CRYPT_DEV_PATH, Z9_CRYPT_DEV_PATH)?);
|
||||||
|
Ok(SpecUpdate {
|
||||||
|
dev: dev_update,
|
||||||
|
pci: Vec::new(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "s390x"))]
|
||||||
|
#[instrument]
|
||||||
|
async fn device_handler(&self, _: &Device, _: &mut DeviceContext) -> Result<SpecUpdate> {
|
||||||
|
Err(anyhow!("VFIO-AP is only supported on s390x"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_vfio_pci_device_name(
|
||||||
|
grp: IommuGroup,
|
||||||
|
sandbox: &Arc<Mutex<Sandbox>>,
|
||||||
|
) -> Result<String> {
|
||||||
|
let matcher = VfioMatcher::new(grp);
|
||||||
|
|
||||||
|
let uev = wait_for_uevent(sandbox, matcher).await?;
|
||||||
|
Ok(format!("{}/{}", SYSTEM_DEV_PATH, &uev.devname))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct VfioMatcher {
|
||||||
|
syspath: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VfioMatcher {
|
||||||
|
pub fn new(grp: IommuGroup) -> VfioMatcher {
|
||||||
|
VfioMatcher {
|
||||||
|
syspath: format!("/devices/virtual/vfio/{}", grp),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UeventMatcher for VfioMatcher {
|
||||||
|
fn is_match(&self, uev: &Uevent) -> bool {
|
||||||
|
uev.devpath == self.syspath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "s390x")]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ApMatcher {
|
||||||
|
syspath: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "s390x")]
|
||||||
|
impl ApMatcher {
|
||||||
|
pub fn new(address: ap::Address) -> ApMatcher {
|
||||||
|
ApMatcher {
|
||||||
|
syspath: format!(
|
||||||
|
"{}/card{:02x}/{}",
|
||||||
|
AP_ROOT_BUS_PATH, address.adapter_id, address
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "s390x")]
|
||||||
|
impl UeventMatcher for ApMatcher {
|
||||||
|
fn is_match(&self, uev: &Uevent) -> bool {
|
||||||
|
uev.action == "add" && uev.devpath == self.syspath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PciMatcher {
|
||||||
|
devpath: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PciMatcher {
|
||||||
|
pub fn new(relpath: &str) -> Result<PciMatcher> {
|
||||||
|
let root_bus = create_pci_root_bus_path();
|
||||||
|
Ok(PciMatcher {
|
||||||
|
devpath: format!("{}{}", root_bus, relpath),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UeventMatcher for PciMatcher {
|
||||||
|
fn is_match(&self, uev: &Uevent) -> bool {
|
||||||
|
uev.devpath == self.devpath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "s390x")]
|
||||||
|
#[instrument]
|
||||||
|
async fn wait_for_ap_device(sandbox: &Arc<Mutex<Sandbox>>, address: ap::Address) -> Result<()> {
|
||||||
|
let matcher = ApMatcher::new(address);
|
||||||
|
wait_for_uevent(sandbox, matcher).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn wait_for_pci_device(
|
||||||
|
sandbox: &Arc<Mutex<Sandbox>>,
|
||||||
|
pcipath: &pci::Path,
|
||||||
|
) -> Result<pci::Address> {
|
||||||
|
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 = PciMatcher::new(&sysfs_rel_path)?;
|
||||||
|
|
||||||
|
let uev = wait_for_uevent(sandbox, matcher).await?;
|
||||||
|
|
||||||
|
let addr = uev
|
||||||
|
.devpath
|
||||||
|
.rsplit('/')
|
||||||
|
.next()
|
||||||
|
.ok_or_else(|| anyhow!("Bad device path {:?} in uevent", &uev.devpath))?;
|
||||||
|
let addr = pci::Address::from_str(addr)?;
|
||||||
|
Ok(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Represents an IOMMU group
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct IommuGroup(u32);
|
||||||
|
|
||||||
|
impl fmt::Display for IommuGroup {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
|
write!(f, "{}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the IOMMU group of a PCI device
|
||||||
|
#[instrument]
|
||||||
|
fn pci_iommu_group<T>(syspci: T, dev: pci::Address) -> Result<Option<IommuGroup>>
|
||||||
|
where
|
||||||
|
T: AsRef<OsStr> + std::fmt::Debug,
|
||||||
|
{
|
||||||
|
let syspci = Path::new(&syspci);
|
||||||
|
let grouppath = syspci
|
||||||
|
.join("devices")
|
||||||
|
.join(dev.to_string())
|
||||||
|
.join("iommu_group");
|
||||||
|
|
||||||
|
match fs::read_link(&grouppath) {
|
||||||
|
// Device has no group
|
||||||
|
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(None),
|
||||||
|
Err(e) => Err(anyhow!("Error reading link {:?}: {}", &grouppath, e)),
|
||||||
|
Ok(group) => {
|
||||||
|
if let Some(group) = group.file_name() {
|
||||||
|
if let Some(group) = group.to_str() {
|
||||||
|
if let Ok(group) = group.parse::<u32>() {
|
||||||
|
return Ok(Some(IommuGroup(group)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(anyhow!(
|
||||||
|
"Unexpected IOMMU group link {:?} => {:?}",
|
||||||
|
grouppath,
|
||||||
|
group
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn split_vfio_pci_option(opt: &str) -> Option<(&str, &str)> {
|
||||||
|
let mut tokens = opt.split('=');
|
||||||
|
let hostbdf = tokens.next()?;
|
||||||
|
let path = tokens.next()?;
|
||||||
|
if tokens.next().is_some() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some((hostbdf, path))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force a given PCI device to bind to the given driver, does
|
||||||
|
// basically the same thing as
|
||||||
|
// driverctl set-override <PCI address> <driver>
|
||||||
|
#[instrument]
|
||||||
|
pub fn pci_driver_override<T, U>(
|
||||||
|
logger: &Logger,
|
||||||
|
syspci: T,
|
||||||
|
dev: pci::Address,
|
||||||
|
drv: U,
|
||||||
|
) -> Result<()>
|
||||||
|
where
|
||||||
|
T: AsRef<OsStr> + std::fmt::Debug,
|
||||||
|
U: AsRef<OsStr> + std::fmt::Debug,
|
||||||
|
{
|
||||||
|
let syspci = Path::new(&syspci);
|
||||||
|
let drv = drv.as_ref();
|
||||||
|
info!(logger, "rebind_pci_driver: {} => {:?}", dev, drv);
|
||||||
|
|
||||||
|
let devpath = syspci.join("devices").join(dev.to_string());
|
||||||
|
let overridepath = &devpath.join("driver_override");
|
||||||
|
|
||||||
|
fs::write(overridepath, drv.as_bytes())?;
|
||||||
|
|
||||||
|
let drvpath = &devpath.join("driver");
|
||||||
|
let need_unbind = match fs::read_link(drvpath) {
|
||||||
|
Ok(d) if d.file_name() == Some(drv) => return Ok(()), // Nothing to do
|
||||||
|
Err(e) if e.kind() == std::io::ErrorKind::NotFound => false, // No current driver
|
||||||
|
Err(e) => return Err(anyhow!("Error checking driver on {}: {}", dev, e)),
|
||||||
|
Ok(_) => true, // Current driver needs unbinding
|
||||||
|
};
|
||||||
|
if need_unbind {
|
||||||
|
let unbindpath = &drvpath.join("unbind");
|
||||||
|
fs::write(unbindpath, dev.to_string())?;
|
||||||
|
}
|
||||||
|
let probepath = syspci.join("drivers_probe");
|
||||||
|
fs::write(probepath, dev.to_string())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use tempfile::tempdir;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[allow(clippy::redundant_clone)]
|
||||||
|
async fn test_vfio_matcher() {
|
||||||
|
let grpa = IommuGroup(1);
|
||||||
|
let grpb = IommuGroup(22);
|
||||||
|
|
||||||
|
let mut uev_a = crate::uevent::Uevent::default();
|
||||||
|
uev_a.action = crate::linux_abi::U_EVENT_ACTION_ADD.to_string();
|
||||||
|
uev_a.devname = format!("vfio/{}", grpa);
|
||||||
|
uev_a.devpath = format!("/devices/virtual/vfio/{}", grpa);
|
||||||
|
let matcher_a = VfioMatcher::new(grpa);
|
||||||
|
|
||||||
|
let mut uev_b = uev_a.clone();
|
||||||
|
uev_b.devpath = format!("/devices/virtual/vfio/{}", grpb);
|
||||||
|
let matcher_b = VfioMatcher::new(grpb);
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_split_vfio_pci_option() {
|
||||||
|
assert_eq!(
|
||||||
|
split_vfio_pci_option("0000:01:00.0=02/01"),
|
||||||
|
Some(("0000:01:00.0", "02/01"))
|
||||||
|
);
|
||||||
|
assert_eq!(split_vfio_pci_option("0000:01:00.0=02/01=rubbish"), None);
|
||||||
|
assert_eq!(split_vfio_pci_option("0000:01:00.0"), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pci_driver_override() {
|
||||||
|
let logger = slog::Logger::root(slog::Discard, o!());
|
||||||
|
let testdir = tempdir().expect("failed to create tmpdir");
|
||||||
|
let syspci = testdir.path(); // Path to mock /sys/bus/pci
|
||||||
|
|
||||||
|
let dev0 = pci::Address::new(0, 0, pci::SlotFn::new(0, 0).unwrap());
|
||||||
|
let dev0path = syspci.join("devices").join(dev0.to_string());
|
||||||
|
let dev0drv = dev0path.join("driver");
|
||||||
|
let dev0override = dev0path.join("driver_override");
|
||||||
|
|
||||||
|
let drvapath = syspci.join("drivers").join("drv_a");
|
||||||
|
let drvaunbind = drvapath.join("unbind");
|
||||||
|
|
||||||
|
let probepath = syspci.join("drivers_probe");
|
||||||
|
|
||||||
|
// Start mocking dev0 as being unbound
|
||||||
|
fs::create_dir_all(&dev0path).unwrap();
|
||||||
|
|
||||||
|
pci_driver_override(&logger, syspci, dev0, "drv_a").unwrap();
|
||||||
|
assert_eq!(fs::read_to_string(&dev0override).unwrap(), "drv_a");
|
||||||
|
assert_eq!(fs::read_to_string(&probepath).unwrap(), dev0.to_string());
|
||||||
|
|
||||||
|
// Now mock dev0 already being attached to drv_a
|
||||||
|
fs::create_dir_all(&drvapath).unwrap();
|
||||||
|
std::os::unix::fs::symlink(&drvapath, dev0drv).unwrap();
|
||||||
|
std::fs::remove_file(&probepath).unwrap();
|
||||||
|
|
||||||
|
pci_driver_override(&logger, syspci, dev0, "drv_a").unwrap(); // no-op
|
||||||
|
assert_eq!(fs::read_to_string(&dev0override).unwrap(), "drv_a");
|
||||||
|
assert!(!probepath.exists());
|
||||||
|
|
||||||
|
// Now try binding to a different driver
|
||||||
|
pci_driver_override(&logger, syspci, dev0, "drv_b").unwrap();
|
||||||
|
assert_eq!(fs::read_to_string(&dev0override).unwrap(), "drv_b");
|
||||||
|
assert_eq!(fs::read_to_string(&probepath).unwrap(), dev0.to_string());
|
||||||
|
assert_eq!(fs::read_to_string(drvaunbind).unwrap(), dev0.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pci_iommu_group() {
|
||||||
|
let testdir = tempdir().expect("failed to create tmpdir"); // mock /sys
|
||||||
|
let syspci = testdir.path().join("bus").join("pci");
|
||||||
|
|
||||||
|
// Mock dev0, which has no group
|
||||||
|
let dev0 = pci::Address::new(0, 0, pci::SlotFn::new(0, 0).unwrap());
|
||||||
|
let dev0path = syspci.join("devices").join(dev0.to_string());
|
||||||
|
|
||||||
|
fs::create_dir_all(dev0path).unwrap();
|
||||||
|
|
||||||
|
// Test dev0
|
||||||
|
assert!(pci_iommu_group(&syspci, dev0).unwrap().is_none());
|
||||||
|
|
||||||
|
// Mock dev1, which is in group 12
|
||||||
|
let dev1 = pci::Address::new(0, 1, pci::SlotFn::new(0, 0).unwrap());
|
||||||
|
let dev1path = syspci.join("devices").join(dev1.to_string());
|
||||||
|
let dev1group = dev1path.join("iommu_group");
|
||||||
|
|
||||||
|
fs::create_dir_all(&dev1path).unwrap();
|
||||||
|
std::os::unix::fs::symlink("../../../kernel/iommu_groups/12", dev1group).unwrap();
|
||||||
|
|
||||||
|
// Test dev1
|
||||||
|
assert_eq!(
|
||||||
|
pci_iommu_group(&syspci, dev1).unwrap(),
|
||||||
|
Some(IommuGroup(12))
|
||||||
|
);
|
||||||
|
|
||||||
|
// Mock dev2, which has a bogus group (dir instead of symlink)
|
||||||
|
let dev2 = pci::Address::new(0, 2, pci::SlotFn::new(0, 0).unwrap());
|
||||||
|
let dev2path = syspci.join("devices").join(dev2.to_string());
|
||||||
|
let dev2group = dev2path.join("iommu_group");
|
||||||
|
|
||||||
|
fs::create_dir_all(dev2group).unwrap();
|
||||||
|
|
||||||
|
// Test dev2
|
||||||
|
assert!(pci_iommu_group(&syspci, dev2).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "s390x")]
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_vfio_ap_matcher() {
|
||||||
|
let subsystem = "ap";
|
||||||
|
let card = "0a";
|
||||||
|
let relpath = format!("{}.0001", card);
|
||||||
|
|
||||||
|
let mut uev = Uevent::default();
|
||||||
|
uev.action = U_EVENT_ACTION_ADD.to_string();
|
||||||
|
uev.subsystem = subsystem.to_string();
|
||||||
|
uev.devpath = format!("{}/card{}/{}", AP_ROOT_BUS_PATH, card, relpath);
|
||||||
|
|
||||||
|
let ap_address = ap::Address::from_str(&relpath).unwrap();
|
||||||
|
let matcher = ApMatcher::new(ap_address);
|
||||||
|
|
||||||
|
assert!(matcher.is_match(&uev));
|
||||||
|
|
||||||
|
let mut uev_remove = uev.clone();
|
||||||
|
uev_remove.action = U_EVENT_ACTION_REMOVE.to_string();
|
||||||
|
assert!(!matcher.is_match(&uev_remove));
|
||||||
|
|
||||||
|
let mut uev_other_device = uev.clone();
|
||||||
|
uev_other_device.devpath = format!("{}/card{}/{}.0002", AP_ROOT_BUS_PATH, card, card);
|
||||||
|
assert!(!matcher.is_match(&uev_other_device));
|
||||||
|
}
|
||||||
|
}
|
@ -56,9 +56,9 @@ use nix::unistd::{self, Pid};
|
|||||||
use rustjail::process::ProcessOperations;
|
use rustjail::process::ProcessOperations;
|
||||||
|
|
||||||
use crate::cdh;
|
use crate::cdh;
|
||||||
use crate::device::{
|
use crate::device::block_device_handler::get_virtio_blk_pci_device_name;
|
||||||
add_devices, get_virtio_blk_pci_device_name, update_env_pci, wait_for_net_interface,
|
use crate::device::network_device_handler::wait_for_net_interface;
|
||||||
};
|
use crate::device::{add_devices, update_env_pci};
|
||||||
use crate::features::get_build_features;
|
use crate::features::get_build_features;
|
||||||
use crate::image::KATA_IMAGE_WORK_DIR;
|
use crate::image::KATA_IMAGE_WORK_DIR;
|
||||||
use crate::linux_abi::*;
|
use crate::linux_abi::*;
|
||||||
@ -222,7 +222,7 @@ impl AgentService {
|
|||||||
// updates the devices listed in the OCI spec, so that they actually
|
// updates the devices listed in the OCI spec, so that they actually
|
||||||
// match real devices inside the VM. This step is necessary since we
|
// match real devices inside the VM. This step is necessary since we
|
||||||
// cannot predict everything from the caller.
|
// cannot predict everything from the caller.
|
||||||
add_devices(&req.devices, &mut oci, &self.sandbox).await?;
|
add_devices(&sl(), &req.devices, &mut oci, &self.sandbox).await?;
|
||||||
|
|
||||||
let process = oci
|
let process = oci
|
||||||
.process_mut()
|
.process_mut()
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
||||||
use crate::device::DRIVER_WATCHABLE_BIND_TYPE;
|
|
||||||
use crate::storage::{new_device, StorageContext, StorageHandler};
|
use crate::storage::{new_device, StorageContext, StorageHandler};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use kata_types::device::DRIVER_WATCHABLE_BIND_TYPE;
|
||||||
use kata_types::mount::StorageDevice;
|
use kata_types::mount::StorageDevice;
|
||||||
use protocols::agent::Storage;
|
use protocols::agent::Storage;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
@ -10,23 +10,26 @@ use std::path::Path;
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::device::{
|
use anyhow::{anyhow, Context, Result};
|
||||||
|
use kata_types::device::{
|
||||||
DRIVER_BLK_CCW_TYPE, DRIVER_BLK_MMIO_TYPE, DRIVER_BLK_PCI_TYPE, DRIVER_NVDIMM_TYPE,
|
DRIVER_BLK_CCW_TYPE, DRIVER_BLK_MMIO_TYPE, DRIVER_BLK_PCI_TYPE, DRIVER_NVDIMM_TYPE,
|
||||||
DRIVER_SCSI_TYPE,
|
DRIVER_SCSI_TYPE,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Context, Result};
|
|
||||||
use kata_types::mount::StorageDevice;
|
use kata_types::mount::StorageDevice;
|
||||||
use protocols::agent::Storage;
|
use protocols::agent::Storage;
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|
||||||
use crate::device::{
|
#[cfg(target_arch = "s390x")]
|
||||||
get_scsi_device_name, get_virtio_blk_pci_device_name, get_virtio_mmio_device_name,
|
use crate::ccw;
|
||||||
wait_for_pmem_device,
|
#[cfg(target_arch = "s390x")]
|
||||||
|
use crate::device::block_device_handler::get_virtio_blk_ccw_device_name;
|
||||||
|
use crate::device::block_device_handler::{
|
||||||
|
get_virtio_blk_mmio_device_name, get_virtio_blk_pci_device_name,
|
||||||
};
|
};
|
||||||
|
use crate::device::nvdimm_device_handler::wait_for_pmem_device;
|
||||||
|
use crate::device::scsi_device_handler::get_scsi_device_name;
|
||||||
use crate::pci;
|
use crate::pci;
|
||||||
use crate::storage::{common_storage_handler, new_device, StorageContext, StorageHandler};
|
use crate::storage::{common_storage_handler, new_device, StorageContext, StorageHandler};
|
||||||
#[cfg(target_arch = "s390x")]
|
|
||||||
use crate::{ccw, device::get_virtio_blk_ccw_device_name};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct VirtioBlkMmioHandler {}
|
pub struct VirtioBlkMmioHandler {}
|
||||||
@ -45,7 +48,7 @@ impl StorageHandler for VirtioBlkMmioHandler {
|
|||||||
ctx: &mut StorageContext,
|
ctx: &mut StorageContext,
|
||||||
) -> Result<Arc<dyn StorageDevice>> {
|
) -> Result<Arc<dyn StorageDevice>> {
|
||||||
if !Path::new(&storage.source).exists() {
|
if !Path::new(&storage.source).exists() {
|
||||||
get_virtio_mmio_device_name(ctx.sandbox, &storage.source)
|
get_virtio_blk_mmio_device_name(ctx.sandbox, &storage.source)
|
||||||
.await
|
.await
|
||||||
.context("failed to get mmio device name")?;
|
.context("failed to get mmio device name")?;
|
||||||
}
|
}
|
||||||
|
@ -20,13 +20,14 @@ use slog::Logger;
|
|||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|
||||||
use crate::device::{DRIVER_EPHEMERAL_TYPE, FS_TYPE_HUGETLB};
|
|
||||||
use crate::mount::baremount;
|
use crate::mount::baremount;
|
||||||
use crate::sandbox::Sandbox;
|
use crate::sandbox::Sandbox;
|
||||||
use crate::storage::{
|
use crate::storage::{
|
||||||
common_storage_handler, new_device, parse_options, StorageContext, StorageHandler, MODE_SETGID,
|
common_storage_handler, new_device, parse_options, StorageContext, StorageHandler, MODE_SETGID,
|
||||||
};
|
};
|
||||||
|
use kata_types::device::DRIVER_EPHEMERAL_TYPE;
|
||||||
|
|
||||||
|
const FS_TYPE_HUGETLB: &str = "hugetlbfs";
|
||||||
const FS_GID_EQ: &str = "fsgid=";
|
const FS_GID_EQ: &str = "fsgid=";
|
||||||
const SYS_FS_HUGEPAGES_PREFIX: &str = "/sys/kernel/mm/hugepages";
|
const SYS_FS_HUGEPAGES_PREFIX: &str = "/sys/kernel/mm/hugepages";
|
||||||
|
|
||||||
|
@ -8,9 +8,9 @@ use std::fs;
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::device::{DRIVER_9P_TYPE, DRIVER_OVERLAYFS_TYPE, DRIVER_VIRTIOFS_TYPE};
|
|
||||||
use crate::storage::{common_storage_handler, new_device, StorageContext, StorageHandler};
|
use crate::storage::{common_storage_handler, new_device, StorageContext, StorageHandler};
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
|
use kata_types::device::{DRIVER_9P_TYPE, DRIVER_OVERLAYFS_TYPE, DRIVER_VIRTIOFS_TYPE};
|
||||||
use kata_types::mount::StorageDevice;
|
use kata_types::mount::StorageDevice;
|
||||||
use protocols::agent::Storage;
|
use protocols::agent::Storage;
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
@ -8,15 +8,14 @@ use std::fs;
|
|||||||
use std::os::unix::fs::PermissionsExt;
|
use std::os::unix::fs::PermissionsExt;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::device::DRIVER_LOCAL_TYPE;
|
use crate::storage::{new_device, parse_options, StorageContext, StorageHandler, MODE_SETGID};
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
use kata_types::device::DRIVER_LOCAL_TYPE;
|
||||||
use kata_types::mount::{StorageDevice, KATA_MOUNT_OPTION_FS_GID};
|
use kata_types::mount::{StorageDevice, KATA_MOUNT_OPTION_FS_GID};
|
||||||
use nix::unistd::Gid;
|
use nix::unistd::Gid;
|
||||||
use protocols::agent::Storage;
|
use protocols::agent::Storage;
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|
||||||
use crate::storage::{new_device, parse_options, StorageContext, StorageHandler, MODE_SETGID};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct LocalHandler {}
|
pub struct LocalHandler {}
|
||||||
|
|
||||||
|
42
src/libs/kata-types/src/device.rs
Normal file
42
src/libs/kata-types/src/device.rs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// Copyright (c) 2024 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
use crate::handler::HandlerManager;
|
||||||
|
|
||||||
|
/// DRIVER_BLK_PCI_TYPE is the device driver for virtio-blk
|
||||||
|
pub const DRIVER_BLK_PCI_TYPE: &str = "blk";
|
||||||
|
/// DRIVER_BLK_CCW_TYPE is the device driver for virtio-blk-ccw
|
||||||
|
pub const DRIVER_BLK_CCW_TYPE: &str = "blk-ccw";
|
||||||
|
/// DRIVER_BLK_MMIO_TYPE is the device driver for virtio-mmio
|
||||||
|
pub const DRIVER_BLK_MMIO_TYPE: &str = "mmioblk";
|
||||||
|
/// DRIVER_SCSI_TYPE is the device driver for virtio-scsi
|
||||||
|
pub const DRIVER_SCSI_TYPE: &str = "scsi";
|
||||||
|
/// DRIVER_NVDIMM_TYPE is the device driver for nvdimm
|
||||||
|
pub const DRIVER_NVDIMM_TYPE: &str = "nvdimm";
|
||||||
|
/// DRIVER_VFIO_PCI_GK_TYPE is the device driver for vfio-pci
|
||||||
|
/// while the device will be bound to a guest kernel driver
|
||||||
|
pub const DRIVER_VFIO_PCI_GK_TYPE: &str = "vfio-pci-gk";
|
||||||
|
/// DRIVER_VFIO_PCI_TYPE is the device driver for vfio-pci
|
||||||
|
/// VFIO PCI device to be bound to vfio-pci and made available inside the
|
||||||
|
/// container as a VFIO device node
|
||||||
|
pub const DRIVER_VFIO_PCI_TYPE: &str = "vfio-pci";
|
||||||
|
/// DRIVER_VFIO_AP_TYPE is the device driver for vfio-ap.
|
||||||
|
pub const DRIVER_VFIO_AP_TYPE: &str = "vfio-ap";
|
||||||
|
|
||||||
|
/// DRIVER_9P_TYPE is the driver for 9pfs volume.
|
||||||
|
pub const DRIVER_9P_TYPE: &str = "9p";
|
||||||
|
/// DRIVER_EPHEMERAL_TYPE is the driver for ephemeral volume.
|
||||||
|
pub const DRIVER_EPHEMERAL_TYPE: &str = "ephemeral";
|
||||||
|
/// DRIVER_LOCAL_TYPE is the driver for local volume.
|
||||||
|
pub const DRIVER_LOCAL_TYPE: &str = "local";
|
||||||
|
/// DRIVER_OVERLAYFS_TYPE is the driver for overlayfs volume.
|
||||||
|
pub const DRIVER_OVERLAYFS_TYPE: &str = "overlayfs";
|
||||||
|
/// DRIVER_VIRTIOFS_TYPE is the driver for virtio-fs volume.
|
||||||
|
pub const DRIVER_VIRTIOFS_TYPE: &str = "virtio-fs";
|
||||||
|
/// DRIVER_VIRTIOFS_TYPE is the driver for Bind watch volume.
|
||||||
|
pub const DRIVER_WATCHABLE_BIND_TYPE: &str = "watchable-bind";
|
||||||
|
|
||||||
|
/// Manager to manage registered device handlers.
|
||||||
|
pub type DeviceHandlerManager<H> = HandlerManager<H>;
|
59
src/libs/kata-types/src/handler.rs
Normal file
59
src/libs/kata-types/src/handler.rs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
// Copyright (c) 2024 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use anyhow::{Result, anyhow};
|
||||||
|
use std::collections::hash_map::Entry;
|
||||||
|
|
||||||
|
/// Generic manager to manage registered handlers.
|
||||||
|
pub struct HandlerManager<H> {
|
||||||
|
handlers: HashMap<String, H>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H> Default for HandlerManager<H>
|
||||||
|
where
|
||||||
|
H: Clone,
|
||||||
|
{
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H> HandlerManager<H>
|
||||||
|
where
|
||||||
|
H: Clone,
|
||||||
|
{
|
||||||
|
/// Create a new instance of `HandlerManager`.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
handlers: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register a handler.
|
||||||
|
pub fn add_handler(&mut self, ids: &[&str], handler: H) -> Result<()> {
|
||||||
|
for &id in ids {
|
||||||
|
match self.handlers.entry(id.to_string()) {
|
||||||
|
Entry::Occupied(_) => {
|
||||||
|
return Err(anyhow!("handler for {} already exists", id));
|
||||||
|
}
|
||||||
|
Entry::Vacant(entry) => {
|
||||||
|
entry.insert(handler.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get handler with specified `id`.
|
||||||
|
pub fn handler(&self, id: &str) -> Option<&H> {
|
||||||
|
self.handlers.get(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get names of registered handlers.
|
||||||
|
pub fn get_handlers(&self) -> Vec<String> {
|
||||||
|
self.handlers.keys().map(|v| v.to_string()).collect()
|
||||||
|
}
|
||||||
|
}
|
@ -23,6 +23,12 @@ pub mod container;
|
|||||||
/// Constants and data types related to CPU.
|
/// Constants and data types related to CPU.
|
||||||
pub mod cpu;
|
pub mod cpu;
|
||||||
|
|
||||||
|
/// Contants and data types related to device.
|
||||||
|
pub mod device;
|
||||||
|
|
||||||
|
/// Constants and data types related to handler.
|
||||||
|
pub mod handler;
|
||||||
|
|
||||||
/// Constants and data types related to Kubernetes/kubelet.
|
/// Constants and data types related to Kubernetes/kubelet.
|
||||||
pub mod k8s;
|
pub mod k8s;
|
||||||
|
|
||||||
|
@ -5,10 +5,11 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
use anyhow::{anyhow, Context, Error, Result};
|
use anyhow::{anyhow, Context, Error, Result};
|
||||||
use std::collections::hash_map::Entry;
|
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::{collections::HashMap, fs, path::PathBuf};
|
use std::{collections::HashMap, fs, path::PathBuf};
|
||||||
|
|
||||||
|
use crate::handler::HandlerManager;
|
||||||
|
|
||||||
/// Prefix to mark a volume as Kata special.
|
/// Prefix to mark a volume as Kata special.
|
||||||
pub const KATA_VOLUME_TYPE_PREFIX: &str = "kata:";
|
pub const KATA_VOLUME_TYPE_PREFIX: &str = "kata:";
|
||||||
|
|
||||||
@ -59,6 +60,9 @@ pub const KATA_VIRTUAL_VOLUME_LAYER_NYDUS_FS: &str = "layer_nydus_fs";
|
|||||||
/// Download and extra container image inside guest vm.
|
/// Download and extra container image inside guest vm.
|
||||||
pub const KATA_VIRTUAL_VOLUME_IMAGE_GUEST_PULL: &str = "image_guest_pull";
|
pub const KATA_VIRTUAL_VOLUME_IMAGE_GUEST_PULL: &str = "image_guest_pull";
|
||||||
|
|
||||||
|
/// Manager to manage registered storage device handlers.
|
||||||
|
pub type StorageHandlerManager<H> = HandlerManager<H>;
|
||||||
|
|
||||||
/// Information about a mount.
|
/// Information about a mount.
|
||||||
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct Mount {
|
pub struct Mount {
|
||||||
@ -441,57 +445,6 @@ pub trait StorageDevice: Send + Sync {
|
|||||||
fn cleanup(&self) -> Result<()>;
|
fn cleanup(&self) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Manager to manage registered storage device handlers.
|
|
||||||
pub struct StorageHandlerManager<H> {
|
|
||||||
handlers: HashMap<String, H>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<H> Default for StorageHandlerManager<H>
|
|
||||||
where
|
|
||||||
H: Clone,
|
|
||||||
{
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<H> StorageHandlerManager<H>
|
|
||||||
where
|
|
||||||
H: Clone,
|
|
||||||
{
|
|
||||||
/// Create a new instance of `StorageHandlerManager`.
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
handlers: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Register a storage device handler.
|
|
||||||
pub fn add_handler(&mut self, ids: &[&str], handler: H) -> Result<()> {
|
|
||||||
for &id in ids {
|
|
||||||
match self.handlers.entry(id.to_string()) {
|
|
||||||
Entry::Occupied(_) => {
|
|
||||||
return Err(anyhow!("storage handler for {} already exists", id));
|
|
||||||
}
|
|
||||||
Entry::Vacant(entry) => {
|
|
||||||
entry.insert(handler.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get storage handler with specified `id`.
|
|
||||||
pub fn handler(&self, id: &str) -> Option<&H> {
|
|
||||||
self.handlers.get(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get names of registered handlers.
|
|
||||||
pub fn get_handlers(&self) -> Vec<String> {
|
|
||||||
self.handlers.keys().map(|v| v.to_string()).collect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Join user provided volume path with kata direct-volume root path.
|
/// Join user provided volume path with kata direct-volume root path.
|
||||||
///
|
///
|
||||||
/// The `volume_path` is base64-url-encoded and then safely joined to the `prefix`
|
/// The `volume_path` is base64-url-encoded and then safely joined to the `prefix`
|
||||||
|
Loading…
Reference in New Issue
Block a user