diff --git a/cloud-hypervisor/tests/integration.rs b/cloud-hypervisor/tests/integration.rs index 94d886eb1..988a49969 100644 --- a/cloud-hypervisor/tests/integration.rs +++ b/cloud-hypervisor/tests/integration.rs @@ -6092,6 +6092,7 @@ mod common_parallel { .args(["--kernel", kernel_path.to_str().unwrap()]) .args(["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE]) .args(["--console", "tty,pci_device_id=7"]) + .args(["--balloon", "size=0,pci_device_id=8"]) .default_net() .default_disks() .capture_output(); @@ -6113,6 +6114,17 @@ mod common_parallel { .is_ok() })); + // Make sure an explicit BDF for virtio-balloon is set. + assert!(wait_until(Duration::from_secs(10), || { + ssh_command_ip_with_auth( + "lspci -n | grep \"00:08.0\"", + &default_guest_auth(), + &guest.network.guest_ip0, + Some(Duration::from_secs(1)), + ) + .is_ok() + })); + let (cmd_success, cmd_stdout, _) = remote_command_w_output( &api_socket, "add-net", diff --git a/vmm/src/api/openapi/cloud-hypervisor.yaml b/vmm/src/api/openapi/cloud-hypervisor.yaml index 54a0e743d..fc7b38e1a 100644 --- a/vmm/src/api/openapi/cloud-hypervisor.yaml +++ b/vmm/src/api/openapi/cloud-hypervisor.yaml @@ -1127,6 +1127,17 @@ components: - size type: object properties: + id: + type: string + pci_segment: + type: integer + format: int16 + pci_device_id: + type: integer + format: uint8 + iommu: + type: boolean + default: false size: type: integer format: int64 diff --git a/vmm/src/config.rs b/vmm/src/config.rs index 232ba158e..b340ac107 100644 --- a/vmm/src/config.rs +++ b/vmm/src/config.rs @@ -1896,13 +1896,15 @@ impl RtcConfig { impl BalloonConfig { pub const SYNTAX: &'static str = "Balloon parameters \"size=,deflate_on_oom=on|off,\ - free_page_reporting=on|off\""; + free_page_reporting=on|off,iommu=on|off,id=,pci_segment=,\ + pci_device_id=\""; pub fn parse(balloon: &str) -> Result { let mut parser = OptionParser::new(); parser.add("size"); parser.add("deflate_on_oom"); parser.add("free_page_reporting"); + parser.add_all(PciDeviceCommonConfig::OPTIONS_IOMMU); parser.parse(balloon).map_err(Error::ParseBalloon)?; let size = parser @@ -1922,12 +1924,19 @@ impl BalloonConfig { .unwrap_or(Toggle(false)) .0; + let pci_common = PciDeviceCommonConfig::parse(balloon)?; + Ok(BalloonConfig { + pci_common, size, deflate_on_oom, free_page_reporting, }) } + + pub fn validate(&self, vm_config: &VmConfig) -> ValidationResult<()> { + self.pci_common.validate(vm_config) + } } impl GenericVhostUserConfig { @@ -3258,6 +3267,10 @@ impl VmConfig { } if let Some(balloon) = &self.balloon { + balloon.validate(self)?; + Self::validate_identifier(&mut id_list, &balloon.pci_common.id)?; + self.iommu |= balloon.pci_common.iommu; + let ram_size = self.memory.total_size(); if balloon.size >= ram_size { return Err(ValidationError::BalloonLargerThanRam( @@ -4373,6 +4386,40 @@ mod unit_tests { Ok(()) } + #[test] + fn test_parse_balloon() -> Result<()> { + assert_eq!( + BalloonConfig::parse( + "size=128M,deflate_on_oom=on,free_page_reporting=on,pci_segment=1,pci_device_id=7" + )?, + BalloonConfig { + pci_common: PciDeviceCommonConfig { + pci_segment: 1, + pci_device_id: Some(7), + ..Default::default() + }, + size: 128 << 20, + deflate_on_oom: true, + free_page_reporting: true, + } + ); + + assert_eq!( + BalloonConfig::parse("size=0,iommu=on")?, + BalloonConfig { + pci_common: PciDeviceCommonConfig { + iommu: true, + ..Default::default() + }, + size: 0, + deflate_on_oom: false, + free_page_reporting: false, + } + ); + + Ok(()) + } + fn fs_fixture() -> FsConfig { FsConfig { pci_common: PciDeviceCommonConfig::default(), @@ -6124,6 +6171,47 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}" Err(ValidationError::InvalidPciDeviceId(pci::NUM_DEVICE_IDS + 1)) ); + // Invalid balloon BDF - Same ID as Root device + let mut invalid_config = valid_config.clone(); + invalid_config.balloon = Some(BalloonConfig { + pci_common: PciDeviceCommonConfig { + pci_device_id: Some(pci::PCI_ROOT_DEVICE_ID), + ..Default::default() + }, + size: 0, + deflate_on_oom: false, + free_page_reporting: false, + }); + assert_eq!( + invalid_config.validate(), + Err(ValidationError::ReservedPciDeviceId( + pci::PCI_ROOT_DEVICE_ID + )) + ); + + // Invalid balloon ID - Duplicate identifier + let mut invalid_config = valid_config.clone(); + invalid_config.balloon = Some(BalloonConfig { + pci_common: PciDeviceCommonConfig { + id: Some("test0".to_string()), + ..Default::default() + }, + size: 0, + deflate_on_oom: false, + free_page_reporting: false, + }); + invalid_config.disks = Some(vec![DiskConfig { + pci_common: PciDeviceCommonConfig { + id: Some("test0".to_string()), + ..Default::default() + }, + ..disk_fixture() + }]); + assert_eq!( + invalid_config.validate(), + Err(ValidationError::IdentifierNotUnique("test0".to_string())) + ); + // Invalid console BDF - Same ID as Root device let mut invalid_config = valid_config.clone(); invalid_config.console.pci_common.pci_device_id = Some(pci::PCI_ROOT_DEVICE_ID); diff --git a/vmm/src/device_manager.rs b/vmm/src/device_manager.rs index b1b8352d0..6fadd5ea5 100644 --- a/vmm/src/device_manager.rs +++ b/vmm/src/device_manager.rs @@ -3664,8 +3664,16 @@ impl DeviceManager { &mut self, snapshot: Option<&Snapshot>, ) -> DeviceManagerResult<()> { - if let Some(balloon_config) = &self.config.lock().unwrap().balloon { - let id = String::from(BALLOON_DEVICE_NAME); + let mut balloon_config = self.config.lock().unwrap().balloon.clone(); + if let Some(balloon_config) = &mut balloon_config { + let id = match balloon_config.pci_common.id.as_ref() { + Some(id) => id.clone(), + None => balloon_config + .pci_common + .id + .insert(BALLOON_DEVICE_NAME.to_string()) + .clone(), + }; info!("Creating virtio-balloon device: id = {id}"); let virtio_balloon_device = Arc::new(Mutex::new( @@ -3674,7 +3682,7 @@ impl DeviceManager { balloon_config.size, balloon_config.deflate_on_oom, balloon_config.free_page_reporting, - self.force_access_platform, + self.force_access_platform | balloon_config.pci_common.iommu, self.seccomp_action.clone(), self.exit_evt .try_clone() @@ -3690,10 +3698,7 @@ impl DeviceManager { self.virtio_devices.push(MetaVirtioDevice { virtio_device: Arc::clone(&virtio_balloon_device) as Arc>, - pci_common: PciDeviceCommonConfig { - id: Some(id.clone()), - ..Default::default() - }, + pci_common: balloon_config.pci_common.clone(), dma_handler: None, }); diff --git a/vmm/src/vm_config.rs b/vmm/src/vm_config.rs index 02efad065..98704c928 100644 --- a/vmm/src/vm_config.rs +++ b/vmm/src/vm_config.rs @@ -535,6 +535,8 @@ impl ApplyLandlock for RngConfig { #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct BalloonConfig { + #[serde(flatten)] + pub pci_common: PciDeviceCommonConfig, pub size: u64, /// Option to deflate the balloon in case the guest is out of memory. #[serde(default)]