From 023dde42b55a58c7a41293566e95a0fc94efa2c6 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Thu, 19 Aug 2021 20:48:25 +0800 Subject: [PATCH 7/8] vfio: fix the problem of dma mapping failed Failed to use libvirt to manager more than two vfio devices. The reason is that creating a container for every vfio device, the process stratovirt uses more than the memory locked limit. Signed-off-by: Xinle.Guo --- machine/src/lib.rs | 24 +++++++++++------------- vfio/src/vfio_dev.rs | 25 +++++++++++++++---------- vfio/src/vfio_pci.rs | 31 ++++++++++++++++++------------- 3 files changed, 44 insertions(+), 36 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 9eb3039..8ba0ba3 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -121,7 +121,7 @@ use devices::legacy::FwCfgOps; #[cfg(target_arch = "aarch64")] use devices::InterruptController; use hypervisor::KVM_FDS; -use kvm_ioctls::{DeviceFd, VcpuFd}; +use kvm_ioctls::VcpuFd; use machine_manager::config::{ get_pci_bdf, parse_balloon, parse_blk, parse_net, parse_rng_dev, parse_root_port, parse_vfio, parse_virtconsole, parse_virtio_serial, parse_vsock, MachineMemConfig, PFlashConfig, PciBdf, @@ -132,7 +132,7 @@ use machine_manager::machine::{KvmVmState, MachineInterface}; use migration::MigrationManager; use util::loop_context::{EventNotifier, NotifierCallback, NotifierOperation}; use util::seccomp::{BpfRule, SeccompOpt, SyscallFilter}; -use vfio::vfio_pci::create_vfio_device; +use vfio::vfio_pci::create_vfio_container; use vfio::{VfioContainer, VfioPciDevice}; use virtio::{balloon_allow_list, Balloon, Block, Console, Rng, VirtioMmioDevice, VirtioPciDevice}; use vmm_sys_util::epoll::EventSet; @@ -478,13 +478,8 @@ pub trait MachineOps { &mut self, vm_config: &VmConfig, cfg_args: &str, - dev_fd: Arc, + container: Arc, ) -> Result<()> { - let sys_mem = self.get_sys_mem().clone(); - let container = Arc::new( - VfioContainer::new(dev_fd, &sys_mem).chain_err(|| "Failed to create vfio container")?, - ); - let device_cfg: VfioConfig = parse_vfio(vm_config, cfg_args)?; let path = "/sys/bus/pci/devices/".to_string() + &device_cfg.host; let name = device_cfg.id; @@ -543,10 +538,7 @@ pub trait MachineOps { .chain_err(|| ErrorKind::AddDevErr("pflash".to_string()))?; } - // Create an emulated kvm device that is used for VFIO. It should be created only once. - // See the kernel docs for `KVM_CREATE_DEVICE` to get more info. - let vfio_dev = create_vfio_device().chain_err(|| "Failed to create kvm device for VFIO")?; - + let mut container: Option> = None; for dev in &cloned_vm_config.devices { let cfg_args = dev.1.as_str(); match dev.0.as_str() { @@ -581,7 +573,13 @@ pub trait MachineOps { self.add_virtio_rng(vm_config, cfg_args)?; } "vfio-pci" => { - self.add_vfio_device(&vm_config, cfg_args, vfio_dev.clone())?; + if container.is_none() { + container = Some( + create_vfio_container(self.get_sys_mem().clone()) + .chain_err(|| "Failed to create vfio container")?, + ); + } + self.add_vfio_device(&vm_config, cfg_args, container.clone().unwrap())?; } _ => { bail!("Unsupported device: {:?}", dev.0.as_str()); diff --git a/vfio/src/vfio_dev.rs b/vfio/src/vfio_dev.rs index 30f7d5a..93922ba 100644 --- a/vfio/src/vfio_dev.rs +++ b/vfio/src/vfio_dev.rs @@ -96,8 +96,8 @@ pub struct VfioMemoryRegion { pub memory_size: u64, // Host virtual address. pub userspace_addr: u64, - // No flags specified for now. - flags_padding: u64, + // IOMMU mapped flag. + pub iommu_mapped: bool, } /// `VfioMemInfo` structure contains pinning pages information. If any pages need to be zapped from @@ -131,7 +131,7 @@ impl VfioMemInfo { guest_phys_addr, memory_size, userspace_addr, - flags_padding: 0_u64, + iommu_mapped: false, }); Ok(()) @@ -150,7 +150,7 @@ impl VfioMemInfo { guest_phys_addr: fr.addr_range.base.raw_value(), memory_size: fr.addr_range.size, userspace_addr: hva + fr.offset_in_region, - flags_padding: 0_u64, + iommu_mapped: false, }; let mut mem_regions = self.regions.lock().unwrap(); for (index, mr) in mem_regions.iter().enumerate() { @@ -378,9 +378,11 @@ impl VfioGroup { return Err(ErrorKind::VfioIoctl("VFIO_GROUP_SET_CONTAINER".to_string(), ret).into()); } - if let Err(e) = container.set_iommu(vfio::VFIO_TYPE1v2_IOMMU) { - unsafe { ioctl_with_ref(&self.group, VFIO_GROUP_UNSET_CONTAINER(), &raw_fd) }; - return Err(e).chain_err(|| "Failed to set IOMMU"); + if container.groups.lock().unwrap().is_empty() { + if let Err(e) = container.set_iommu(vfio::VFIO_TYPE1v2_IOMMU) { + unsafe { ioctl_with_ref(&self.group, VFIO_GROUP_UNSET_CONTAINER(), &raw_fd) }; + return Err(e).chain_err(|| "Failed to set IOMMU"); + } } if let Err(e) = container.kvm_device_add_group(&self.group.as_raw_fd()) { @@ -473,15 +475,18 @@ impl VfioDevice { group_id = n.parse::().chain_err(|| "Invalid iommu group id")?; } - let mut groups = container.groups.lock().unwrap(); - if let Some(g) = groups.get(&group_id) { + if let Some(g) = container.groups.lock().unwrap().get(&group_id) { return Ok(g.clone()); } let group = Arc::new(VfioGroup::new(group_id)?); group .connect_container(&container) .chain_err(|| "Fail to connect container")?; - groups.insert(group_id, group.clone()); + container + .groups + .lock() + .unwrap() + .insert(group_id, group.clone()); Ok(group) } diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index 5cc674a..7bec445 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -19,13 +19,12 @@ use std::sync::{Arc, Mutex, Weak}; use byteorder::{ByteOrder, LittleEndian}; use error_chain::ChainedError; use kvm_bindings::{kvm_create_device, kvm_device_type_KVM_DEV_TYPE_VFIO}; -use kvm_ioctls::DeviceFd; use vfio_bindings::bindings::vfio; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::ioctl::ioctl_with_mut_ref; use super::errors::{ErrorKind, Result, ResultExt}; -use address_space::{FileBackend, GuestAddress, HostMemMapping, Region, RegionOps}; +use address_space::{AddressSpace, FileBackend, GuestAddress, HostMemMapping, Region, RegionOps}; use hypervisor::{MsiVector, KVM_FDS}; #[cfg(target_arch = "aarch64")] use pci::config::SECONDARY_BUS_NUM; @@ -380,7 +379,7 @@ impl VfioPciDevice { .register_bar(i as usize, bar_region, vfio_bar.region_type, false, size); } - self.map_guest_memory()?; + self.do_dma_map()?; Ok(()) } @@ -569,14 +568,17 @@ impl VfioPciDevice { } /// Add all guest memory regions into IOMMU table. - fn map_guest_memory(&mut self) -> Result<()> { + fn do_dma_map(&mut self) -> Result<()> { let container = &self.vfio_device.container; - let regions = container.vfio_mem_info.regions.lock().unwrap(); - - for r in regions.iter() { - container - .vfio_dma_map(r.guest_phys_addr, r.memory_size, r.userspace_addr) - .chain_err(|| "Failed to add guest memory region map into IOMMU table")?; + let mut regions = container.vfio_mem_info.regions.lock().unwrap(); + + for r in regions.iter_mut() { + if !r.iommu_mapped { + container + .vfio_dma_map(r.guest_phys_addr, r.memory_size, r.userspace_addr) + .chain_err(|| "Failed to add guest memory region map into IOMMU table")?; + r.iommu_mapped = true; + } } Ok(()) } @@ -872,7 +874,7 @@ fn get_irq_rawfds(gsi_msi_routes: &[GsiMsiRoute]) -> Vec { rawfds } -pub fn create_vfio_device() -> Result> { +pub fn create_vfio_container(sys_mem: Arc) -> Result> { let mut vfio_device = kvm_create_device { type_: kvm_device_type_KVM_DEV_TYPE_VFIO, fd: 0, @@ -884,7 +886,10 @@ pub fn create_vfio_device() -> Result> { .as_ref() .unwrap() .create_device(&mut vfio_device) - .chain_err(|| "Failed to create VFIO type KVM device")?; + .chain_err(|| "Failed to create kvm device for VFIO")?; - Ok(Arc::new(dev_fd)) + Ok(Arc::new( + VfioContainer::new(Arc::new(dev_fd), &sys_mem) + .chain_err(|| "Failed to create vfio container")?, + )) } -- 2.25.1