Skip to content

Commit a90bdad

Browse files
committed
device_manager: save resource allocator in snapshot
vm-allocator now allows us to (De)serialize IdAllocator and AddressAllocator types. Add ResourceAllocator in DeviceManager snapshot state and restore it when loading a snapshot. Like this we can avoid doing the ExactMatch allocations during snapshot resumes for reserving the exact same MMIO ranges. Moreover, change DeviceManager and PciDevices to provide save/restore functionality via the Persist trait. Like that we can avoid first creating the objects and then restoring their state, overwriting their fields. Signed-off-by: Babis Chalios <[email protected]>
1 parent edac335 commit a90bdad

File tree

10 files changed

+293
-134
lines changed

10 files changed

+293
-134
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/vmm/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ userfaultfd = "0.8.1"
5252
utils = { path = "../utils" }
5353
uuid = "1.16.0"
5454
vhost = { version = "0.14.0", features = ["vhost-user-frontend"] }
55-
vm-allocator = "0.1.2"
55+
vm-allocator = { version = "0.1.2", features = ["serde"] }
5656
vm-device = { path = "../vm-device" }
5757
vm-memory = { version = "0.16.2", features = [
5858
"backend-mmap",

src/vmm/src/builder.rs

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ use crate::logger::debug;
4141
use crate::persist::{MicrovmState, MicrovmStateError};
4242
use crate::resources::VmResources;
4343
use crate::seccomp::BpfThreadMap;
44+
use crate::snapshot::Persist;
4445
use crate::vmm_config::instance_info::InstanceInfo;
4546
use crate::vmm_config::machine_config::MachineConfigError;
4647
use crate::vstate::kvm::Kvm;
@@ -410,12 +411,25 @@ pub fn build_microvm_from_snapshot(
410411
.create_vcpus(vm_resources.machine_config.vcpu_count)
411412
.unwrap();
412413

413-
let mut device_manager = DeviceManager::new(event_manager, &vcpus_exit_evt, &vm).unwrap();
414-
415414
vm.register_memory_regions(guest_memory)
416415
.map_err(VmmError::Vm)
417416
.map_err(StartMicrovmError::Internal)?;
418417

418+
// Restore devices states.
419+
let device_ctor_args = DeviceRestoreArgs {
420+
mem: vm.guest_memory(),
421+
vm: &vm,
422+
event_manager,
423+
vm_resources,
424+
instance_id: &instance_info.id,
425+
restored_from_file: uffd.is_none(),
426+
vcpus_exit_evt: &vcpus_exit_evt,
427+
};
428+
429+
#[allow(unused_mut)]
430+
let mut device_manager =
431+
DeviceManager::restore(device_ctor_args, &microvm_state.device_states)?;
432+
419433
#[cfg(target_arch = "x86_64")]
420434
{
421435
// Scale TSC to match, extract the TSC freq from the state if specified
@@ -430,16 +444,6 @@ pub fn build_microvm_from_snapshot(
430444
}
431445
}
432446

433-
// Restore allocator state
434-
#[cfg(target_arch = "aarch64")]
435-
if let Some(pvtime_ipa) = vcpus[0].kvm_vcpu.pvtime_ipa {
436-
allocate_pvtime_region(
437-
&mut device_manager,
438-
vcpus.len(),
439-
vm_allocator::AllocPolicy::ExactMatch(pvtime_ipa.0),
440-
)?;
441-
}
442-
443447
// Restore vcpus kvm state.
444448
for (vcpu, state) in vcpus.iter_mut().zip(microvm_state.vcpu_states.iter()) {
445449
vcpu.kvm_vcpu
@@ -462,18 +466,6 @@ pub fn build_microvm_from_snapshot(
462466
// Restore the boot source config paths.
463467
vm_resources.boot_source.config = microvm_state.vm_info.boot_source;
464468

465-
// Restore devices states.
466-
let device_ctor_args = DeviceRestoreArgs {
467-
mem: vm.guest_memory(),
468-
vm: &vm,
469-
event_manager,
470-
vm_resources,
471-
instance_id: &instance_info.id,
472-
restored_from_file: uffd.is_none(),
473-
};
474-
475-
device_manager.restore(&microvm_state.device_states, device_ctor_args)?;
476-
477469
let mut vmm = Vmm {
478470
events_observer: Some(std::io::stdin()),
479471
instance_info: instance_info.clone(),

src/vmm/src/device_manager/mod.rs

Lines changed: 101 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use legacy::{LegacyDeviceError, PortIODeviceManager};
1515
use linux_loader::loader::Cmdline;
1616
use log::error;
1717
use mmio::{MMIODeviceManager, MmioError};
18-
use pci_mngr::{PciDevices, PciManagerError};
18+
use pci_mngr::{PciDevices, PciDevicesConstructorArgs, PciManagerError};
1919
use persist::{ACPIDeviceManagerConstructorArgs, MMIODevManagerConstructorArgs};
2020
use resources::ResourceAllocator;
2121
use serde::{Deserialize, Serialize};
@@ -127,30 +127,39 @@ impl DeviceManager {
127127
Ok(serial)
128128
}
129129

130+
#[cfg(target_arch = "x86_64")]
131+
fn create_legacy_devices(
132+
event_manager: &mut EventManager,
133+
vcpus_exit_evt: &EventFd,
134+
vm: &Vm,
135+
resource_allocator: &ResourceAllocator,
136+
) -> Result<PortIODeviceManager, DeviceManagerCreateError> {
137+
Self::set_stdout_nonblocking();
138+
139+
// Create serial device
140+
let serial = Self::setup_serial_device(event_manager)?;
141+
let reset_evt = vcpus_exit_evt
142+
.try_clone()
143+
.map_err(DeviceManagerCreateError::EventFd)?;
144+
// Create keyboard emulator for reset event
145+
let i8042 = Arc::new(Mutex::new(I8042Device::new(reset_evt)?));
146+
147+
// create pio dev manager with legacy devices
148+
let mut legacy_devices = PortIODeviceManager::new(serial, i8042)?;
149+
legacy_devices.register_devices(&resource_allocator.pio_bus, vm)?;
150+
Ok(legacy_devices)
151+
}
152+
130153
#[cfg_attr(target_arch = "aarch64", allow(unused))]
131154
pub fn new(
132155
event_manager: &mut EventManager,
133-
vcpu_exit_evt: &EventFd,
156+
vcpus_exit_evt: &EventFd,
134157
vm: &Vm,
135158
) -> Result<Self, DeviceManagerCreateError> {
136159
let resource_allocator = Arc::new(ResourceAllocator::new()?);
137160
#[cfg(target_arch = "x86_64")]
138-
let legacy_devices = {
139-
Self::set_stdout_nonblocking();
140-
141-
// Create serial device
142-
let serial = Self::setup_serial_device(event_manager)?;
143-
let reset_evt = vcpu_exit_evt
144-
.try_clone()
145-
.map_err(DeviceManagerCreateError::EventFd)?;
146-
// Create keyboard emulator for reset event
147-
let i8042 = Arc::new(Mutex::new(I8042Device::new(reset_evt)?));
148-
149-
// create pio dev manager with legacy devices
150-
let mut legacy_devices = PortIODeviceManager::new(serial, i8042)?;
151-
legacy_devices.register_devices(&resource_allocator.pio_bus, vm)?;
152-
legacy_devices
153-
};
161+
let legacy_devices =
162+
Self::create_legacy_devices(event_manager, vcpus_exit_evt, vm, &resource_allocator)?;
154163

155164
Ok(DeviceManager {
156165
resource_allocator,
@@ -270,6 +279,8 @@ impl DeviceManager {
270279
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
271280
/// State of devices in the system
272281
pub struct DevicesState {
282+
/// Resource allocator state
283+
pub resource_allocator_state: resources::ResourceAllocatorState,
273284
/// MMIO devices state
274285
pub mmio_state: persist::DeviceStates,
275286
/// ACPI devices state
@@ -292,12 +303,15 @@ pub enum DevicePersistError {
292303
SerialRestore(#[from] EmulateSerialInitError),
293304
/// Error inserting device in bus: {0}
294305
Bus(#[from] vm_device::BusError),
306+
/// Error creating DeviceManager: {0}
307+
DeviceManager(#[from] DeviceManagerCreateError),
295308
}
296309

297310
pub struct DeviceRestoreArgs<'a> {
298311
pub mem: &'a GuestMemoryMmap,
299312
pub vm: &'a Vm,
300313
pub event_manager: &'a mut EventManager,
314+
pub vcpus_exit_evt: &'a EventFd,
301315
pub vm_resources: &'a mut VmResources,
302316
pub instance_id: &'a str,
303317
pub restored_from_file: bool,
@@ -315,15 +329,82 @@ impl std::fmt::Debug for DeviceRestoreArgs<'_> {
315329
}
316330
}
317331

318-
impl DeviceManager {
319-
pub fn save(&self) -> DevicesState {
332+
impl<'a> Persist<'a> for DeviceManager {
333+
type State = DevicesState;
334+
type ConstructorArgs = DeviceRestoreArgs<'a>;
335+
type Error = DevicePersistError;
336+
337+
fn save(&self) -> Self::State {
320338
DevicesState {
339+
resource_allocator_state: self.resource_allocator.save(),
321340
mmio_state: self.mmio_devices.save(),
322341
acpi_state: self.acpi_devices.save(),
323342
pci_state: self.pci_devices.save(),
324343
}
325344
}
326345

346+
fn restore(
347+
constructor_args: Self::ConstructorArgs,
348+
state: &Self::State,
349+
) -> std::result::Result<Self, Self::Error> {
350+
// Safe to unwrap here. ResourceAllocator restoring cannot fail.
351+
let resource_allocator =
352+
Arc::new(ResourceAllocator::restore((), &state.resource_allocator_state).unwrap());
353+
354+
// Restore MMIO devices
355+
let mmio_ctor_args = MMIODevManagerConstructorArgs {
356+
mem: constructor_args.mem,
357+
vm: constructor_args.vm,
358+
event_manager: constructor_args.event_manager,
359+
resource_allocator: &resource_allocator,
360+
vm_resources: constructor_args.vm_resources,
361+
instance_id: constructor_args.instance_id,
362+
restored_from_file: constructor_args.restored_from_file,
363+
};
364+
let mmio_devices = MMIODeviceManager::restore(mmio_ctor_args, &state.mmio_state)?;
365+
366+
// Restore ACPI devices
367+
let acpi_ctor_args = ACPIDeviceManagerConstructorArgs {
368+
mem: constructor_args.mem,
369+
resource_allocator: &resource_allocator,
370+
vm: constructor_args.vm,
371+
};
372+
let mut acpi_devices = ACPIDeviceManager::restore(acpi_ctor_args, &state.acpi_state)?;
373+
acpi_devices.notify_vmgenid()?;
374+
375+
// Restore PCI devices
376+
let pci_ctor_args = PciDevicesConstructorArgs {
377+
resource_allocator: &resource_allocator,
378+
};
379+
let pci_devices = PciDevices::restore(pci_ctor_args, &state.pci_state)?;
380+
381+
// Setup legacy devices in case of x86
382+
#[cfg(target_arch = "x86_64")]
383+
let legacy_devices = Self::create_legacy_devices(
384+
constructor_args.event_manager,
385+
constructor_args.vcpus_exit_evt,
386+
constructor_args.vm,
387+
&resource_allocator,
388+
)?;
389+
390+
let device_manager = DeviceManager {
391+
resource_allocator,
392+
mmio_devices,
393+
#[cfg(target_arch = "x86_64")]
394+
legacy_devices,
395+
acpi_devices,
396+
pci_devices,
397+
};
398+
399+
// Restore serial.
400+
// We need to do that after we restore mmio devices, otherwise it won't succeed in Aarch64
401+
device_manager.emulate_serial_init()?;
402+
403+
Ok(device_manager)
404+
}
405+
}
406+
407+
impl DeviceManager {
327408
/// Sets RDA bit in serial console
328409
pub fn emulate_serial_init(&self) -> Result<(), EmulateSerialInitError> {
329410
// When restoring from a previously saved state, there is no serial
@@ -361,43 +442,6 @@ impl DeviceManager {
361442
Ok(())
362443
}
363444
}
364-
365-
pub fn restore(
366-
&mut self,
367-
state: &DevicesState,
368-
restore_args: DeviceRestoreArgs,
369-
) -> Result<(), DevicePersistError> {
370-
// Restore MMIO devices
371-
let mmio_ctor_args = MMIODevManagerConstructorArgs {
372-
mem: restore_args.mem,
373-
vm: restore_args.vm,
374-
event_manager: restore_args.event_manager,
375-
resource_allocator: &self.resource_allocator,
376-
vm_resources: restore_args.vm_resources,
377-
instance_id: restore_args.instance_id,
378-
restored_from_file: restore_args.restored_from_file,
379-
};
380-
self.mmio_devices = MMIODeviceManager::restore(mmio_ctor_args, &state.mmio_state)?;
381-
382-
// Restore serial.
383-
// We need to do that after we restore mmio devices, otherwise it won't succeed in Aarch64
384-
self.emulate_serial_init()?;
385-
386-
// Restore ACPI devices
387-
let acpi_ctor_args = ACPIDeviceManagerConstructorArgs {
388-
mem: restore_args.mem,
389-
resource_allocator: &self.resource_allocator,
390-
vm: restore_args.vm,
391-
};
392-
self.acpi_devices = ACPIDeviceManager::restore(acpi_ctor_args, &state.acpi_state)?;
393-
self.acpi_devices.notify_vmgenid()?;
394-
395-
// Restore PCI devices
396-
self.pci_devices
397-
.restore(&state.pci_state, &self.resource_allocator)?;
398-
399-
Ok(())
400-
}
401445
}
402446

403447
#[cfg(test)]

src/vmm/src/device_manager/pci_mngr.rs

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use crate::device_manager::resources::ResourceAllocator;
1616
use crate::devices::pci::PciSegment;
1717
use crate::devices::virtio::device::VirtioDevice;
1818
use crate::devices::virtio::transport::pci::device::{VirtioPciDevice, VirtioPciDeviceError};
19+
use crate::snapshot::Persist;
1920
use crate::vstate::vm::InterruptError;
2021

2122
#[derive(Debug, Default)]
@@ -63,24 +64,6 @@ impl PciDevices {
6364
Ok(())
6465
}
6566

66-
pub fn save(&self) -> PciDevicesState {
67-
PciDevicesState {
68-
pci_enabled: self.pci_segment.is_some(),
69-
}
70-
}
71-
72-
pub fn restore(
73-
&mut self,
74-
state: &PciDevicesState,
75-
resource_allocator: &Arc<ResourceAllocator>,
76-
) -> Result<(), PciManagerError> {
77-
if state.pci_enabled {
78-
self.attach_pci_segment(resource_allocator)?;
79-
}
80-
81-
Ok(())
82-
}
83-
8467
pub(crate) fn attach_pci_virtio_device<
8568
T: 'static + VirtioDevice + MutEventSubscriber + Debug,
8669
>(
@@ -186,3 +169,33 @@ impl PciDevices {
186169
pub struct PciDevicesState {
187170
pci_enabled: bool,
188171
}
172+
173+
#[derive(Debug)]
174+
pub struct PciDevicesConstructorArgs<'a> {
175+
pub resource_allocator: &'a Arc<ResourceAllocator>,
176+
}
177+
178+
impl<'a> Persist<'a> for PciDevices {
179+
type State = PciDevicesState;
180+
type ConstructorArgs = PciDevicesConstructorArgs<'a>;
181+
type Error = PciManagerError;
182+
183+
fn save(&self) -> Self::State {
184+
PciDevicesState {
185+
pci_enabled: self.pci_segment.is_some(),
186+
}
187+
}
188+
189+
fn restore(
190+
constructor_args: Self::ConstructorArgs,
191+
state: &Self::State,
192+
) -> std::result::Result<Self, Self::Error> {
193+
let mut pci_devices = PciDevices::new();
194+
195+
if state.pci_enabled {
196+
pci_devices.attach_pci_segment(constructor_args.resource_allocator)?;
197+
}
198+
199+
Ok(pci_devices)
200+
}
201+
}

0 commit comments

Comments
 (0)