diff --git a/src/agent/src/device.rs b/src/agent/src/device.rs index 218d6aa54c..6b3704ce5e 100644 --- a/src/agent/src/device.rs +++ b/src/agent/src/device.rs @@ -29,8 +29,8 @@ use protocols::agent::Device; use tracing::instrument; use kata_types::device::{ - DRIVER_BLK_CCW_TYPE, DRIVER_BLK_MMIO_TYPE, DRIVER_BLK_PCI_TYPE, DRIVER_NVDIMM_TYPE, - DRIVER_SCSI_TYPE, DRIVER_VFIO_AP_TYPE, DRIVER_VFIO_PCI_GK_TYPE, DRIVER_VFIO_PCI_TYPE, + DRIVER_NVDIMM_TYPE, DRIVER_SCSI_TYPE, DRIVER_VFIO_AP_TYPE, DRIVER_VFIO_PCI_GK_TYPE, + DRIVER_VFIO_PCI_TYPE, }; // Convenience function to obtain the scope logger. @@ -43,7 +43,6 @@ const BLOCK: &str = "block"; cfg_if! { if #[cfg(target_arch = "s390x")] { use crate::ap; - use crate::ccw; } } @@ -206,82 +205,6 @@ pub async fn get_scsi_device_name( Ok(format!("{}/{}", SYSTEM_DEV_PATH, &uev.devname)) } -#[derive(Debug)] -struct VirtioBlkPciMatcher { - rex: Regex, -} - -impl VirtioBlkPciMatcher { - 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() - } -} - -#[instrument] -pub async fn get_virtio_blk_pci_device_name( - sandbox: &Arc>, - 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 = VirtioBlkPciMatcher::new(&sysfs_rel_path); - - let uev = wait_for_uevent(sandbox, matcher).await?; - Ok(format!("{}/{}", SYSTEM_DEV_PATH, &uev.devname)) -} - -#[cfg(target_arch = "s390x")] -#[derive(Debug)] -struct VirtioBlkCCWMatcher { - rex: Regex, -} - -#[cfg(target_arch = "s390x")] -impl VirtioBlkCCWMatcher { - 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(target_arch = "s390x")] -#[instrument] -pub async fn get_virtio_blk_ccw_device_name( - sandbox: &Arc>, - device: &ccw::Device, -) -> Result { - let matcher = VirtioBlkCCWMatcher::new(CCW_ROOT_BUS_PATH, device); - let uev = wait_for_uevent(sandbox, matcher).await?; - let devname = uev.devname; - return match Path::new(SYSTEM_DEV_PATH).join(&devname).to_str() { - Some(path) => Ok(String::from(path)), - None => Err(anyhow!("CCW device name {} is not valid UTF-8", &devname)), - }; -} - #[derive(Debug)] struct PmemBlockMatcher { suffix: String, @@ -486,48 +409,6 @@ async fn wait_for_ap_device(sandbox: &Arc>, address: ap::Address) Ok(()) } -#[derive(Debug)] -struct MmioBlockMatcher { - suffix: String, -} - -impl MmioBlockMatcher { - fn new(devname: &str) -> MmioBlockMatcher { - MmioBlockMatcher { - suffix: format!(r"/block/{}", devname), - } - } -} - -impl UeventMatcher for MmioBlockMatcher { - fn is_match(&self, uev: &Uevent) -> bool { - uev.subsystem == BLOCK && uev.devpath.ends_with(&self.suffix) && !uev.devname.is_empty() - } -} - -#[instrument] -pub async fn get_virtio_mmio_device_name( - sandbox: &Arc>, - devpath: &str, -) -> Result<()> { - let devname = devpath - .strip_prefix("/dev/") - .ok_or_else(|| anyhow!("Storage source '{}' must start with /dev/", devpath))?; - - let matcher = MmioBlockMatcher::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(()) -} - /// Scan SCSI bus for the given SCSI address(SCSI-Id and LUN) #[instrument] fn scan_scsi_bus(scsi_addr: &str) -> Result<()> { @@ -820,61 +701,6 @@ pub fn update_env_pci( Ok(()) } -// device.Id should be the predicted device name (vda, vdb, ...) -// device.VmPath already provides a way to send it in -#[instrument] -async fn virtiommio_blk_device_handler( - device: &Device, - sandbox: &Arc>, -) -> Result { - 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_mmio_device_name(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()) -} - -// device.Id should be a PCI path string -#[instrument] -async fn virtio_blk_device_handler( - device: &Device, - sandbox: &Arc>, -) -> Result { - let pcipath = pci::Path::from_str(&device.id)?; - let vm_path = get_virtio_blk_pci_device_name(sandbox, &pcipath).await?; - - Ok(DeviceInfo::new(&vm_path, true) - .context("New device info")? - .into()) -} - -// device.id should be a CCW path string -#[cfg(target_arch = "s390x")] -#[instrument] -async fn virtio_blk_ccw_device_handler( - device: &Device, - sandbox: &Arc>, -) -> Result { - let ccw_device = ccw::Device::from_str(&device.id)?; - let vm_path = get_virtio_blk_ccw_device_name(sandbox, &ccw_device).await?; - - Ok(DeviceInfo::new(&vm_path, true)?.into()) -} - -#[cfg(not(target_arch = "s390x"))] -#[instrument] -async fn virtio_blk_ccw_device_handler(_: &Device, _: &Arc>) -> Result { - Err(anyhow!("CCW is only supported on s390x")) -} - // device.Id should be the SCSI address of the disk in the format "scsiID:lunID" #[instrument] async fn virtio_scsi_device_handler( diff --git a/src/agent/src/device/block_device_handler.rs b/src/agent/src/device/block_device_handler.rs new file mode 100644 index 0000000000..cd7567e582 --- /dev/null +++ b/src/agent/src/device/block_device_handler.rs @@ -0,0 +1,208 @@ +// 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 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] + async fn device_handler(&self, device: &Device, ctx: &mut DeviceContext) -> Result { + 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 { + #[cfg(target_arch = "s390x")] + #[instrument] + async fn device_handler(&self, device: &Device, ctx: &mut DeviceContext) -> Result { + 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 { + Err(anyhow!("CCW is only supported on s390x")) + } +} + +#[async_trait::async_trait] +impl DeviceHandler for VirtioBlkMmioDeviceHandler { + #[instrument] + async fn device_handler(&self, device: &Device, ctx: &mut DeviceContext) -> Result { + 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>, + 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 = 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>, + 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>, + device: &ccw::Device, +) -> Result { + 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() + } +}