Merge pull request #8284 from amshinde/runtime-rs-update-device-pci-info

runtime-rs: update device pci info for vfio and virtio-blk devices
This commit is contained in:
Archana Shinde 2023-11-06 01:09:20 -08:00 committed by GitHub
commit 3b2fb6a604
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 153 additions and 72 deletions

View File

@ -70,7 +70,7 @@ pub async fn cloud_hypervisor_vm_stop(mut socket: UnixStream) -> Result<Option<S
.await? .await?
} }
#[derive(Serialize, Deserialize)] #[derive(Deserialize, Debug)]
pub struct PciDeviceInfo { pub struct PciDeviceInfo {
pub id: String, pub id: String,
pub bdf: String, pub bdf: String,

View File

@ -7,16 +7,18 @@
use super::inner::CloudHypervisorInner; use super::inner::CloudHypervisorInner;
use crate::device::DeviceType; use crate::device::DeviceType;
use crate::BlockDevice; use crate::BlockDevice;
use crate::HybridVsockConfig; use crate::HybridVsockDevice;
use crate::NetworkConfig; use crate::NetworkConfig;
use crate::PciPath;
use crate::ShareFsDevice;
use crate::ShareFsDeviceConfig; use crate::ShareFsDeviceConfig;
use crate::VfioDevice; use crate::VfioDevice;
use crate::VmmState; use crate::VmmState;
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use ch_config::ch_api::cloud_hypervisor_vm_device_add;
use ch_config::ch_api::{ use ch_config::ch_api::{
cloud_hypervisor_vm_blockdev_add, cloud_hypervisor_vm_device_add, cloud_hypervisor_vm_blockdev_add, cloud_hypervisor_vm_device_remove,
cloud_hypervisor_vm_device_remove, cloud_hypervisor_vm_fs_add, PciDeviceInfo, cloud_hypervisor_vm_fs_add, PciDeviceInfo, VmRemoveDeviceData,
VmRemoveDeviceData,
}; };
use ch_config::convert::{DEFAULT_DISK_QUEUES, DEFAULT_DISK_QUEUE_SIZE, DEFAULT_NUM_PCI_SEGMENTS}; use ch_config::convert::{DEFAULT_DISK_QUEUES, DEFAULT_DISK_QUEUE_SIZE, DEFAULT_NUM_PCI_SEGMENTS};
use ch_config::DiskConfig; use ch_config::DiskConfig;
@ -31,7 +33,7 @@ pub const DEFAULT_FS_QUEUES: usize = 1;
const DEFAULT_FS_QUEUE_SIZE: u16 = 1024; const DEFAULT_FS_QUEUE_SIZE: u16 = 1024;
impl CloudHypervisorInner { impl CloudHypervisorInner {
pub(crate) async fn add_device(&mut self, device: DeviceType) -> Result<()> { pub(crate) async fn add_device(&mut self, device: DeviceType) -> Result<DeviceType> {
if self.state != VmmState::VmRunning { if self.state != VmmState::VmRunning {
// If the VM is not running, add the device to the pending list to // If the VM is not running, add the device to the pending list to
// be handled later. // be handled later.
@ -55,8 +57,8 @@ impl CloudHypervisorInner {
// - Network details need to be saved for later application. // - Network details need to be saved for later application.
// //
match device { match device {
DeviceType::ShareFs(_) => self.pending_devices.insert(0, device), DeviceType::ShareFs(_) => self.pending_devices.insert(0, device.clone()),
DeviceType::Network(_) => self.pending_devices.insert(0, device), DeviceType::Network(_) => self.pending_devices.insert(0, device.clone()),
_ => { _ => {
debug!( debug!(
sl!(), sl!(),
@ -65,20 +67,18 @@ impl CloudHypervisorInner {
} }
} }
return Ok(()); return Ok(device);
} }
self.handle_add_device(device).await?; self.handle_add_device(device).await
Ok(())
} }
async fn handle_add_device(&mut self, device: DeviceType) -> Result<()> { async fn handle_add_device(&mut self, device: DeviceType) -> Result<DeviceType> {
match device { match device {
DeviceType::ShareFs(sharefs) => self.handle_share_fs_device(sharefs.config).await, DeviceType::ShareFs(sharefs) => self.handle_share_fs_device(sharefs).await,
DeviceType::HybridVsock(hvsock) => self.handle_hvsock_device(&hvsock.config).await, DeviceType::HybridVsock(hvsock) => self.handle_hvsock_device(hvsock).await,
DeviceType::Block(block) => self.handle_block_device(block).await, DeviceType::Block(block) => self.handle_block_device(block).await,
DeviceType::Vfio(vfiodev) => self.handle_vfio_device(&vfiodev).await, DeviceType::Vfio(vfiodev) => self.handle_vfio_device(vfiodev).await,
_ => Err(anyhow!("unhandled device: {:?}", device)), _ => Err(anyhow!("unhandled device: {:?}", device)),
} }
} }
@ -108,9 +108,13 @@ impl CloudHypervisorInner {
} }
} }
async fn handle_share_fs_device(&mut self, cfg: ShareFsDeviceConfig) -> Result<()> { async fn handle_share_fs_device(&mut self, sharefs: ShareFsDevice) -> Result<DeviceType> {
if cfg.fs_type != VIRTIO_FS { let device: ShareFsDevice = sharefs.clone();
return Err(anyhow!("cannot handle share fs type: {:?}", cfg.fs_type)); if device.config.fs_type != VIRTIO_FS {
return Err(anyhow!(
"cannot handle share fs type: {:?}",
device.config.fs_type
));
} }
let socket = self let socket = self
@ -119,26 +123,26 @@ impl CloudHypervisorInner {
.ok_or("missing socket") .ok_or("missing socket")
.map_err(|e| anyhow!(e))?; .map_err(|e| anyhow!(e))?;
let num_queues: usize = if cfg.queue_num > 0 { let num_queues: usize = if device.config.queue_num > 0 {
cfg.queue_num as usize device.config.queue_num as usize
} else { } else {
DEFAULT_FS_QUEUES DEFAULT_FS_QUEUES
}; };
let queue_size: u16 = if cfg.queue_num > 0 { let queue_size: u16 = if device.config.queue_num > 0 {
u16::try_from(cfg.queue_size)? u16::try_from(device.config.queue_size)?
} else { } else {
DEFAULT_FS_QUEUE_SIZE DEFAULT_FS_QUEUE_SIZE
}; };
let socket_path = if cfg.sock_path.starts_with('/') { let socket_path = if device.config.sock_path.starts_with('/') {
PathBuf::from(cfg.sock_path) PathBuf::from(device.config.sock_path)
} else { } else {
scoped_join(&self.vm_path, cfg.sock_path)? scoped_join(&self.vm_path, device.config.sock_path)?
}; };
let fs_config = FsConfig { let fs_config = FsConfig {
tag: cfg.mount_tag, tag: device.config.mount_tag,
socket: socket_path, socket: socket_path,
num_queues, num_queues,
queue_size, queue_size,
@ -157,14 +161,16 @@ impl CloudHypervisorInner {
debug!(sl!(), "fs add response: {:?}", detail); debug!(sl!(), "fs add response: {:?}", detail);
} }
Ok(()) Ok(DeviceType::ShareFs(sharefs))
} }
async fn handle_hvsock_device(&mut self, _cfg: &HybridVsockConfig) -> Result<()> { async fn handle_hvsock_device(&mut self, device: HybridVsockDevice) -> Result<DeviceType> {
Ok(()) Ok(DeviceType::HybridVsock(device))
} }
async fn handle_vfio_device(&mut self, device: &VfioDevice) -> Result<()> { async fn handle_vfio_device(&mut self, device: VfioDevice) -> Result<DeviceType> {
let mut vfio_device: VfioDevice = device.clone();
// A device with multi-funtions, or a IOMMU group with one more // A device with multi-funtions, or a IOMMU group with one more
// devices, the Primary device is selected to be passed to VM. // devices, the Primary device is selected to be passed to VM.
// And the the first one is Primary device. // And the the first one is Primary device.
@ -204,9 +210,15 @@ impl CloudHypervisorInner {
serde_json::from_str(detail.as_str()).map_err(|e| anyhow!(e))?; serde_json::from_str(detail.as_str()).map_err(|e| anyhow!(e))?;
self.device_ids self.device_ids
.insert(device.device_id.clone(), dev_info.id); .insert(device.device_id.clone(), dev_info.id);
// Update PCI path for the vfio host device. It is safe to directly access the slice element
// here as we have already checked if it exists.
// Todo: Handle vfio-ap mediated devices - return error for them.
vfio_device.devices[0].guest_pci_path =
Some(Self::clh_pci_info_to_path(&dev_info.bdf)?);
} }
Ok(()) Ok(DeviceType::Vfio(vfio_device))
} }
async fn remove_vfio_device(&mut self, device: &VfioDevice) -> Result<()> { async fn remove_vfio_device(&mut self, device: &VfioDevice) -> Result<()> {
@ -242,7 +254,31 @@ impl CloudHypervisorInner {
Ok(()) Ok(())
} }
async fn handle_block_device(&mut self, device: BlockDevice) -> Result<()> { // Various cloud-hypervisor APIs report a PCI address in "BB:DD.F"
// form within the PciDeviceInfo struct.
// eg "0000:00:DD.F"
fn clh_pci_info_to_path(bdf: &str) -> Result<PciPath> {
let tokens: Vec<&str> = bdf.split(':').collect();
if tokens.len() != 3 || tokens[0] != "0000" || tokens[1] != "00" {
return Err(anyhow!(
"Unexpected PCI address {:?} for clh device add",
bdf
));
}
let toks: Vec<&str> = tokens[2].split('.').collect();
if toks.len() != 2 || toks[1] != "0" || toks[0].len() != 2 {
return Err(anyhow!(
"Unexpected PCI address {:?} for clh device add",
bdf
));
}
PciPath::convert_from_string(toks[0])
}
async fn handle_block_device(&mut self, device: BlockDevice) -> Result<DeviceType> {
let mut block_dev = device.clone();
let socket = self let socket = self
.api_socket .api_socket
.as_ref() .as_ref()
@ -272,9 +308,10 @@ impl CloudHypervisorInner {
let dev_info: PciDeviceInfo = let dev_info: PciDeviceInfo =
serde_json::from_str(detail.as_str()).map_err(|e| anyhow!(e))?; serde_json::from_str(detail.as_str()).map_err(|e| anyhow!(e))?;
self.device_ids.insert(device.device_id, dev_info.id); self.device_ids.insert(device.device_id, dev_info.id);
block_dev.config.pci_path = Some(Self::clh_pci_info_to_path(dev_info.bdf.as_str())?);
} }
Ok(()) Ok(DeviceType::Block(block_dev))
} }
pub(crate) async fn get_shared_devices( pub(crate) async fn get_shared_devices(

View File

@ -79,7 +79,7 @@ impl Hypervisor for CloudHypervisor {
inner.save_vm().await inner.save_vm().await
} }
async fn add_device(&self, device: DeviceType) -> Result<()> { async fn add_device(&self, device: DeviceType) -> Result<DeviceType> {
let mut inner = self.inner.write().await; let mut inner = self.inner.write().await;
inner.add_device(device).await inner.add_device(device).await
} }

View File

@ -389,7 +389,7 @@ impl VfioDevice {
.get_vfio_device_vendor(&dev_bdf) .get_vfio_device_vendor(&dev_bdf)
.context("get property device and vendor failed")?; .context("get property device and vendor failed")?;
let mut vfio_dev = HostDevice { let vfio_dev = HostDevice {
bus_slot_func: dev_bdf.clone(), bus_slot_func: dev_bdf.clone(),
device_vendor: Some(dev_vendor), device_vendor: Some(dev_vendor),
sysfs_path: vfio_dev_details.1, sysfs_path: vfio_dev_details.1,
@ -397,18 +397,6 @@ impl VfioDevice {
..Default::default() ..Default::default()
}; };
// when vfio pci, kata-agent handles with device_options, and its
// format: "DDDD:BB:DD.F=<pcipath>"
// DDDD:BB:DD.F is the device's PCI address on host
// <pcipath> is the device's PCI path in the guest
if self.bus_mode == VfioBusMode::PCI {
let pci_path =
generate_guest_pci_path(dev_bdf.clone()).context("generate pci path failed")?;
vfio_dev.guest_pci_path = Some(pci_path.clone());
self.device_options
.push(format!("0000:{}={}", dev_bdf, pci_path.convert_to_string()));
}
Ok(vfio_dev) Ok(vfio_dev)
} }
@ -493,13 +481,42 @@ impl Device for VfioDevice {
} }
// do add device for vfio deivce // do add device for vfio deivce
if let Err(e) = h.add_device(DeviceType::Vfio(self.clone())).await { match h.add_device(DeviceType::Vfio(self.clone())).await {
self.decrease_attach_count().await?; Ok(dev) => {
// Update device info with the one received from device attach
if let DeviceType::Vfio(vfio) = dev {
self.config = vfio.config;
self.devices = vfio.devices;
}
return Err(e); if self.bus_mode == VfioBusMode::PCI {
for hostdev in self.devices.iter_mut() {
if hostdev.guest_pci_path.is_none() {
// guest_pci_path may be empty for certain hypervisors such as
// dragonball
hostdev.guest_pci_path = Some(
generate_guest_pci_path(hostdev.bus_slot_func.clone())
.map_err(|e| anyhow!("generate pci path failed: {:?}", e))?,
);
}
// Safe to call unwrap here because of previous assignment.
let pci_path = hostdev.guest_pci_path.clone().unwrap();
self.device_options.push(format!(
"0000:{}={}",
hostdev.bus_slot_func.clone(),
pci_path.convert_to_string()
));
}
}
Ok(())
}
Err(e) => {
self.decrease_attach_count().await?;
return Err(e);
}
} }
Ok(())
} }
async fn detach(&mut self, h: &dyn hypervisor) -> Result<Option<u64>> { async fn detach(&mut self, h: &dyn hypervisor) -> Result<Option<u64>> {

View File

@ -7,6 +7,7 @@
use crate::device::Device; use crate::device::Device;
use crate::device::DeviceType; use crate::device::DeviceType;
use crate::Hypervisor as hypervisor; use crate::Hypervisor as hypervisor;
use crate::PciPath;
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use async_trait::async_trait; use async_trait::async_trait;
@ -39,6 +40,9 @@ pub struct BlockConfig {
/// device path in guest /// device path in guest
pub virt_path: String, pub virt_path: String,
/// pci path is the slot at which the drive is attached
pub pci_path: Option<PciPath>,
/// device attach count /// device attach count
pub attach_count: u64, pub attach_count: u64,
@ -78,11 +82,20 @@ impl Device for BlockDevice {
{ {
return Ok(()); return Ok(());
} }
if let Err(e) = h.add_device(DeviceType::Block(self.clone())).await {
self.decrease_attach_count().await?; match h.add_device(DeviceType::Block(self.clone())).await {
return Err(e); Ok(dev) => {
// Update device info with the one received from device attach
if let DeviceType::Block(blk) = dev {
self.config = blk.config;
}
Ok(())
}
Err(e) => {
self.decrease_attach_count().await?;
return Err(e);
}
} }
return Ok(());
} }
async fn detach(&mut self, h: &dyn hypervisor) -> Result<Option<u64>> { async fn detach(&mut self, h: &dyn hypervisor) -> Result<Option<u64>> {

View File

@ -10,7 +10,7 @@ use crate::device::driver::vhost_user_blk::VhostUserBlkDevice;
use crate::{ use crate::{
BlockConfig, BlockDevice, HybridVsockConfig, HybridVsockDevice, Hypervisor as hypervisor, BlockConfig, BlockDevice, HybridVsockConfig, HybridVsockDevice, Hypervisor as hypervisor,
NetworkConfig, NetworkDevice, ShareFsDevice, ShareFsDeviceConfig, ShareFsMountConfig, NetworkConfig, NetworkDevice, ShareFsDevice, ShareFsDeviceConfig, ShareFsMountConfig,
ShareFsMountDevice, VfioConfig, VfioDevice, VhostUserConfig, VsockConfig, VsockDevice, ShareFsMountDevice, VfioConfig, VfioDevice, VhostUserConfig, VsockConfig,
}; };
use anyhow::Result; use anyhow::Result;
use async_trait::async_trait; use async_trait::async_trait;
@ -31,7 +31,7 @@ pub enum DeviceConfig {
HybridVsockCfg(HybridVsockConfig), HybridVsockCfg(HybridVsockConfig),
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub enum DeviceType { pub enum DeviceType {
Block(BlockDevice), Block(BlockDevice),
VhostUserBlk(VhostUserBlkDevice), VhostUserBlk(VhostUserBlkDevice),
@ -40,7 +40,6 @@ pub enum DeviceType {
ShareFs(ShareFsDevice), ShareFs(ShareFsDevice),
ShareFsMount(ShareFsMountDevice), ShareFsMount(ShareFsMountDevice),
HybridVsock(HybridVsockDevice), HybridVsock(HybridVsockDevice),
Vsock(VsockDevice),
} }
impl fmt::Display for DeviceType { impl fmt::Display for DeviceType {

View File

@ -74,9 +74,6 @@ impl DragonballInner {
DeviceType::ShareFsMount(sharefs_mount) => self DeviceType::ShareFsMount(sharefs_mount) => self
.add_share_fs_mount(&sharefs_mount.config) .add_share_fs_mount(&sharefs_mount.config)
.context("add share fs mount"), .context("add share fs mount"),
DeviceType::Vsock(_) => {
todo!()
}
} }
} }

View File

@ -92,9 +92,12 @@ impl Hypervisor for Dragonball {
inner.resize_vcpu(old_vcpus, new_vcpus).await inner.resize_vcpu(old_vcpus, new_vcpus).await
} }
async fn add_device(&self, device: DeviceType) -> Result<()> { async fn add_device(&self, device: DeviceType) -> Result<DeviceType> {
let mut inner = self.inner.write().await; let mut inner = self.inner.write().await;
inner.add_device(device).await match inner.add_device(device.clone()).await {
Ok(_) => Ok(device),
Err(err) => Err(err),
}
} }
async fn remove_device(&self, device: DeviceType) -> Result<()> { async fn remove_device(&self, device: DeviceType) -> Result<()> {

View File

@ -85,7 +85,7 @@ pub trait Hypervisor: std::fmt::Debug + Send + Sync {
async fn resize_vcpu(&self, old_vcpus: u32, new_vcpus: u32) -> Result<(u32, u32)>; // returns (old_vcpus, new_vcpus) async fn resize_vcpu(&self, old_vcpus: u32, new_vcpus: u32) -> Result<(u32, u32)>; // returns (old_vcpus, new_vcpus)
// device manager // device manager
async fn add_device(&self, device: DeviceType) -> Result<()>; async fn add_device(&self, device: DeviceType) -> Result<DeviceType>;
async fn remove_device(&self, device: DeviceType) -> Result<()>; async fn remove_device(&self, device: DeviceType) -> Result<()>;
// utils // utils

View File

@ -146,9 +146,9 @@ use crate::device::DeviceType;
// device manager part of Hypervisor // device manager part of Hypervisor
impl QemuInner { impl QemuInner {
pub(crate) async fn add_device(&mut self, device: DeviceType) -> Result<()> { pub(crate) async fn add_device(&mut self, device: DeviceType) -> Result<DeviceType> {
info!(sl!(), "QemuInner::add_device() {}", device); info!(sl!(), "QemuInner::add_device() {}", device);
todo!() Ok(device)
} }
pub(crate) async fn remove_device(&mut self, device: DeviceType) -> Result<()> { pub(crate) async fn remove_device(&mut self, device: DeviceType) -> Result<()> {

View File

@ -74,7 +74,7 @@ impl Hypervisor for Qemu {
inner.save_vm().await inner.save_vm().await
} }
async fn add_device(&self, device: DeviceType) -> Result<()> { async fn add_device(&self, device: DeviceType) -> Result<DeviceType> {
let mut inner = self.inner.write().await; let mut inner = self.inner.write().await;
inner.add_device(device).await inner.add_device(device).await
} }

View File

@ -297,8 +297,16 @@ impl ResourceManagerInner {
// create block device for kata agent, // create block device for kata agent,
// if driver is virtio-blk-pci, the id will be pci address. // if driver is virtio-blk-pci, the id will be pci address.
if let DeviceType::Block(device) = device_info { if let DeviceType::Block(device) = device_info {
// The following would work for drivers virtio-blk-pci and mmio.
// Once scsi support is added, need to handle scsi identifiers.
let id = if let Some(pci_path) = device.config.pci_path {
pci_path.convert_to_string()
} else {
device.config.virt_path.clone()
};
let agent_device = Device { let agent_device = Device {
id: device.config.virt_path.clone(), id,
container_path: d.path.clone(), container_path: d.path.clone(),
field_type: device.config.driver_option, field_type: device.config.driver_option,
vm_path: device.config.virt_path, vm_path: device.config.virt_path,

View File

@ -89,9 +89,16 @@ pub(crate) async fn setup_inline_virtiofs(id: &str, h: &dyn Hypervisor) -> Resul
prefetch_list_path: None, prefetch_list_path: None,
}, },
}; };
h.add_device(DeviceType::ShareFsMount(virtio_fs))
let result = h
.add_device(DeviceType::ShareFsMount(virtio_fs))
.await .await
.with_context(|| format!("fail to attach passthrough fs {:?}", source)) .with_context(|| format!("fail to attach passthrough fs {:?}", source));
match result {
Ok(_) => Ok(()),
Err(e) => Err(e),
}
} }
pub async fn rafs_mount( pub async fn rafs_mount(