agent/device: Rework uevent handling for virtio-blk devices

There are some problems with get_pci_device_name():

1) It's misnamed: in fact it is only used for handling virtio-blk PCI
   devices.  It's also only correct for virtio-blk devices, the event
   matching doesn't locate the "raw" PCI device, but rather the block
   device created by virtio-blk as a child of the PCI device itself.

2) The uevent matching is imprecise.  As all things using the legacy
   DevAddrMatcher, it matches on a bunch of conditions used across several
   different device types, not all of which make sense for virtio-blk pci
   devices specifically.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
This commit is contained in:
David Gibson 2021-03-12 09:48:55 +11:00
parent 15c2d7ed30
commit 484a364729
2 changed files with 54 additions and 5 deletions

View File

@ -5,6 +5,7 @@
use libc::{c_uint, major, minor}; use libc::{c_uint, major, minor};
use nix::sys::stat; use nix::sys::stat;
use regex::Regex;
use std::collections::HashMap; use std::collections::HashMap;
use std::fs; use std::fs;
use std::os::unix::fs::MetadataExt; use std::os::unix::fs::MetadataExt;
@ -148,15 +149,39 @@ pub async fn get_scsi_device_name(
get_device_name(sandbox, &dev_sub_path).await get_device_name(sandbox, &dev_sub_path).await
} }
pub async fn get_pci_device_name( #[derive(Debug)]
struct VirtioBlkPciMatcher {
rex: Regex,
}
impl VirtioBlkPciMatcher {
fn new(relpath: &str) -> Result<VirtioBlkPciMatcher> {
let root_bus = create_pci_root_bus_path();
let re = format!(r"^{}{}/virtio[0-9]+/block/", root_bus, relpath);
Ok(VirtioBlkPciMatcher {
rex: Regex::new(&re).unwrap(),
})
}
}
impl UeventMatcher for VirtioBlkPciMatcher {
fn is_match(&self, uev: &Uevent) -> bool {
uev.subsystem == "block" && self.rex.is_match(&uev.devpath) && !uev.devname.is_empty()
}
}
pub async fn get_virtio_blk_pci_device_name(
sandbox: &Arc<Mutex<Sandbox>>, sandbox: &Arc<Mutex<Sandbox>>,
pcipath: &pci::Path, pcipath: &pci::Path,
) -> Result<String> { ) -> Result<String> {
let root_bus_sysfs = format!("{}{}", SYSFS_DIR, create_pci_root_bus_path()); let root_bus_sysfs = format!("{}{}", SYSFS_DIR, create_pci_root_bus_path());
let sysfs_rel_path = pcipath_to_sysfs(&root_bus_sysfs, pcipath)?; let sysfs_rel_path = pcipath_to_sysfs(&root_bus_sysfs, pcipath)?;
let matcher = VirtioBlkPciMatcher::new(&sysfs_rel_path)?;
rescan_pci_bus()?; rescan_pci_bus()?;
get_device_name(sandbox, &sysfs_rel_path).await
let uev = wait_for_uevent(sandbox, matcher).await?;
Ok(format!("{}/{}", SYSTEM_DEV_PATH, &uev.devname))
} }
pub async fn get_pmem_device_name( pub async fn get_pmem_device_name(
@ -305,7 +330,7 @@ async fn virtio_blk_device_handler(
// path for cloud-hypervisor when BDF information is not available // path for cloud-hypervisor when BDF information is not available
if !device.id.is_empty() { if !device.id.is_empty() {
let pcipath = pci::Path::from_str(&device.id)?; let pcipath = pci::Path::from_str(&device.id)?;
dev.vm_path = get_pci_device_name(sandbox, &pcipath).await?; dev.vm_path = get_virtio_blk_pci_device_name(sandbox, &pcipath).await?;
} }
update_spec_device_list(&dev, spec, devidx) update_spec_device_list(&dev, spec, devidx)
@ -821,4 +846,28 @@ mod tests {
assert!(name.is_ok(), "{}", name.unwrap_err()); assert!(name.is_ok(), "{}", name.unwrap_err());
assert_eq!(name.unwrap(), format!("{}/{}", SYSTEM_DEV_PATH, devname)); assert_eq!(name.unwrap(), format!("{}/{}", SYSTEM_DEV_PATH, devname));
} }
#[tokio::test]
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).unwrap();
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).unwrap();
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));
}
} }

View File

@ -24,7 +24,7 @@ use std::fs::File;
use std::io::{BufRead, BufReader}; use std::io::{BufRead, BufReader};
use crate::device::{ use crate::device::{
get_pci_device_name, get_pmem_device_name, get_scsi_device_name, online_device, get_pmem_device_name, get_scsi_device_name, get_virtio_blk_pci_device_name, online_device,
}; };
use crate::linux_abi::*; use crate::linux_abi::*;
use crate::pci; use crate::pci;
@ -342,7 +342,7 @@ async fn virtio_blk_storage_handler(
} }
} else { } else {
let pcipath = pci::Path::from_str(&storage.source)?; let pcipath = pci::Path::from_str(&storage.source)?;
let dev_path = get_pci_device_name(&sandbox, &pcipath).await?; let dev_path = get_virtio_blk_pci_device_name(&sandbox, &pcipath).await?;
storage.source = dev_path; storage.source = dev_path;
} }