Skip to content

Commit 83e7966

Browse files
committed
vm: implement DeviceRelocation for Vm
Implement PCI device relocation logic for Vm type. Up until now, we were only implementing it in a dummy way. Make sure we correctly deallocate previously allocate ranges and unregister them from the corresponding bus. Try to allocate new addresses and register them with the bus. Signed-off-by: Babis Chalios <[email protected]>
1 parent 486bc1d commit 83e7966

File tree

1 file changed

+261
-7
lines changed

1 file changed

+261
-7
lines changed

src/vmm/src/vstate/vm.rs

Lines changed: 261 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,17 @@ use kvm_bindings::{
2020
};
2121
use kvm_ioctls::VmFd;
2222
use log::debug;
23-
use pci::DeviceRelocation;
23+
use pci::{DeviceRelocation, PciBarRegionType, PciDevice};
2424
use serde::{Deserialize, Serialize};
25+
use vm_allocator::RangeInclusive;
2526
use vm_device::interrupt::{
2627
InterruptIndex, InterruptSourceConfig, InterruptSourceGroup, MsiIrqSourceConfig,
2728
};
2829
use vmm_sys_util::errno;
2930
use vmm_sys_util::eventfd::EventFd;
3031

3132
pub use crate::arch::{ArchVm as Vm, ArchVmError, VmState};
33+
use crate::devices::virtio::transport::pci::device::VirtioPciDevice;
3234
use crate::logger::info;
3335
use crate::persist::CreateSnapshotError;
3436
use crate::snapshot::Persist;
@@ -612,23 +614,90 @@ impl Vm {
612614
impl DeviceRelocation for Vm {
613615
fn move_bar(
614616
&self,
615-
_old_base: u64,
616-
_new_base: u64,
617-
_len: u64,
618-
_pci_dev: &mut dyn pci::PciDevice,
619-
_region_type: pci::PciBarRegionType,
617+
old_base: u64,
618+
new_base: u64,
619+
len: u64,
620+
pci_dev: &mut dyn pci::PciDevice,
621+
region_type: pci::PciBarRegionType,
620622
) -> Result<(), std::io::Error> {
621-
todo!()
623+
debug!("pci: moving BAR from {old_base:#x}:{len:#x} to {new_base:#x}:{len:#x}");
624+
match region_type {
625+
PciBarRegionType::IoRegion => {
626+
#[cfg(target_arch = "x86_64")]
627+
// We do not allocate IO addresses, we just hard-code them, no need to handle
628+
// (re)allocations. Just update PIO bus
629+
self.pio_bus
630+
.update_range(old_base, len, new_base, len)
631+
.map_err(std::io::Error::other)?;
632+
633+
#[cfg(target_arch = "aarch64")]
634+
return Err(std::io::Error::other(
635+
"pci: IO regions not supported on Aarch64",
636+
));
637+
}
638+
PciBarRegionType::Memory32BitRegion | PciBarRegionType::Memory64BitRegion => {
639+
let old_range =
640+
RangeInclusive::new(old_base, old_base + len - 1).map_err(|_| {
641+
std::io::Error::other("pci: invalid old range for device relocation")
642+
})?;
643+
let allocator = if region_type == PciBarRegionType::Memory32BitRegion {
644+
&self.common.resource_allocator.mmio32_memory
645+
} else {
646+
&self.common.resource_allocator.mmio64_memory
647+
};
648+
649+
allocator
650+
.lock()
651+
.expect("Poisoned lock")
652+
.free(&old_range)
653+
.map_err(|_| {
654+
std::io::Error::other("pci: failed deallocating old MMIO range")
655+
})?;
656+
657+
allocator
658+
.lock()
659+
.unwrap()
660+
.allocate(len, len, vm_allocator::AllocPolicy::ExactMatch(new_base))
661+
.map_err(|_| std::io::Error::other("pci: failed allocating new MMIO range"))?;
662+
663+
// Update MMIO bus
664+
self.common
665+
.mmio_bus
666+
.update_range(old_base, len, new_base, len)
667+
.map_err(std::io::Error::other)?;
668+
}
669+
}
670+
671+
if let Some(virtio_pci_dev) = pci_dev.as_any_mut().downcast_mut::<VirtioPciDevice>() {
672+
if virtio_pci_dev.config_bar_addr() == new_base {
673+
virtio_pci_dev.unregister_notification_ioevent(self)?;
674+
}
675+
676+
virtio_pci_dev.move_bar(old_base, new_base)?;
677+
678+
if virtio_pci_dev.config_bar_addr() == new_base {
679+
virtio_pci_dev.register_notification_ioevent(self)?;
680+
}
681+
} else {
682+
pci_dev.move_bar(old_base, new_base)?;
683+
}
684+
685+
Ok(())
622686
}
623687
}
624688

625689
#[cfg(test)]
626690
pub(crate) mod tests {
691+
use std::ops::DerefMut;
692+
693+
use pci::PciBdf;
694+
use vm_allocator::AllocPolicy;
627695
use vm_device::interrupt::{InterruptSourceConfig, LegacyIrqSourceConfig};
628696
use vm_memory::GuestAddress;
629697
use vm_memory::mmap::MmapRegionBuilder;
630698

631699
use super::*;
700+
use crate::device_manager::mmio::tests::DummyDevice;
632701
use crate::test_utils::single_region_mem_raw;
633702
use crate::utils::mib_to_bytes;
634703
use crate::vstate::kvm::Kvm;
@@ -967,4 +1036,189 @@ pub(crate) mod tests {
9671036
assert!(!new_vector.enabled.load(Ordering::Acquire));
9681037
}
9691038
}
1039+
1040+
fn new_virtio_pci_device(vm: &Arc<Vm>) -> Arc<Mutex<VirtioPciDevice>> {
1041+
let dummy = Arc::new(Mutex::new(DummyDevice::new()));
1042+
let msi_vectors = Arc::new(Vm::create_msix_group(vm.clone(), 0, 2).unwrap());
1043+
Arc::new(Mutex::new(
1044+
VirtioPciDevice::new(
1045+
"dummy".to_string(),
1046+
vm.guest_memory().clone(),
1047+
dummy,
1048+
msi_vectors,
1049+
PciBdf::new(0, 0, 1, 0).into(),
1050+
)
1051+
.unwrap(),
1052+
))
1053+
}
1054+
1055+
#[cfg(target_arch = "aarch64")]
1056+
#[test]
1057+
fn test_device_relocation_no_io_on_arm() {
1058+
let (_, vm) = setup_vm_with_memory(mib_to_bytes(128));
1059+
let vm = Arc::new(vm);
1060+
let old_base = 0x42;
1061+
let new_base = 0x84;
1062+
let len = 0x1312000;
1063+
let virtio_dev = new_virtio_pci_device(&vm);
1064+
let mut virtio_dev_locked = virtio_dev.lock().unwrap();
1065+
let pci_dev = virtio_dev_locked.deref_mut();
1066+
1067+
vm.move_bar(old_base, new_base, len, pci_dev, PciBarRegionType::IoRegion)
1068+
.unwrap_err();
1069+
}
1070+
1071+
#[test]
1072+
fn test_device_relocation_bad_ranges() {
1073+
let (_, vm) = setup_vm_with_memory(mib_to_bytes(128));
1074+
let vm = Arc::new(vm);
1075+
let virtio_dev = new_virtio_pci_device(&vm);
1076+
let mut virtio_dev_locked = virtio_dev.lock().unwrap();
1077+
let pci_dev = virtio_dev_locked.deref_mut();
1078+
1079+
// Old region would overflow
1080+
vm.move_bar(0, 0x12, u64::MAX, pci_dev, PciBarRegionType::IoRegion)
1081+
.unwrap_err();
1082+
// New region would overflow
1083+
vm.move_bar(0x13, 0, u64::MAX, pci_dev, PciBarRegionType::IoRegion)
1084+
.unwrap_err();
1085+
}
1086+
1087+
#[test]
1088+
fn test_device_relocation_old_region_not_allocated() {
1089+
let (_, vm) = setup_vm_with_memory(mib_to_bytes(128));
1090+
let vm = Arc::new(vm);
1091+
let virtio_dev = new_virtio_pci_device(&vm);
1092+
let mut virtio_dev_locked = virtio_dev.lock().unwrap();
1093+
let pci_dev = virtio_dev_locked.deref_mut();
1094+
1095+
let err = vm
1096+
.move_bar(
1097+
0x12,
1098+
0x13,
1099+
0x42,
1100+
pci_dev,
1101+
PciBarRegionType::Memory32BitRegion,
1102+
)
1103+
.unwrap_err();
1104+
assert_eq!(format!("{err}"), "pci: failed deallocating old MMIO range");
1105+
}
1106+
1107+
#[test]
1108+
fn test_device_relocation_new_region_allocated() {
1109+
let (_, vm) = setup_vm_with_memory(mib_to_bytes(128));
1110+
let vm = Arc::new(vm);
1111+
let virtio_dev = new_virtio_pci_device(&vm);
1112+
let mut virtio_dev_locked = virtio_dev.lock().unwrap();
1113+
let pci_dev = virtio_dev_locked.deref_mut();
1114+
1115+
// Allocate old range and add it to bus
1116+
let old_base = vm
1117+
.common
1118+
.resource_allocator
1119+
.allocate_32bit_mmio_memory(0x1000, 0x1000, AllocPolicy::FirstMatch)
1120+
.unwrap();
1121+
vm.common
1122+
.mmio_bus
1123+
.insert(virtio_dev.clone(), 0x1000, 0x1000)
1124+
.unwrap();
1125+
1126+
// Also allocate new region. This should cause relocation to fail
1127+
let new_base = vm
1128+
.common
1129+
.resource_allocator
1130+
.allocate_32bit_mmio_memory(0x1000, 0x1000, AllocPolicy::FirstMatch)
1131+
.unwrap();
1132+
1133+
let err = vm
1134+
.move_bar(
1135+
old_base,
1136+
new_base,
1137+
0x1000,
1138+
pci_dev,
1139+
PciBarRegionType::Memory32BitRegion,
1140+
)
1141+
.unwrap_err();
1142+
assert_eq!(format!("{err}"), "pci: failed allocating new MMIO range");
1143+
}
1144+
1145+
#[cfg(target_arch = "x86_64")]
1146+
#[test]
1147+
fn test_device_relocation_io_device() {
1148+
let (_, vm) = setup_vm_with_memory(mib_to_bytes(128));
1149+
let vm = Arc::new(vm);
1150+
let virtio_dev = new_virtio_pci_device(&vm);
1151+
let mut virtio_dev_locked = virtio_dev.lock().unwrap();
1152+
let pci_dev = virtio_dev_locked.deref_mut();
1153+
1154+
let err = vm
1155+
.move_bar(0x12, 0x13, 0x42000, pci_dev, PciBarRegionType::IoRegion)
1156+
.unwrap_err();
1157+
assert_eq!(format!("{err}"), "bus_error: MissingAddressRange");
1158+
1159+
// If, instead, we add the device in the PIO bus, everything should work fine
1160+
vm.pio_bus
1161+
.insert(virtio_dev.clone(), 0x12, 0x42000)
1162+
.unwrap();
1163+
1164+
vm.move_bar(0x12, 0x13, 0x42000, pci_dev, PciBarRegionType::IoRegion)
1165+
.unwrap()
1166+
}
1167+
1168+
#[test]
1169+
fn test_device_relocation_mmio_device() {
1170+
let (_, vm) = setup_vm_with_memory(mib_to_bytes(128));
1171+
let vm = Arc::new(vm);
1172+
1173+
let virtio_dev = new_virtio_pci_device(&vm);
1174+
let mut virtio_dev_locked = virtio_dev.lock().unwrap();
1175+
let pci_dev = virtio_dev_locked.deref_mut();
1176+
1177+
let old_base = vm
1178+
.common
1179+
.resource_allocator
1180+
.allocate_64bit_mmio_memory(0x8000, 0x1000, AllocPolicy::FirstMatch)
1181+
.unwrap();
1182+
1183+
let err = vm
1184+
.move_bar(
1185+
old_base,
1186+
old_base + 0x8000,
1187+
0x8000,
1188+
pci_dev,
1189+
PciBarRegionType::Memory64BitRegion,
1190+
)
1191+
.unwrap_err();
1192+
assert_eq!(format!("{err}"), "bus_error: MissingAddressRange");
1193+
1194+
// Need to reset the allocator here. Erroring out left it to a limbo state (old range is
1195+
// deallocated, new range is allocated).
1196+
vm.common
1197+
.resource_allocator
1198+
.mmio64_memory
1199+
.lock()
1200+
.unwrap()
1201+
.free(&RangeInclusive::new(old_base + 0x8000, old_base + 0x8000 + 0x8000 - 1).unwrap())
1202+
.unwrap();
1203+
vm.common
1204+
.resource_allocator
1205+
.allocate_64bit_mmio_memory(0x8000, 0x1000, AllocPolicy::FirstMatch)
1206+
.unwrap();
1207+
1208+
// If we add the device to the MMIO bus, everything should work fine
1209+
vm.common
1210+
.mmio_bus
1211+
.insert(virtio_dev.clone(), old_base, 0x8000)
1212+
.unwrap();
1213+
1214+
println!("old base: {old_base:#x}");
1215+
vm.move_bar(
1216+
old_base,
1217+
old_base + 0x8000,
1218+
0x8000,
1219+
pci_dev,
1220+
PciBarRegionType::Memory64BitRegion,
1221+
)
1222+
.unwrap();
1223+
}
9701224
}

0 commit comments

Comments
 (0)