vmm, pci, openapi: Add VFIO mmap BAR exclusion

Allow VFIO devices to list BAR indices that should not be
mmapped into the guest. This lets operators skip large BARs that
are known not to be used by their workload.

When a BAR is skipped, the log also calls out that P2P DMA
mapping is skipped because the VFIO DMA map path uses the same
mmap backing.

Signed-off-by: Damian Barabonkov <dbctl@pm.me>
Assisted-by: OpenCode:gpt-5.5
This commit is contained in:
Damian Barabonkov
2026-05-06 08:16:19 +02:00
committed by Rob Bradford
parent 12f48700a2
commit 41e12e9d6a
6 changed files with 102 additions and 7 deletions

View File

@@ -501,6 +501,13 @@ pub(crate) struct VfioCommon {
pub(crate) vfio_wrapper: Arc<dyn Vfio>, pub(crate) vfio_wrapper: Arc<dyn Vfio>,
pub(crate) patches: HashMap<usize, ConfigPatch>, pub(crate) patches: HashMap<usize, ConfigPatch>,
x_nv_gpudirect_clique: Option<u8>, x_nv_gpudirect_clique: Option<u8>,
x_exclude_mmap_bars: Vec<u8>,
}
#[derive(Default)]
pub(crate) struct VfioCommonConfig {
pub(crate) x_nv_gpudirect_clique: Option<u8>,
pub(crate) x_exclude_mmap_bars: Vec<u8>,
} }
impl VfioCommon { impl VfioCommon {
@@ -511,7 +518,7 @@ impl VfioCommon {
subclass: &dyn PciSubclass, subclass: &dyn PciSubclass,
bdf: PciBdf, bdf: PciBdf,
snapshot: Option<&Snapshot>, snapshot: Option<&Snapshot>,
x_nv_gpudirect_clique: Option<u8>, config: VfioCommonConfig,
) -> Result<Self, VfioPciError> { ) -> Result<Self, VfioPciError> {
let pci_configuration_state = vm_migration::state_from_id(snapshot, PCI_CONFIGURATION_ID) let pci_configuration_state = vm_migration::state_from_id(snapshot, PCI_CONFIGURATION_ID)
.map_err(|e| { .map_err(|e| {
@@ -546,7 +553,8 @@ impl VfioCommon {
legacy_interrupt_group, legacy_interrupt_group,
vfio_wrapper, vfio_wrapper,
patches: HashMap::new(), patches: HashMap::new(),
x_nv_gpudirect_clique, x_nv_gpudirect_clique: config.x_nv_gpudirect_clique,
x_exclude_mmap_bars: config.x_exclude_mmap_bars,
}; };
let state: Option<VfioCommonState> = snapshot let state: Option<VfioCommonState> = snapshot
@@ -1499,6 +1507,7 @@ impl VfioPciDevice {
memory_slot_allocator: MemorySlotAllocator, memory_slot_allocator: MemorySlotAllocator,
snapshot: Option<&Snapshot>, snapshot: Option<&Snapshot>,
x_nv_gpudirect_clique: Option<u8>, x_nv_gpudirect_clique: Option<u8>,
x_exclude_mmap_bars: Vec<u8>,
device_path: PathBuf, device_path: PathBuf,
) -> Result<Self, VfioPciError> { ) -> Result<Self, VfioPciError> {
let device = Arc::new(device); let device = Arc::new(device);
@@ -1513,7 +1522,10 @@ impl VfioPciDevice {
&PciVfioSubclass::VfioSubclass, &PciVfioSubclass::VfioSubclass,
bdf, bdf,
vm_migration::snapshot_from_id(snapshot, VFIO_COMMON_ID), vm_migration::snapshot_from_id(snapshot, VFIO_COMMON_ID),
x_nv_gpudirect_clique, VfioCommonConfig {
x_nv_gpudirect_clique,
x_exclude_mmap_bars,
},
)?; )?;
let vfio_pci_device = VfioPciDevice { let vfio_pci_device = VfioPciDevice {
@@ -1649,6 +1661,21 @@ impl VfioPciDevice {
// SAFETY: fd is guaranteed valid // SAFETY: fd is guaranteed valid
let fd = unsafe { BorrowedFd::borrow_raw(fd) }; let fd = unsafe { BorrowedFd::borrow_raw(fd) };
for region in self.common.mmio_regions.iter_mut() { for region in self.common.mmio_regions.iter_mut() {
if self
.common
.x_exclude_mmap_bars
.contains(&(region.index as u8))
{
info!(
"Skipping VFIO BAR mmap and P2P DMA mapping for device {} at {} BAR {} (size = 0x{:x})",
self.bdf,
self.device_path.display(),
region.index,
region.length
);
continue;
}
let region_flags = self.device.get_region_flags(region.index); let region_flags = self.device.get_region_flags(region.index);
if region_flags & VFIO_REGION_INFO_FLAG_MMAP != 0 { if region_flags & VFIO_REGION_INFO_FLAG_MMAP != 0 {
let mut prot = 0; let mut prot = 0;

View File

@@ -26,7 +26,9 @@ use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottabl
use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::eventfd::EventFd;
use crate::mmap::MmapRegion; use crate::mmap::MmapRegion;
use crate::vfio::{UserMemoryRegion, VFIO_COMMON_ID, Vfio, VfioCommon, VfioError}; use crate::vfio::{
UserMemoryRegion, VFIO_COMMON_ID, Vfio, VfioCommon, VfioCommonConfig, VfioError,
};
use crate::{ use crate::{
BarReprogrammingParams, PciBarConfiguration, PciBdf, PciDevice, PciDeviceError, PciSubclass, BarReprogrammingParams, PciBarConfiguration, PciBdf, PciDevice, PciDeviceError, PciSubclass,
VfioPciError, VfioPciError,
@@ -101,7 +103,7 @@ impl VfioUserPciDevice {
&PciVfioUserSubclass::VfioUserSubclass, &PciVfioUserSubclass::VfioUserSubclass,
bdf, bdf,
vm_migration::snapshot_from_id(snapshot, VFIO_COMMON_ID), vm_migration::snapshot_from_id(snapshot, VFIO_COMMON_ID),
None, VfioCommonConfig::default(),
) )
.map_err(VfioUserPciDeviceError::CreateVfioCommon)?; .map_err(VfioUserPciDeviceError::CreateVfioCommon)?;

View File

@@ -1223,6 +1223,11 @@ components:
x_nv_gpudirect_clique: x_nv_gpudirect_clique:
type: integer type: integer
format: int8 format: int8
x_exclude_mmap_bars:
type: array
items:
type: integer
format: int64
UserDeviceConfig: UserDeviceConfig:
required: required:

View File

@@ -310,6 +310,9 @@ pub enum ValidationError {
/// Invalid PCI segment aperture weight /// Invalid PCI segment aperture weight
#[error("Invalid PCI segment aperture weight: {0}")] #[error("Invalid PCI segment aperture weight: {0}")]
InvalidPciSegmentApertureWeight(u32), InvalidPciSegmentApertureWeight(u32),
/// Invalid VFIO excluded-mmap BAR index
#[error("Invalid VFIO excluded-mmap BAR index: {0}")]
InvalidDeviceExcludeMmapBar(u64),
/// Invalid IOMMU address width in bits /// Invalid IOMMU address width in bits
#[error( #[error(
"IOMMU address width in bits ({0}) should be less than or equal to {MAX_IOMMU_ADDRESS_WIDTH_BITS}" "IOMMU address width in bits ({0}) should be less than or equal to {MAX_IOMMU_ADDRESS_WIDTH_BITS}"
@@ -2237,14 +2240,17 @@ impl DebugConsoleConfig {
impl DeviceConfig { impl DeviceConfig {
pub const SYNTAX: &'static str = "Direct device assignment parameters \ pub const SYNTAX: &'static str = "Direct device assignment parameters \
\"path=<device_path>,iommu=on|off,id=<device_id>,\ \"path=<device_path>,iommu=on|off,id=<device_id>,\
pci_segment=<segment_id>,pci_device_id=<pci_slot>\""; pci_segment=<segment_id>,pci_device_id=<pci_slot>,\
x_nv_gpudirect_clique=<clique_id>,\
x_exclude_mmap_bars=[<bar>...]\"";
pub fn parse(device: &str) -> Result<Self> { pub fn parse(device: &str) -> Result<Self> {
let mut parser = OptionParser::new(); let mut parser = OptionParser::new();
parser parser
.add("path") .add("path")
.add_all(PciDeviceCommonConfig::OPTIONS_IOMMU) .add_all(PciDeviceCommonConfig::OPTIONS_IOMMU)
.add("x_nv_gpudirect_clique"); .add("x_nv_gpudirect_clique")
.add("x_exclude_mmap_bars");
parser.parse(device).map_err(Error::ParseDevice)?; parser.parse(device).map_err(Error::ParseDevice)?;
let pci_common = PciDeviceCommonConfig::parse(device)?; let pci_common = PciDeviceCommonConfig::parse(device)?;
@@ -2255,10 +2261,16 @@ impl DeviceConfig {
let x_nv_gpudirect_clique = parser let x_nv_gpudirect_clique = parser
.convert::<u8>("x_nv_gpudirect_clique") .convert::<u8>("x_nv_gpudirect_clique")
.map_err(Error::ParseDevice)?; .map_err(Error::ParseDevice)?;
let x_exclude_mmap_bars = parser
.convert::<IntegerList>("x_exclude_mmap_bars")
.map_err(Error::ParseDevice)?
.map(|bars| bars.0)
.unwrap_or_default();
Ok(DeviceConfig { Ok(DeviceConfig {
pci_common, pci_common,
path, path,
x_nv_gpudirect_clique, x_nv_gpudirect_clique,
x_exclude_mmap_bars,
}) })
} }
@@ -2272,6 +2284,13 @@ impl DeviceConfig {
} }
} }
// PCI devices expose six BARs, so only BAR indices 0 through 5 are valid here.
for bar in &self.x_exclude_mmap_bars {
if *bar > 5 {
return Err(ValidationError::InvalidDeviceExcludeMmapBar(*bar));
}
}
Ok(()) Ok(())
} }
} }
@@ -4427,6 +4446,7 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}"
pci_common: PciDeviceCommonConfig::default(), pci_common: PciDeviceCommonConfig::default(),
path: PathBuf::from("/path/to/device"), path: PathBuf::from("/path/to/device"),
x_nv_gpudirect_clique: None, x_nv_gpudirect_clique: None,
x_exclude_mmap_bars: Vec::new(),
} }
} }
@@ -4462,6 +4482,29 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}"
} }
); );
assert_eq!(
DeviceConfig::parse("path=/path/to/device,x_exclude_mmap_bars=[2]")?,
DeviceConfig {
x_exclude_mmap_bars: vec![2],
..device_fixture()
}
);
assert_eq!(
DeviceConfig::parse("path=/path/to/device,x_exclude_mmap_bars=[0,2,5]")?,
DeviceConfig {
x_exclude_mmap_bars: vec![0, 2, 5],
..device_fixture()
}
);
assert_eq!(
DeviceConfig::parse("path=/path/to/device,x_exclude_mmap_bars=[6]")?,
DeviceConfig {
x_exclude_mmap_bars: vec![6],
..device_fixture()
}
);
Ok(()) Ok(())
} }
@@ -5850,6 +5893,17 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}"
}]); }]);
still_valid_config.validate().unwrap(); still_valid_config.validate().unwrap();
// x_exclude_mmap_bars only accepts PCI BAR indices 0 through 5
let mut invalid_config = valid_config.clone();
invalid_config.devices = Some(vec![DeviceConfig {
x_exclude_mmap_bars: vec![6],
..device_fixture()
}]);
assert_eq!(
invalid_config.validate(),
Err(ValidationError::InvalidDeviceExcludeMmapBar(6))
);
let mut still_valid_config = valid_config.clone(); let mut still_valid_config = valid_config.clone();
// SAFETY: Safe as the file was just opened // SAFETY: Safe as the file was just opened
let fd1 = unsafe { libc::dup(File::open("/dev/null").unwrap().as_raw_fd()) }; let fd1 = unsafe { libc::dup(File::open("/dev/null").unwrap().as_raw_fd()) };

