From 43365ade2e226454f5c79211ef6672e5ae52b198 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Wed, 11 Aug 2021 10:53:21 +0000 Subject: [PATCH] vmm, pci: Implement virtio-mem support for vfio-user Implement the infrastructure that lets a virtio-mem device map the guest memory into the device. This is necessary since with virtio-mem zones memory can be added or removed and the vfio-user device must be informed. Fixes: #3025 Signed-off-by: Rob Bradford --- pci/src/lib.rs | 2 +- pci/src/vfio_user.rs | 60 ++++++++++++++++++++++++++- virtio-devices/src/seccomp_filters.rs | 2 + vmm/src/device_manager.rs | 26 +++++++++++- 4 files changed, 86 insertions(+), 4 deletions(-) diff --git a/pci/src/lib.rs b/pci/src/lib.rs index 5d9f2f6d6..3803cddda 100644 --- a/pci/src/lib.rs +++ b/pci/src/lib.rs @@ -26,7 +26,7 @@ pub use self::device::{ pub use self::msi::{msi_num_enabled_vectors, MsiCap, MsiConfig}; pub use self::msix::{MsixCap, MsixConfig, MsixTableEntry, MSIX_TABLE_ENTRY_SIZE}; pub use self::vfio::{VfioPciDevice, VfioPciError}; -pub use self::vfio_user::{VfioUserPciDevice, VfioUserPciDeviceError}; +pub use self::vfio_user::{VfioUserDmaMapping, VfioUserPciDevice, VfioUserPciDeviceError}; /// PCI has four interrupt pins A->D. #[derive(Copy, Clone)] diff --git a/pci/src/vfio_user.rs b/pci/src/vfio_user.rs index 404f8ba19..29887de0a 100644 --- a/pci/src/vfio_user.rs +++ b/pci/src/vfio_user.rs @@ -19,10 +19,14 @@ use vfio_bindings::bindings::vfio::*; use vfio_ioctls::VfioIrq; use vfio_user::{Client, Error as VfioUserError}; use vm_allocator::SystemAllocator; +use vm_device::dma_mapping::ExternalDmaMapping; use vm_device::interrupt::{InterruptManager, InterruptSourceGroup, MsiIrqGroupConfig}; use vm_device::BusDevice; use vm_memory::bitmap::AtomicBitmap; -use vm_memory::{Address, GuestAddress, GuestMemoryRegion, GuestRegionMmap, GuestUsize}; +use vm_memory::{ + Address, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryRegion, GuestRegionMmap, + GuestUsize, +}; use vmm_sys_util::eventfd::EventFd; pub struct VfioUserPciDevice { @@ -483,3 +487,57 @@ impl Drop for VfioUserPciDevice { } } } + +pub struct VfioUserDmaMapping { + client: Arc>, + memory: Arc, +} + +impl VfioUserDmaMapping { + pub fn new(client: Arc>, memory: Arc) -> Self { + Self { client, memory } + } +} + +impl ExternalDmaMapping for VfioUserDmaMapping { + fn map(&self, iova: u64, gpa: u64, size: u64) -> std::result::Result<(), std::io::Error> { + let mem = self.memory.memory(); + let guest_addr = GuestAddress(gpa); + let region = mem.find_region(guest_addr); + + if let Some(region) = region { + let file_offset = region.file_offset().unwrap(); + let offset = (GuestAddress(gpa).checked_offset_from(region.start_addr())).unwrap() + + file_offset.start(); + + self.client + .lock() + .unwrap() + .dma_map(offset, iova, size, file_offset.file().as_raw_fd()) + .map_err(|e| { + std::io::Error::new( + std::io::ErrorKind::Other, + format!("Error mapping region: {}", e), + ) + }) + } else { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!("Region not found for 0x{:x}", gpa), + )); + } + } + + fn unmap(&self, iova: u64, size: u64) -> std::result::Result<(), std::io::Error> { + self.client + .lock() + .unwrap() + .dma_unmap(iova, size) + .map_err(|e| { + std::io::Error::new( + std::io::ErrorKind::Other, + format!("Error unmapping region: {}", e), + ) + }) + } +} diff --git a/virtio-devices/src/seccomp_filters.rs b/virtio-devices/src/seccomp_filters.rs index b50250c59..882bfd54e 100644 --- a/virtio-devices/src/seccomp_filters.rs +++ b/virtio-devices/src/seccomp_filters.rs @@ -123,6 +123,8 @@ fn virtio_mem_thread_rules() -> Vec<(i64, Vec)> { vec![ (libc::SYS_fallocate, vec![]), (libc::SYS_ioctl, create_virtio_mem_ioctl_seccomp_rule()), + (libc::SYS_recvfrom, vec![]), + (libc::SYS_sendmsg, vec![]), ] } diff --git a/vmm/src/device_manager.rs b/vmm/src/device_manager.rs index 22241445e..929a4d649 100644 --- a/vmm/src/device_manager.rs +++ b/vmm/src/device_manager.rs @@ -69,7 +69,7 @@ use libc::{ }; use pci::{ DeviceRelocation, PciBarRegionType, PciBus, PciConfigMmio, PciDevice, PciRoot, VfioPciDevice, - VfioUserPciDevice, VfioUserPciDeviceError, + VfioUserDmaMapping, VfioUserPciDevice, VfioUserPciDeviceError, }; #[cfg(target_arch = "x86_64")] use pci::{PciConfigIo, PCI_CONFIG_IO_PORT, PCI_CONFIG_IO_PORT_SIZE}; @@ -3115,7 +3115,7 @@ impl DeviceManager { let mut vfio_user_pci_device = VfioUserPciDevice::new( &self.address_manager.vm, - client, + client.clone(), &self.msi_interrupt_manager, legacy_interrupt_group, ) @@ -3127,6 +3127,19 @@ impl DeviceManager { }) .map_err(DeviceManagerError::VfioUserMapRegion)?; + let memory = self.memory_manager.lock().unwrap().guest_memory(); + let vfio_user_mapping = Arc::new(VfioUserDmaMapping::new(client, Arc::new(memory))); + for virtio_mem_device in self.virtio_mem_devices.iter() { + virtio_mem_device + .lock() + .unwrap() + .add_dma_mapping_handler( + VirtioMemMappingSource::Device(pci_device_bdf), + vfio_user_mapping.clone(), + ) + .map_err(DeviceManagerError::AddDmaMappingHandlerVirtioMem)?; + } + for (_, zone) in self.memory_manager.lock().unwrap().memory_zones().iter() { for region in zone.regions() { vfio_user_pci_device @@ -3564,6 +3577,7 @@ impl DeviceManager { .pci_device_handle .ok_or(DeviceManagerError::MissingPciDevice)?; let (pci_device, bus_device, virtio_device) = match pci_device_handle { + // No need to remove any virtio-mem mapping here as the container outlives all devices PciDeviceHandle::Vfio(vfio_pci_device) => ( Arc::clone(&vfio_pci_device) as Arc>, Arc::clone(&vfio_pci_device) as Arc>, @@ -3594,6 +3608,14 @@ impl DeviceManager { } } + for virtio_mem_device in self.virtio_mem_devices.iter() { + virtio_mem_device + .lock() + .unwrap() + .remove_dma_mapping_handler(VirtioMemMappingSource::Device(pci_device_bdf)) + .map_err(DeviceManagerError::RemoveDmaMappingHandlerVirtioMem)?; + } + ( Arc::clone(&vfio_user_pci_device) as Arc>, Arc::clone(&vfio_user_pci_device) as Arc>,