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 <dgreid@fb.com>
This commit is contained in:
Dylan Reid
2026-04-03 16:11:22 -07:00
committed by Rob Bradford
parent d48d1dcd3d
commit d5179a73c2

View File

@@ -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<Mutex<dyn virtio_devices::VirtioDevice>>,
iommu_mapping: &Option<Arc<IommuMapping>>,
virtio_device_id: &str,
pci_segment_id: u16,
is_hotplug: bool,
dma_handler: Option<Arc<dyn ExternalDmaMapping>>,
pci_device_id: Option<u8>,
) -> DeviceManagerResult<PciBdf> {
@@ -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);