Skip to content

Commit 95d3367

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 e839cca commit 95d3367

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,13 +20,15 @@ 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::{InterruptSourceGroup, MsiIrqSourceConfig};
2627
use vmm_sys_util::errno;
2728
use vmm_sys_util::eventfd::EventFd;
2829

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

637701
#[cfg(test)]
638702
pub(crate) mod tests {
703+
use std::ops::DerefMut;
704+
705+
use pci::PciBdf;
706+
use vm_allocator::AllocPolicy;
639707
use vm_device::interrupt::{InterruptSourceConfig, LegacyIrqSourceConfig};
640708
use vm_memory::GuestAddress;
641709
use vm_memory::mmap::MmapRegionBuilder;
642710

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

0 commit comments

Comments
 (0)