Skip to content

Commit 27cc729

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 25db112 commit 27cc729

File tree

10 files changed

+283
-124
lines changed

10 files changed

+283
-124
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: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ use crate::logger::debug;
4242
use crate::persist::{MicrovmState, MicrovmStateError};
4343
use crate::resources::VmResources;
4444
use crate::seccomp::BpfThreadMap;
45+
use crate::snapshot::Persist;
4546
use crate::vmm_config::instance_info::InstanceInfo;
4647
use crate::vmm_config::machine_config::MachineConfigError;
4748
use crate::vstate::kvm::{Kvm, KvmError};
@@ -411,8 +412,6 @@ pub fn build_microvm_from_snapshot(
411412
.create_vcpus(vm_resources.machine_config.vcpu_count)
412413
.map_err(StartMicrovmError::Vm)?;
413414

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

@@ -430,16 +429,6 @@ pub fn build_microvm_from_snapshot(
430429
}
431430
}
432431

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-
443432
// Restore vcpus kvm state.
444433
for (vcpu, state) in vcpus.iter_mut().zip(microvm_state.vcpu_states.iter()) {
445434
vcpu.kvm_vcpu
@@ -463,16 +452,21 @@ pub fn build_microvm_from_snapshot(
463452
vm_resources.boot_source.config = microvm_state.vm_info.boot_source;
464453

465454
// Restore devices states.
455+
// Restoring VMGenID injects an interrupt in the guest to notify it about the new generation
456+
// ID. As a result, we need to restore DeviceManager after restoring the KVM state, otherwise
457+
// the injected interrupt will be overwritten.
466458
let device_ctor_args = DeviceRestoreArgs {
467459
mem: vm.guest_memory(),
468460
vm: &vm,
469461
event_manager,
470462
vm_resources,
471463
instance_id: &instance_info.id,
472464
restored_from_file: uffd.is_none(),
465+
vcpus_exit_evt: &vcpus_exit_evt,
473466
};
474-
475-
device_manager.restore(&microvm_state.device_states, device_ctor_args)?;
467+
#[allow(unused_mut)]
468+
let mut device_manager =
469+
DeviceManager::restore(device_ctor_args, &microvm_state.device_states)?;
476470

477471
let mut vmm = Vmm {
478472
events_observer: Some(std::io::stdin()),

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+
// Setup legacy devices in case of x86
355+
#[cfg(target_arch = "x86_64")]
356+
let legacy_devices = Self::create_legacy_devices(
357+
constructor_args.event_manager,
358+
constructor_args.vcpus_exit_evt,
359+
constructor_args.vm,
360+
&resource_allocator,
361+
)?;
362+
363+
// Restore MMIO devices
364+
let mmio_ctor_args = MMIODevManagerConstructorArgs {
365+
mem: constructor_args.mem,
366+
vm: constructor_args.vm,
367+
event_manager: constructor_args.event_manager,
368+
resource_allocator: &resource_allocator,
369+
vm_resources: constructor_args.vm_resources,
370+
instance_id: constructor_args.instance_id,
371+
restored_from_file: constructor_args.restored_from_file,
372+
};
373+
let mmio_devices = MMIODeviceManager::restore(mmio_ctor_args, &state.mmio_state)?;
374+
375+
// Restore ACPI devices
376+
let acpi_ctor_args = ACPIDeviceManagerConstructorArgs {
377+
mem: constructor_args.mem,
378+
resource_allocator: &resource_allocator,
379+
vm: constructor_args.vm,
380+
};
381+
let mut acpi_devices = ACPIDeviceManager::restore(acpi_ctor_args, &state.acpi_state)?;
382+
acpi_devices.notify_vmgenid()?;
383+
384+
// Restore PCI devices
385+
let pci_ctor_args = PciDevicesConstructorArgs {
386+
resource_allocator: &resource_allocator,
387+
};
388+
let pci_devices = PciDevices::restore(pci_ctor_args, &state.pci_state)?;
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)]
@@ -65,24 +66,6 @@ impl PciDevices {
6566
Ok(())
6667
}
6768

68-
pub fn save(&self) -> PciDevicesState {
69-
PciDevicesState {
70-
pci_enabled: self.pci_segment.is_some(),
71-
}
72-
}
73-
74-
pub fn restore(
75-
&mut self,
76-
state: &PciDevicesState,
77-
resource_allocator: &Arc<ResourceAllocator>,
78-
) -> Result<(), PciManagerError> {
79-
if state.pci_enabled {
80-
self.attach_pci_segment(resource_allocator)?;
81-
}
82-
83-
Ok(())
84-
}
85-
8669
fn register_bars_with_bus(
8770
resource_allocator: &ResourceAllocator,
8871
virtio_device: &Arc<Mutex<VirtioPciDevice>>,
@@ -194,3 +177,33 @@ impl PciDevices {
194177
pub struct PciDevicesState {
195178
pci_enabled: bool,
196179
}
180+
181+
#[derive(Debug)]
182+
pub struct PciDevicesConstructorArgs<'a> {
183+
pub resource_allocator: &'a Arc<ResourceAllocator>,
184+
}
185+
186+
impl<'a> Persist<'a> for PciDevices {
187+
type State = PciDevicesState;
188+
type ConstructorArgs = PciDevicesConstructorArgs<'a>;
189+
type Error = PciManagerError;
190+
191+
fn save(&self) -> Self::State {
192+
PciDevicesState {
193+
pci_enabled: self.pci_segment.is_some(),
194+
}
195+
}
196+
197+
fn restore(
198+
constructor_args: Self::ConstructorArgs,
199+
state: &Self::State,
200+
) -> std::result::Result<Self, Self::Error> {
201+
let mut pci_devices = PciDevices::new();
202+
203+
if state.pci_enabled {
204+
pci_devices.attach_pci_segment(constructor_args.resource_allocator)?;
205+
}
206+
207+
Ok(pci_devices)
208+
}
209+
}

0 commit comments

Comments
 (0)