From d5179a73c20aa5d78092ed39a6eb1aa6a37ab487 Mon Sep 17 00:00:00 2001 From: Dylan Reid Date: Fri, 3 Apr 2026 16:11:22 -0700 Subject: [PATCH] vmm: use 64-bit BARs for hotplugged virtio block devices Boot-time block devices on PCI segment 0 use 32-bit BARs so early firmware can access them without additional identity mapping in the firmware page tables. However, hot-plugged block devices are only ever seen by the OS kernel which handles 64-bit BARs natively. Switch hot-plugged block devices to 64-bit BARs to avoid exhausting the scarce 32-bit MMIO window (typically 2-3 GB between RAM and 4 GB) when many devices are hot-plugged. Extract the BAR sizing decision into use_64bit_bar_for_virtio_device() and thread an is_hotplug flag through add_virtio_pci_device(). Add unit tests covering all relevant combinations. Signed-off-by: Dylan Reid --- vmm/src/device_manager.rs | 56 ++++++++++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/vmm/src/device_manager.rs b/vmm/src/device_manager.rs index dc3d827e7..0a263e7c4 100644 --- a/vmm/src/device_manager.rs +++ b/vmm/src/device_manager.rs @@ -1200,6 +1200,14 @@ fn create_mmio_allocators( mmio_allocators } +fn use_64bit_bar_for_virtio_device( + device_type: u32, + pci_segment_id: u16, + is_hotplug: bool, +) -> bool { + pci_segment_id > 0 || device_type != VirtioDeviceType::Block as u32 || is_hotplug +} + impl DeviceManager { #[allow(clippy::too_many_arguments)] pub fn new( @@ -1714,6 +1722,7 @@ impl DeviceManager { &mapping, &id, handle.pci_common.pci_segment, + false, handle.dma_handler, handle.pci_common.pci_device_id, )?; @@ -1747,8 +1756,15 @@ impl DeviceManager { } if let Some(iommu_device) = iommu_device { - let dev_id = - self.add_virtio_pci_device(iommu_device, &None, &iommu_id, 0, None, None)?; + let dev_id = self.add_virtio_pci_device( + iommu_device, + &None, + &iommu_id, + 0, + false, + None, + None, + )?; self.iommu_attached_devices = Some((dev_id, iommu_attached_devices)); } } @@ -4307,12 +4323,14 @@ impl DeviceManager { Ok(vec![]) } + #[allow(clippy::too_many_arguments)] fn add_virtio_pci_device( &mut self, virtio_device: Arc>, iommu_mapping: &Option>, virtio_device_id: &str, pci_segment_id: u16, + is_hotplug: bool, dma_handler: Option>, pci_device_id: Option, ) -> DeviceManagerResult { @@ -4412,11 +4430,10 @@ impl DeviceManager { self.activate_evt .try_clone() .map_err(DeviceManagerError::EventFd)?, - // All device types *except* virtio block devices should be allocated a 64-bit bar - // The block devices should be given a 32-bit BAR so that they are easily accessible - // to firmware without requiring excessive identity mapping. - // The exception being if not on the default PCI segment. - pci_segment_id > 0 || device_type != VirtioDeviceType::Block as u32, + // Boot-time block devices stay in 32-bit BAR space so early firmware can access + // them without additional identity mapping. Hot-plugged block devices do not have + // that constraint and should use 64-bit BARs like the rest of the virtio devices. + use_64bit_bar_for_virtio_device(device_type, pci_segment_id, is_hotplug), dma_handler, self.pending_activations.clone(), vm_migration::snapshot_from_id(self.snapshot.as_ref(), id.as_str()), @@ -5120,6 +5137,7 @@ impl DeviceManager { &mapping, &id, handle.pci_common.pci_segment, + true, handle.dma_handler, handle.pci_common.pci_device_id, )?; @@ -5868,6 +5886,30 @@ impl Drop for DeviceManager { mod unit_tests { use super::*; + #[test] + fn test_hotplugged_block_devices_use_64bit_bars() { + assert!(!use_64bit_bar_for_virtio_device( + VirtioDeviceType::Block as u32, + 0, + false, + )); + assert!(use_64bit_bar_for_virtio_device( + VirtioDeviceType::Block as u32, + 0, + true, + )); + assert!(use_64bit_bar_for_virtio_device( + VirtioDeviceType::Net as u32, + 0, + false, + )); + assert!(use_64bit_bar_for_virtio_device( + VirtioDeviceType::Block as u32, + 1, + false, + )); + } + #[test] fn test_create_mmio_allocators() { let res = create_mmio_allocators(0x100000, 0x3fffff, 1, &[1], 4 << 10);