From 212986f0138282e9339c3662eccbf9b5af8b84b5 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Tue, 19 May 2026 14:59:35 +0200 Subject: [PATCH] vmm, docs: make PCI BDF configurable for ivshmem Add shared PCI config to ivshmem. On-behalf-of: SAP philipp.schuster@sap.com Signed-off-by: Philipp Schuster --- docs/ivshmem.md | 2 +- vmm/src/config.rs | 35 ++++++++++++++++++++++++++++++++--- vmm/src/device_manager.rs | 34 +++++++++++++++++++++++++++------- vmm/src/vm_config.rs | 3 +++ 4 files changed, 63 insertions(+), 11 deletions(-) diff --git a/docs/ivshmem.md b/docs/ivshmem.md index 0bc82cbb9..6930eaddd 100644 --- a/docs/ivshmem.md +++ b/docs/ivshmem.md @@ -19,7 +19,7 @@ This argument takes a file as a `path` value and a file size as a `size` value. The `size` value must be 2^n. ``` ---ivshmem device backend file "path=,size=" +--ivshmem device backend file "path=,size=,id=,pci_segment=,pci_device_id=" ``` ## Example diff --git a/vmm/src/config.rs b/vmm/src/config.rs index b340ac107..e50d7bc66 100644 --- a/vmm/src/config.rs +++ b/vmm/src/config.rs @@ -2911,12 +2911,14 @@ impl LandlockConfig { #[cfg(feature = "ivshmem")] impl IvshmemConfig { pub const SYNTAX: &'static str = "Ivshmem device. Specify the backend file path and size \ - for the shared memory: \"path=, size=\" \ + for the shared memory: \"path=,size=,id=,\ + pci_segment=,pci_device_id=\" \ \nThe must be a power of 2 (e.g., 2M, 4M, etc.), as it represents the size \ of the memory region mapped to the guest. Default size is 128M."; pub fn parse(ivshmem: &str) -> Result { let mut parser = OptionParser::new(); parser.add("path").add("size"); + parser.add_all(PciDeviceCommonConfig::OPTIONS); parser.parse(ivshmem).map_err(Error::ParseIvshmem)?; let path = parser .get("path") @@ -2927,13 +2929,20 @@ impl IvshmemConfig { .map_err(Error::ParseIvshmem)? .unwrap_or(ByteSized((DEFAULT_IVSHMEM_SIZE << 20) as u64)) .0; + let pci_common = PciDeviceCommonConfig::parse(ivshmem)?; Ok(IvshmemConfig { + pci_common, path, size: size as usize, }) } - pub fn validate(&self) -> ValidationResult<()> { + pub fn validate(&self, vm_config: &VmConfig) -> ValidationResult<()> { + if self.pci_common.iommu { + return Err(ValidationError::IommuNotSupported); + } + self.pci_common.validate(vm_config)?; + let size = self.size as u64; let path = &self.path; // size must = 2^n @@ -3376,7 +3385,8 @@ impl VmConfig { } #[cfg(feature = "ivshmem")] if let Some(ivshmem_config) = &self.ivshmem { - ivshmem_config.validate()?; + ivshmem_config.validate(self)?; + Self::validate_identifier(&mut id_list, &ivshmem_config.pci_common.id)?; } Ok(id_list) @@ -4420,6 +4430,25 @@ mod unit_tests { Ok(()) } + #[test] + #[cfg(feature = "ivshmem")] + fn test_parse_ivshmem() -> Result<()> { + assert_eq!( + IvshmemConfig::parse("path=/tmp/ivshmem.data,size=2M,pci_segment=1,pci_device_id=7")?, + IvshmemConfig { + pci_common: PciDeviceCommonConfig { + pci_segment: 1, + pci_device_id: Some(7), + ..Default::default() + }, + path: PathBuf::from("/tmp/ivshmem.data"), + size: 2 << 20, + } + ); + + Ok(()) + } + fn fs_fixture() -> FsConfig { FsConfig { pci_common: PciDeviceCommonConfig::default(), diff --git a/vmm/src/device_manager.rs b/vmm/src/device_manager.rs index 6fadd5ea5..7c52b7ec3 100644 --- a/vmm/src/device_manager.rs +++ b/vmm/src/device_manager.rs @@ -1560,8 +1560,11 @@ impl DeviceManager { } #[cfg(feature = "ivshmem")] - if let Some(ivshmem) = self.config.clone().lock().unwrap().ivshmem.as_ref() { - self.ivshmem_device = self.add_ivshmem_device(ivshmem, snapshot)?; + { + let mut ivshmem = self.config.lock().unwrap().ivshmem.clone(); + if let Some(ivshmem) = &mut ivshmem { + self.ivshmem_device = self.add_ivshmem_device(ivshmem, snapshot)?; + } } Ok(()) @@ -4481,15 +4484,24 @@ impl DeviceManager { #[cfg(feature = "ivshmem")] fn add_ivshmem_device( &mut self, - ivshmem_cfg: &IvshmemConfig, + ivshmem_cfg: &mut IvshmemConfig, snapshot: Option<&Snapshot>, ) -> DeviceManagerResult>>> { - let id = String::from(IVSHMEM_DEVICE_NAME); - let pci_segment_id = 0x0_u16; + let id = match ivshmem_cfg.pci_common.id.as_ref() { + Some(id) => id.clone(), + None => ivshmem_cfg + .pci_common + .id + .insert(IVSHMEM_DEVICE_NAME.to_string()) + .clone(), + }; info!("Creating ivshmem device {id}"); - let (pci_segment_id, pci_device_bdf, resources) = - self.pci_resources(&id, pci_segment_id, None)?; + let (pci_segment_id, pci_device_bdf, resources) = self.pci_resources( + &id, + ivshmem_cfg.pci_common.pci_segment, + ivshmem_cfg.pci_common.pci_device_id, + )?; let snapshot = snapshot_from_id(snapshot, id.as_str()); let ivshmem_ops = Arc::new(Mutex::new(IvshmemHandler { @@ -4558,6 +4570,14 @@ impl DeviceManager { } } + #[cfg(feature = "ivshmem")] + if let Some(ivshmem_cfg) = &config.ivshmem + && let Some(device_id) = ivshmem_cfg.pci_common.pci_device_id + { + self.pci_segments[ivshmem_cfg.pci_common.pci_segment as usize] + .reserve_device_id(device_id)?; + } + Ok(()) } diff --git a/vmm/src/vm_config.rs b/vmm/src/vm_config.rs index 98704c928..5adf293ff 100644 --- a/vmm/src/vm_config.rs +++ b/vmm/src/vm_config.rs @@ -837,6 +837,8 @@ pub const DEFAULT_IVSHMEM_SIZE: usize = 128; #[cfg(feature = "ivshmem")] #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct IvshmemConfig { + #[serde(flatten)] + pub pci_common: PciDeviceCommonConfig, pub path: PathBuf, pub size: usize, } @@ -845,6 +847,7 @@ pub struct IvshmemConfig { impl Default for IvshmemConfig { fn default() -> Self { Self { + pci_common: PciDeviceCommonConfig::default(), path: PathBuf::new(), size: DEFAULT_IVSHMEM_SIZE << 20, }