vmm: Add fast path for PCI config IO port

Looking up devices on the port I/O bus is time consuming during the
boot at there is an O(lg n) tree lookup and the overhead from taking a
lock on the bus contents.

Avoid this by adding a fast path uses the hardcoded port address and
size and directs PCI config requests directly to the device.

Command line:
target/release/cloud-hypervisor --kernel ~/src/linux/vmlinux --cmdline "root=/dev/vda1 console=ttyS0" --serial tty --console off --disk path=~/workloads/focal-server-cloudimg-amd64-custom-20210609-0.raw --api-socket /tmp/api

PIO exit: 17913
PCI fast path: 17871
Percentage on fast path: 99.8%

perf before:

marvin:~/src/cloud-hypervisor (main *)$ perf report -g | grep resolve
     6.20%     6.20%  vcpu0            cloud-hypervisor    [.] vm_device::bus::Bus::resolve

perf after:

marvin:~/src/cloud-hypervisor (2021-09-17-ioapic-fast-path *)$ perf report -g | grep resolve
     0.08%     0.08%  vcpu0            cloud-hypervisor    [.] vm_device::bus::Bus::resolve

The compromise required to implement this fast path is bringing the
creation of the PciConfigIo device into the DeviceManager::new() so that
it can be used in the VmmOps struct which is created before
DeviceManager::create_devices() is called.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
This commit is contained in:
Rob Bradford
2021-09-17 09:59:30 +01:00
parent da8eecc797
commit 0faa7afac2
4 changed files with 75 additions and 9 deletions
+27 -5
View File
@@ -67,11 +67,12 @@ use libc::{
isatty, tcgetattr, tcsetattr, termios, ECHO, ICANON, ISIG, MAP_NORESERVE, MAP_PRIVATE,
MAP_SHARED, O_TMPFILE, PROT_READ, PROT_WRITE, TCSANOW,
};
use pci::VfioPciDevice;
use pci::{
DeviceRelocation, PciBarRegionType, PciBus, PciConfigIo, PciConfigMmio, PciDevice, PciRoot,
DeviceRelocation, PciBarRegionType, PciBus, PciConfigMmio, PciDevice, PciRoot, VfioPciDevice,
VfioUserPciDevice, VfioUserPciDeviceError,
};
#[cfg(target_arch = "x86_64")]
use pci::{PciConfigIo, PCI_CONFIG_IO_PORT, PCI_CONFIG_IO_PORT_SIZE};
use seccompiler::SeccompAction;
use std::collections::HashMap;
use std::convert::TryInto;
@@ -826,6 +827,9 @@ pub struct DeviceManager {
// Keep a reference to the PCI bus
pci_bus: Option<Arc<Mutex<PciBus>>>,
#[cfg(target_arch = "x86_64")]
pci_config_io: Arc<Mutex<PciConfigIo>>,
#[cfg_attr(target_arch = "aarch64", allow(dead_code))]
// MSI Interrupt Manager
msi_interrupt_manager: Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
@@ -959,6 +963,9 @@ impl DeviceManager {
bus_devices: Vec::new(),
device_id_cnt: Wrapping(0),
pci_bus: None,
#[cfg(target_arch = "x86_64")]
// Create early so ready for use in `VmmOps`
pci_config_io: Arc::new(Mutex::new(PciConfigIo::new())),
msi_interrupt_manager,
legacy_interrupt_manager: None,
passthrough_device: None,
@@ -1222,13 +1229,22 @@ impl DeviceManager {
}
let pci_bus = Arc::new(Mutex::new(pci_bus));
let pci_config_io = Arc::new(Mutex::new(PciConfigIo::new(Arc::clone(&pci_bus))));
#[cfg(target_arch = "x86_64")]
self.pci_config_io
.lock()
.unwrap()
.set_bus(Arc::clone(&pci_bus));
#[cfg(target_arch = "x86_64")]
self.bus_devices
.push(Arc::clone(&pci_config_io) as Arc<Mutex<dyn BusDevice>>);
.push(Arc::clone(&self.pci_config_io) as Arc<Mutex<dyn BusDevice>>);
#[cfg(target_arch = "x86_64")]
self.address_manager
.io_bus
.insert(pci_config_io, 0xcf8, 0x8)
.insert(
self.pci_config_io.clone(),
PCI_CONFIG_IO_PORT,
PCI_CONFIG_IO_PORT_SIZE,
)
.map_err(DeviceManagerError::BusError)?;
let pci_config_mmio = Arc::new(Mutex::new(PciConfigMmio::new(Arc::clone(&pci_bus))));
self.bus_devices
@@ -3314,6 +3330,12 @@ impl DeviceManager {
.map(|ic| ic.clone() as Arc<Mutex<dyn InterruptController>>)
}
#[cfg(target_arch = "x86_64")]
// Used to provide a fast path for handling PIO exits
pub fn pci_config_io(&self) -> Arc<Mutex<PciConfigIo>> {
self.pci_config_io.clone()
}
pub fn console(&self) -> &Arc<Console> {
&self.console
}