View File

@@ -3899,6 +3899,11 @@ impl DeviceManager {
memory_manager.lock().unwrap().memory_slot_allocator(), memory_manager.lock().unwrap().memory_slot_allocator(),
vm_migration::snapshot_from_id(self.snapshot.as_ref(), vfio_name.as_str()), vm_migration::snapshot_from_id(self.snapshot.as_ref(), vfio_name.as_str()),
device_cfg.x_nv_gpudirect_clique, device_cfg.x_nv_gpudirect_clique,
device_cfg
.x_exclude_mmap_bars
.iter()
.map(|bar| *bar as u8)
.collect(),
device_cfg.path.clone(), device_cfg.path.clone(),
) )
.map_err(DeviceManagerError::VfioPciCreate)?; .map_err(DeviceManagerError::VfioPciCreate)?;

View File

@@ -614,6 +614,8 @@ pub struct DeviceConfig {
pub path: PathBuf, pub path: PathBuf,
#[serde(default)] #[serde(default)]
pub x_nv_gpudirect_clique: Option<u8>, pub x_nv_gpudirect_clique: Option<u8>,
#[serde(default)]
pub x_exclude_mmap_bars: Vec<u64>,
} }
impl ApplyLandlock for DeviceConfig { impl ApplyLandlock for DeviceConfig {