pci: vfio_user: Allow a BAR to have multiple user memory regions

Similar to what's being supported for vfio devices, vfio-user devices
may also have BARs that need multiple kvm user memory regions,
e.g. device regions with `VFIO_REGION_INFO_CAP_SPARSE_MMAP`.

Signed-off-by: Bo Chen <chen.bo@intel.com>
This commit is contained in:
Bo Chen 2022-04-28 14:12:12 -07:00 committed by Sebastien Boeuf
parent 1dfe4eda5c
commit bf39146caa
2 changed files with 66 additions and 54 deletions

View File

@ -211,10 +211,10 @@ impl Interrupt {
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct UserMemoryRegion { pub struct UserMemoryRegion {
slot: u32, pub slot: u32,
start: u64, pub start: u64,
size: u64, pub size: u64,
host_addr: u64, pub host_addr: u64,
} }
#[derive(Clone)] #[derive(Clone)]
@ -223,7 +223,6 @@ pub struct MmioRegion {
pub length: GuestUsize, pub length: GuestUsize,
pub(crate) type_: PciBarRegionType, pub(crate) type_: PciBarRegionType,
pub(crate) index: u32, pub(crate) index: u32,
pub(crate) mem_slot: Option<u32>,
pub(crate) host_addr: Option<u64>, pub(crate) host_addr: Option<u64>,
pub(crate) mmap_size: Option<usize>, pub(crate) mmap_size: Option<usize>,
pub(crate) user_memory_regions: Vec<UserMemoryRegion>, pub(crate) user_memory_regions: Vec<UserMemoryRegion>,
@ -546,7 +545,6 @@ impl VfioCommon {
length: region_size, length: region_size,
type_: region_type, type_: region_type,
index: bar_id as u32, index: bar_id as u32,
mem_slot: None,
host_addr: None, host_addr: None,
mmap_size: None, mmap_size: None,
user_memory_regions: Vec::new(), user_memory_regions: Vec::new(),

View File

@ -3,7 +3,7 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// //
use crate::vfio::{Interrupt, Vfio, VfioCommon, VfioError}; use crate::vfio::{Interrupt, UserMemoryRegion, Vfio, VfioCommon, VfioError};
use crate::{BarReprogrammingParams, PciBarConfiguration, VfioPciError}; use crate::{BarReprogrammingParams, PciBarConfiguration, VfioPciError};
use crate::{ use crate::{
PciBdf, PciClassCode, PciConfiguration, PciDevice, PciDeviceError, PciHeaderType, PciSubclass, PciBdf, PciClassCode, PciConfiguration, PciDevice, PciDeviceError, PciHeaderType, PciSubclass,
@ -174,22 +174,30 @@ impl VfioUserPciDevice {
continue; continue;
} }
let slot = mem_slot(); let user_memory_regions = vec![UserMemoryRegion {
let mem_region = vm.make_user_memory_region( slot: mem_slot(),
slot, start: mmio_region.start.0,
mmio_region.start.0, size: mmio_region.length as u64,
mmio_region.length as u64, host_addr: host_addr as u64,
host_addr as u64, }];
false,
false,
);
vm.create_user_memory_region(mem_region) for user_memory_region in user_memory_regions.iter() {
.map_err(VfioUserPciDeviceError::MapRegionGuest)?; let mem_region = vm.make_user_memory_region(
user_memory_region.slot,
user_memory_region.start,
user_memory_region.size,
user_memory_region.host_addr,
false,
false,
);
vm.create_user_memory_region(mem_region)
.map_err(VfioUserPciDeviceError::MapRegionGuest)?;
}
mmio_region.mem_slot = Some(slot);
mmio_region.host_addr = Some(host_addr as u64); mmio_region.host_addr = Some(host_addr as u64);
mmio_region.mmap_size = Some(mmio_region.length as usize); mmio_region.mmap_size = Some(mmio_region.length as usize);
mmio_region.user_memory_regions = user_memory_regions;
} }
} }
@ -198,17 +206,13 @@ impl VfioUserPciDevice {
pub fn unmap_mmio_regions(&mut self) { pub fn unmap_mmio_regions(&mut self) {
for mmio_region in self.common.mmio_regions.iter() { for mmio_region in self.common.mmio_regions.iter() {
if let (Some(host_addr), Some(mmap_size), Some(mem_slot)) = ( for user_memory_region in mmio_region.user_memory_regions.iter() {
mmio_region.host_addr,
mmio_region.mmap_size,
mmio_region.mem_slot,
) {
// Remove region // Remove region
let r = self.vm.make_user_memory_region( let r = self.vm.make_user_memory_region(
mem_slot, user_memory_region.slot,
mmio_region.start.raw_value(), user_memory_region.start,
mmap_size as u64, user_memory_region.size,
host_addr as u64, user_memory_region.host_addr,
false, false,
false, false,
); );
@ -216,7 +220,11 @@ impl VfioUserPciDevice {
if let Err(e) = self.vm.remove_user_memory_region(r) { if let Err(e) = self.vm.remove_user_memory_region(r) {
error!("Could not remove the userspace memory region: {}", e); error!("Could not remove the userspace memory region: {}", e);
} }
}
if let (Some(host_addr), Some(mmap_size)) =
(mmio_region.host_addr, mmio_region.mmap_size)
{
let ret = unsafe { libc::munmap(host_addr as *mut libc::c_void, mmap_size) }; let ret = unsafe { libc::munmap(host_addr as *mut libc::c_void, mmap_size) };
if ret != 0 { if ret != 0 {
error!( error!(
@ -450,35 +458,41 @@ impl PciDevice for VfioUserPciDevice {
if mmio_region.start.raw_value() == old_base { if mmio_region.start.raw_value() == old_base {
mmio_region.start = GuestAddress(new_base); mmio_region.start = GuestAddress(new_base);
if let Some(mem_slot) = mmio_region.mem_slot { for user_memory_region in mmio_region.user_memory_regions.iter_mut() {
if let Some(host_addr) = mmio_region.host_addr { // Remove old region
// Remove original region let old_region = self.vm.make_user_memory_region(
let old_region = self.vm.make_user_memory_region( user_memory_region.slot,
mem_slot, user_memory_region.start,
old_base, user_memory_region.size,
mmio_region.length as u64, user_memory_region.host_addr,
host_addr as u64, false,
false, false,
false, );
);
self.vm self.vm
.remove_user_memory_region(old_region) .remove_user_memory_region(old_region)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
let new_region = self.vm.make_user_memory_region( // Update the user memory region with the correct start address.
mem_slot, if new_base > old_base {
new_base, user_memory_region.start += new_base - old_base;
mmio_region.length as u64, } else {
host_addr as u64, user_memory_region.start -= old_base - new_base;
false,
false,
);
self.vm
.create_user_memory_region(new_region)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
} }
// Insert new region
let new_region = self.vm.make_user_memory_region(
user_memory_region.slot,
user_memory_region.start,
user_memory_region.size,
user_memory_region.host_addr,
false,
false,
);
self.vm
.create_user_memory_region(new_region)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
} }
info!("Moved bar 0x{:x} -> 0x{:x}", old_base, new_base); info!("Moved bar 0x{:x} -> 0x{:x}", old_base, new_base);
} }