diff --git a/src/agent/src/device.rs b/src/agent/src/device.rs index 67d35651bd..53fe77b1b4 100644 --- a/src/agent/src/device.rs +++ b/src/agent/src/device.rs @@ -34,7 +34,7 @@ macro_rules! sl { } const VM_ROOTFS: &str = "/"; - +const BLOCK: &str = "block"; pub const DRIVER_9P_TYPE: &str = "9p"; pub const DRIVER_VIRTIOFS_TYPE: &str = "virtio-fs"; pub const DRIVER_BLK_TYPE: &str = "blk"; @@ -204,7 +204,7 @@ impl ScsiBlockMatcher { impl UeventMatcher for ScsiBlockMatcher { fn is_match(&self, uev: &Uevent) -> bool { - uev.subsystem == "block" && uev.devpath.contains(&self.search) && !uev.devname.is_empty() + uev.subsystem == BLOCK && uev.devpath.contains(&self.search) && !uev.devname.is_empty() } } @@ -238,7 +238,7 @@ impl VirtioBlkPciMatcher { impl UeventMatcher for VirtioBlkPciMatcher { fn is_match(&self, uev: &Uevent) -> bool { - uev.subsystem == "block" && self.rex.is_match(&uev.devpath) && !uev.devname.is_empty() + uev.subsystem == BLOCK && self.rex.is_match(&uev.devpath) && !uev.devname.is_empty() } } @@ -311,7 +311,7 @@ impl PmemBlockMatcher { impl UeventMatcher for PmemBlockMatcher { fn is_match(&self, uev: &Uevent) -> bool { - uev.subsystem == "block" + uev.subsystem == BLOCK && uev.devpath.starts_with(ACPI_DEV_PATH) && uev.devpath.ends_with(&self.suffix) && !uev.devname.is_empty() @@ -441,6 +441,48 @@ 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<()> { @@ -676,12 +718,18 @@ pub fn update_env_pci( #[instrument] async fn virtiommio_blk_device_handler( device: &Device, - _sandbox: &Arc>, + 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(DevNumUpdate::from_vm_path(&device.vm_path)?.into()) } @@ -1394,7 +1442,7 @@ mod tests { let mut uev = crate::uevent::Uevent::default(); uev.action = crate::linux_abi::U_EVENT_ACTION_ADD.to_string(); - uev.subsystem = "block".to_string(); + uev.subsystem = BLOCK.to_string(); uev.devpath = devpath.clone(); uev.devname = devname.to_string(); @@ -1428,7 +1476,7 @@ mod tests { 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.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); @@ -1512,7 +1560,7 @@ mod tests { 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.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", @@ -1555,6 +1603,33 @@ mod tests { assert!(!matcher_a.is_match(&uev_b)); } + #[tokio::test] + async fn test_mmio_block_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 = MmioBlockMatcher::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 = MmioBlockMatcher::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)); + } + #[test] fn test_split_vfio_pci_option() { assert_eq!( diff --git a/src/agent/src/mount.rs b/src/agent/src/mount.rs index 0eff266f24..5b0d95c19c 100644 --- a/src/agent/src/mount.rs +++ b/src/agent/src/mount.rs @@ -21,10 +21,11 @@ use nix::unistd::{Gid, Uid}; use regex::Regex; use crate::device::{ - get_scsi_device_name, get_virtio_blk_pci_device_name, online_device, wait_for_pmem_device, - DRIVER_9P_TYPE, DRIVER_BLK_CCW_TYPE, DRIVER_BLK_TYPE, DRIVER_EPHEMERAL_TYPE, DRIVER_LOCAL_TYPE, - DRIVER_MMIO_BLK_TYPE, DRIVER_NVDIMM_TYPE, DRIVER_OVERLAYFS_TYPE, DRIVER_SCSI_TYPE, - DRIVER_VIRTIOFS_TYPE, DRIVER_WATCHABLE_BIND_TYPE, FS_TYPE_HUGETLB, + get_scsi_device_name, get_virtio_blk_pci_device_name, get_virtio_mmio_device_name, + online_device, wait_for_pmem_device, DRIVER_9P_TYPE, DRIVER_BLK_CCW_TYPE, DRIVER_BLK_TYPE, + DRIVER_EPHEMERAL_TYPE, DRIVER_LOCAL_TYPE, DRIVER_MMIO_BLK_TYPE, DRIVER_NVDIMM_TYPE, + DRIVER_OVERLAYFS_TYPE, DRIVER_SCSI_TYPE, DRIVER_VIRTIOFS_TYPE, DRIVER_WATCHABLE_BIND_TYPE, + FS_TYPE_HUGETLB, }; use crate::linux_abi::*; use crate::pci; @@ -473,8 +474,14 @@ async fn virtiommio_blk_storage_handler( storage: &Storage, sandbox: Arc>, ) -> Result { + let storage = storage.clone(); + if !Path::new(&storage.source).exists() { + get_virtio_mmio_device_name(&sandbox, &storage.source) + .await + .context("failed to get mmio device name")?; + } //The source path is VmPath - common_storage_handler(logger, storage) + common_storage_handler(logger, &storage) } // virtiofs_storage_handler handles the storage for virtio-fs.