mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
pci, virtio-devices, vmm: Allocate PCI 64-bit bars per segment
Since each segment must have a non-overlapping memory range associated with it the device memory must be equally divided amongst all segments. A new allocator is used for each segment to ensure that BARs are allocated from the correct address ranges. This requires changes to PciDevice::allocate/free_bars to take that allocator and when reallocating BARs the correct allocator must be identified from the ranges. Signed-off-by: Rob Bradford <robert.bradford@intel.com>
This commit is contained in:
@@ -93,7 +93,7 @@ use virtio_devices::{AccessPlatformMapping, VirtioMemMappingSource};
|
||||
use virtio_devices::{Endpoint, IommuMapping};
|
||||
use virtio_devices::{VirtioSharedMemory, VirtioSharedMemoryList};
|
||||
use virtio_queue::AccessPlatform;
|
||||
use vm_allocator::SystemAllocator;
|
||||
use vm_allocator::{AddressAllocator, SystemAllocator};
|
||||
use vm_device::dma_mapping::vfio::VfioDmaMapping;
|
||||
use vm_device::interrupt::{
|
||||
InterruptIndex, InterruptManager, LegacyIrqGroupConfig, MsiIrqGroupConfig,
|
||||
@@ -540,6 +540,7 @@ pub(crate) struct AddressManager {
|
||||
pub(crate) mmio_bus: Arc<Bus>,
|
||||
vm: Arc<dyn hypervisor::Vm>,
|
||||
device_tree: Arc<Mutex<DeviceTree>>,
|
||||
pci_mmio_allocators: Vec<Arc<Mutex<AddressAllocator>>>,
|
||||
}
|
||||
|
||||
impl DeviceRelocation for AddressManager {
|
||||
@@ -604,25 +605,35 @@ impl DeviceRelocation for AddressManager {
|
||||
)
|
||||
})?;
|
||||
} else {
|
||||
self.allocator
|
||||
.lock()
|
||||
.unwrap()
|
||||
.free_mmio_addresses(GuestAddress(old_base), len as GuestUsize);
|
||||
// Find the specific allocator that this BAR was allocated from and use it for new one
|
||||
for allocator in &self.pci_mmio_allocators {
|
||||
let allocator_base = allocator.lock().unwrap().base();
|
||||
let allocator_end = allocator.lock().unwrap().end();
|
||||
|
||||
self.allocator
|
||||
.lock()
|
||||
.unwrap()
|
||||
.allocate_mmio_addresses(
|
||||
Some(GuestAddress(new_base)),
|
||||
len as GuestUsize,
|
||||
Some(len),
|
||||
)
|
||||
.ok_or_else(|| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"failed allocating new 64 bits MMIO range",
|
||||
)
|
||||
})?;
|
||||
if old_base >= allocator_base.0 && old_base <= allocator_end.0 {
|
||||
allocator
|
||||
.lock()
|
||||
.unwrap()
|
||||
.free(GuestAddress(old_base), len as GuestUsize);
|
||||
|
||||
allocator
|
||||
.lock()
|
||||
.unwrap()
|
||||
.allocate(
|
||||
Some(GuestAddress(new_base)),
|
||||
len as GuestUsize,
|
||||
Some(len),
|
||||
)
|
||||
.ok_or_else(|| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"failed allocating new 64 bits MMIO range",
|
||||
)
|
||||
})?;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update MMIO bus
|
||||
@@ -916,6 +927,30 @@ impl DeviceManager {
|
||||
) -> DeviceManagerResult<Arc<Mutex<Self>>> {
|
||||
let device_tree = Arc::new(Mutex::new(DeviceTree::new()));
|
||||
|
||||
let num_pci_segments =
|
||||
if let Some(platform_config) = config.lock().unwrap().platform.as_ref() {
|
||||
platform_config.num_pci_segments
|
||||
} else {
|
||||
1
|
||||
};
|
||||
|
||||
let start_of_device_area = memory_manager.lock().unwrap().start_of_device_area().0;
|
||||
let end_of_device_area = memory_manager.lock().unwrap().end_of_device_area().0;
|
||||
|
||||
// Start each PCI segment range on a 4GiB boundary
|
||||
let pci_segment_size = (end_of_device_area - start_of_device_area + 1)
|
||||
/ ((4 << 30) * num_pci_segments as u64)
|
||||
* (4 << 30);
|
||||
|
||||
let mut pci_mmio_allocators = vec![];
|
||||
for i in 0..num_pci_segments as u64 {
|
||||
let mmio_start = start_of_device_area + i * pci_segment_size;
|
||||
let allocator = Arc::new(Mutex::new(
|
||||
AddressAllocator::new(GuestAddress(mmio_start), pci_segment_size).unwrap(),
|
||||
));
|
||||
pci_mmio_allocators.push(allocator)
|
||||
}
|
||||
|
||||
let address_manager = Arc::new(AddressManager {
|
||||
allocator: memory_manager.lock().unwrap().allocator(),
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
@@ -923,6 +958,7 @@ impl DeviceManager {
|
||||
mmio_bus: Arc::new(Bus::new()),
|
||||
vm: vm.clone(),
|
||||
device_tree: Arc::clone(&device_tree),
|
||||
pci_mmio_allocators,
|
||||
});
|
||||
|
||||
// First we create the MSI interrupt manager, the legacy one is created
|
||||
@@ -945,9 +981,6 @@ impl DeviceManager {
|
||||
.allocate_mmio_addresses(None, DEVICE_MANAGER_ACPI_SIZE as u64, None)
|
||||
.ok_or(DeviceManagerError::AllocateIoPort)?;
|
||||
|
||||
let start_of_device_area = memory_manager.lock().unwrap().start_of_device_area().0;
|
||||
let end_of_device_area = memory_manager.lock().unwrap().end_of_device_area().0;
|
||||
|
||||
let mut pci_irq_slots = [0; 32];
|
||||
PciSegment::reserve_legacy_interrupts_for_pci_devices(
|
||||
&address_manager,
|
||||
@@ -956,21 +989,17 @@ impl DeviceManager {
|
||||
|
||||
let mut pci_segments = vec![PciSegment::new_default_segment(
|
||||
&address_manager,
|
||||
start_of_device_area,
|
||||
end_of_device_area,
|
||||
Arc::clone(&address_manager.pci_mmio_allocators[0]),
|
||||
&pci_irq_slots,
|
||||
)?];
|
||||
|
||||
if let Some(platform_config) = config.lock().unwrap().platform.as_ref() {
|
||||
for i in 1..platform_config.num_pci_segments {
|
||||
pci_segments.push(PciSegment::new(
|
||||
i as u16,
|
||||
&address_manager,
|
||||
start_of_device_area,
|
||||
end_of_device_area,
|
||||
&pci_irq_slots,
|
||||
)?);
|
||||
}
|
||||
for i in 1..num_pci_segments as usize {
|
||||
pci_segments.push(PciSegment::new(
|
||||
i as u16,
|
||||
&address_manager,
|
||||
Arc::clone(&address_manager.pci_mmio_allocators[i]),
|
||||
&pci_irq_slots,
|
||||
)?);
|
||||
}
|
||||
|
||||
let device_manager = DeviceManager {
|
||||
@@ -3013,7 +3042,13 @@ impl DeviceManager {
|
||||
let bars = pci_device
|
||||
.lock()
|
||||
.unwrap()
|
||||
.allocate_bars(&mut self.address_manager.allocator.lock().unwrap())
|
||||
.allocate_bars(
|
||||
&mut self.address_manager.allocator.lock().unwrap(),
|
||||
&mut self.pci_segments[segment_id as usize]
|
||||
.allocator
|
||||
.lock()
|
||||
.unwrap(),
|
||||
)
|
||||
.map_err(DeviceManagerError::AllocateBars)?;
|
||||
|
||||
let mut pci_bus = self.pci_segments[segment_id as usize]
|
||||
@@ -3597,7 +3632,13 @@ impl DeviceManager {
|
||||
pci_device
|
||||
.lock()
|
||||
.unwrap()
|
||||
.free_bars(&mut self.address_manager.allocator.lock().unwrap())
|
||||
.free_bars(
|
||||
&mut self.address_manager.allocator.lock().unwrap(),
|
||||
&mut self.pci_segments[pci_segment_id as usize]
|
||||
.allocator
|
||||
.lock()
|
||||
.unwrap(),
|
||||
)
|
||||
.map_err(DeviceManagerError::FreePciBars)?;
|
||||
|
||||
// Remove the device from the PCI bus
|
||||
|
||||
@@ -19,6 +19,7 @@ use pci::{PciConfigIo, PCI_CONFIG_IO_PORT, PCI_CONFIG_IO_PORT_SIZE};
|
||||
use std::sync::{Arc, Mutex};
|
||||
#[cfg(feature = "acpi")]
|
||||
use uuid::Uuid;
|
||||
use vm_allocator::AddressAllocator;
|
||||
use vm_device::BusDevice;
|
||||
|
||||
// One bus with potentially 256 devices (32 slots x 8 functions).
|
||||
@@ -43,14 +44,15 @@ pub(crate) struct PciSegment {
|
||||
// Device memory covered by this segment
|
||||
pub(crate) start_of_device_area: u64,
|
||||
pub(crate) end_of_device_area: u64,
|
||||
|
||||
pub(crate) allocator: Arc<Mutex<AddressAllocator>>,
|
||||
}
|
||||
|
||||
impl PciSegment {
|
||||
pub(crate) fn new(
|
||||
id: u16,
|
||||
address_manager: &Arc<AddressManager>,
|
||||
start_of_device_area: u64,
|
||||
end_of_device_area: u64,
|
||||
allocator: Arc<Mutex<AddressAllocator>>,
|
||||
pci_irq_slots: &[u8; 32],
|
||||
) -> DeviceManagerResult<PciSegment> {
|
||||
let pci_root = PciRoot::new(None);
|
||||
@@ -71,6 +73,9 @@ impl PciSegment {
|
||||
)
|
||||
.map_err(DeviceManagerError::BusError)?;
|
||||
|
||||
let start_of_device_area = allocator.lock().unwrap().base().0;
|
||||
let end_of_device_area = allocator.lock().unwrap().end().0;
|
||||
|
||||
let segment = PciSegment {
|
||||
id,
|
||||
pci_bus,
|
||||
@@ -80,6 +85,7 @@ impl PciSegment {
|
||||
pci_devices_down: 0,
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pci_config_io: None,
|
||||
allocator,
|
||||
start_of_device_area,
|
||||
end_of_device_area,
|
||||
pci_irq_slots: *pci_irq_slots,
|
||||
@@ -95,17 +101,10 @@ impl PciSegment {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub(crate) fn new_default_segment(
|
||||
address_manager: &Arc<AddressManager>,
|
||||
start_of_device_area: u64,
|
||||
end_of_device_area: u64,
|
||||
allocator: Arc<Mutex<AddressAllocator>>,
|
||||
pci_irq_slots: &[u8; 32],
|
||||
) -> DeviceManagerResult<PciSegment> {
|
||||
let mut segment = Self::new(
|
||||
0,
|
||||
address_manager,
|
||||
start_of_device_area,
|
||||
end_of_device_area,
|
||||
pci_irq_slots,
|
||||
)?;
|
||||
let mut segment = Self::new(0, address_manager, allocator, pci_irq_slots)?;
|
||||
let pci_config_io = Arc::new(Mutex::new(PciConfigIo::new(Arc::clone(&segment.pci_bus))));
|
||||
|
||||
address_manager
|
||||
@@ -125,17 +124,10 @@ impl PciSegment {
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
pub(crate) fn new_default_segment(
|
||||
address_manager: &Arc<AddressManager>,
|
||||
start_of_device_area: u64,
|
||||
end_of_device_area: u64,
|
||||
allocator: Arc<Mutex<AddressAllocator>>,
|
||||
pci_irq_slots: &[u8; 32],
|
||||
) -> DeviceManagerResult<PciSegment> {
|
||||
Self::new(
|
||||
0,
|
||||
address_manager,
|
||||
start_of_device_area,
|
||||
end_of_device_area,
|
||||
pci_irq_slots,
|
||||
)
|
||||
Self::new(0, address_manager, allocator, pci_irq_slots)
|
||||
}
|
||||
|
||||
pub(crate) fn next_device_bdf(&self) -> DeviceManagerResult<u32> {
|
||||
|
||||
Reference in New Issue
Block a user