Skip to content

Commit eeefec4

Browse files
committed
Add snapshot save and restore functionality to CpuContainer
Implement Persist and the required functions for saving and restoring snapshots Note: Currently when saving a snapshot, custom CPU templates are not saved. This is because, up until now, it has only been needed at initialization. With the implementation of hotplugging however, CPU templates and the vCPU config are now required for the lifetime of the VMM. As a result of this, hotplugging will throw an error after restoring from snapshot. Signed-off-by: James Curtis <[email protected]>
1 parent 4be023f commit eeefec4

File tree

5 files changed

+136
-3
lines changed

5 files changed

+136
-3
lines changed

src/vmm/src/builder.rs

+47
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ use vm_superio::Serial;
2929

3030
#[cfg(target_arch = "x86_64")]
3131
use crate::acpi;
32+
#[cfg(target_arch = "x86_64")]
33+
use crate::arch::DeviceType;
3234
use crate::arch::InitrdConfig;
3335
#[cfg(target_arch = "aarch64")]
3436
use crate::construct_kvm_mpidrs;
@@ -58,6 +60,8 @@ use crate::devices::virtio::mmio::MmioTransport;
5860
use crate::devices::virtio::net::Net;
5961
use crate::devices::virtio::rng::Entropy;
6062
use crate::devices::virtio::vsock::{Vsock, VsockUnixBackend};
63+
#[cfg(target_arch = "x86_64")]
64+
use crate::devices::BusDevice;
6165
use crate::logger::{debug, error};
6266
use crate::persist::{MicrovmState, MicrovmStateError};
6367
use crate::resources::VmResources;
@@ -451,6 +455,8 @@ pub enum BuildMicrovmFromSnapshotError {
451455
StartVcpus(#[from] crate::StartVcpusError),
452456
/// Failed to restore vCPUs: {0}
453457
RestoreVcpus(#[from] VcpuError),
458+
/// Failed to restore CPUID: {0}
459+
RestoreCpuId(#[from] GuestConfigError),
454460
/// Failed to apply VMM secccomp filter as none found.
455461
MissingVmmSeccompFilters,
456462
/// Failed to apply VMM secccomp filter: {0}
@@ -522,6 +528,31 @@ pub fn build_microvm_from_snapshot(
522528
#[cfg(target_arch = "x86_64")]
523529
vmm.vm.restore_state(&microvm_state.vm_state)?;
524530

531+
#[cfg(target_arch = "x86_64")]
532+
{
533+
// FIXME: Custom templates are not stored when snapshots are taken, as they were previously
534+
// only needed at boottime. With hotplugging, the template could be necessary at any time,
535+
// so snapshot must be modified in order to save custom templates
536+
537+
// let cpuid = Cpuid::try_from(vmm.vm.supported_cpuid().clone())
538+
// .map_err(GuestConfigError::CpuidFromKvmCpuid)?;
539+
540+
// let_cpu_template = &microvm_state.vm_info.cpu_template;
541+
//
542+
// let msrs = vcpus[0]
543+
// .kvm_vcpu
544+
// .get_msrs(&[])
545+
// .map_err(GuestConfigError::VcpuIoctl)?;
546+
//
547+
// let cpu_config = CpuConfiguration { cpuid, msrs };
548+
//
549+
// let vcpu_config = VcpuConfig {
550+
// vcpu_count: vcpus.len().try_into().unwrap(),
551+
// smt: microvm_state.vm_info.smt,
552+
// cpu_config,
553+
// };
554+
}
555+
525556
// Restore the boot source config paths.
526557
vm_resources.set_boot_source_config(microvm_state.vm_info.boot_source);
527558

@@ -550,6 +581,22 @@ pub fn build_microvm_from_snapshot(
550581
vmm.acpi_device_manager =
551582
ACPIDeviceManager::restore(acpi_ctor_args, &microvm_state.acpi_dev_state)?;
552583

584+
#[cfg(target_arch = "x86_64")]
585+
if let Some(BusDevice::CpuContainer(container)) = vmm.get_bus_device(
586+
DeviceType::CpuContainer,
587+
&DeviceType::CpuContainer.to_string(),
588+
) {
589+
vmm.acpi_device_manager
590+
.attach_cpu_container(container.clone(), vmm.vm.fd())
591+
.map_err(|err| {
592+
BuildMicrovmFromSnapshotError::ACPIDeviManager(
593+
ACPIDeviceManagerRestoreError::CpuContainer(
594+
CpuContainerError::RegisterIrqFd(err),
595+
),
596+
)
597+
})?;
598+
}
599+
553600
// Inject the notification to VMGenID that we have resumed from a snapshot.
554601
// This needs to happen before we resume vCPUs, so that we minimize the time between vCPUs
555602
// resuming and notification being handled by the driver.

src/vmm/src/device_manager/persist.rs

+44-3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ use super::mmio::*;
1717
use super::resources::ResourceAllocator;
1818
#[cfg(target_arch = "aarch64")]
1919
use crate::arch::DeviceType;
20+
#[cfg(target_arch = "x86_64")]
21+
use crate::devices::acpi::cpu_container::{
22+
CpuContainer, CpuContainerConstructorArgs, CpuContainerError, CpuContainerState,
23+
};
2024
use crate::devices::acpi::vmgenid::{VMGenIDState, VMGenIdConstructorArgs, VmGenId, VmGenIdError};
2125
use crate::devices::virtio::balloon::persist::{BalloonConstructorArgs, BalloonState};
2226
use crate::devices::virtio::balloon::{Balloon, BalloonError};
@@ -55,6 +59,9 @@ pub enum DevicePersistError {
5559
Balloon(#[from] BalloonError),
5660
/// Block: {0}
5761
Block(#[from] BlockError),
62+
/// CpuContainer: {0}
63+
#[cfg(target_arch = "x86_64")]
64+
CpuContainer(#[from] CpuContainerError),
5865
/// Device manager: {0}
5966
DeviceManager(#[from] super::mmio::MmioError),
6067
/// Mmio transport
@@ -151,6 +158,13 @@ pub struct ConnectedLegacyState {
151158
pub device_info: MMIODeviceInfo,
152159
}
153160

161+
#[cfg(target_arch = "x86_64")]
162+
#[derive(Debug, Clone, Serialize, Deserialize)]
163+
pub struct ConnectedCpuContainerState {
164+
pub device_state: CpuContainerState,
165+
pub device_info: MMIODeviceInfo,
166+
}
167+
154168
/// Holds the MMDS data store version.
155169
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
156170
pub enum MmdsVersionState {
@@ -194,6 +208,9 @@ pub struct DeviceStates {
194208
pub mmds_version: Option<MmdsVersionState>,
195209
/// Entropy device state.
196210
pub entropy_device: Option<ConnectedEntropyState>,
211+
/// Cpu Container device state.
212+
#[cfg(target_arch = "x86_64")]
213+
pub cpu_container: Option<ConnectedCpuContainerState>,
197214
}
198215

199216
/// A type used to extract the concrete `Arc<Mutex<T>>` for each of the device
@@ -245,6 +262,9 @@ pub enum ACPIDeviceManagerRestoreError {
245262
Interrupt(#[from] kvm_ioctls::Error),
246263
/// Could not create VMGenID device: {0}
247264
VMGenID(#[from] VmGenIdError),
265+
/// Could not create CpuContainer device: {0}
266+
#[cfg(target_arch = "x86_64")]
267+
CpuContainer(#[from] CpuContainerError),
248268
}
249269

250270
impl<'a> Persist<'a> for ACPIDeviceManager {
@@ -283,11 +303,21 @@ impl<'a> Persist<'a> for MMIODeviceManager {
283303
type Error = DevicePersistError;
284304

285305
fn save(&self) -> Self::State {
306+
use crate::devices::bus::BusDevice::*;
286307
let mut states = DeviceStates::default();
308+
#[allow(unused_variables)]
287309
let _: Result<(), ()> = self.for_each_device(|devtype, devid, device_info, bus_dev| {
288-
if *devtype == crate::arch::DeviceType::BootTimer {
289-
// No need to save BootTimer state.
290-
return Ok(());
310+
match bus_dev {
311+
BootTimer(_) => return Ok(()),
312+
#[cfg(target_arch = "x86_64")]
313+
CpuContainer(x) => {
314+
states.cpu_container = Some(ConnectedCpuContainerState {
315+
device_state: x.lock().expect("Poisoned lock").save(),
316+
device_info: device_info.clone(),
317+
});
318+
return Ok(());
319+
}
320+
_ => (),
291321
}
292322

293323
#[cfg(target_arch = "aarch64")]
@@ -644,6 +674,17 @@ impl<'a> Persist<'a> for MMIODeviceManager {
644674
)?;
645675
}
646676

677+
#[cfg(target_arch = "x86_64")]
678+
if let Some(container) = &state.cpu_container {
679+
let ctor_args = CpuContainerConstructorArgs { vm };
680+
let device = Arc::new(Mutex::new(CpuContainer::restore(
681+
ctor_args,
682+
&container.device_state,
683+
)?));
684+
685+
dev_manager.register_mmio_cpu_container(vm, device, &container.device_info)?;
686+
}
687+
647688
Ok(dev_manager)
648689
}
649690
}

src/vmm/src/devices/acpi/cpu_container.rs

+42
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
use acpi_tables::madt::LocalAPIC;
77
use acpi_tables::{aml, Aml};
8+
use kvm_ioctls::VmFd;
89
use log::{debug, error};
910
use serde::{Deserialize, Serialize};
1011
use utils::eventfd::EventFd;
@@ -15,6 +16,7 @@ use zerocopy::AsBytes;
1516
use crate::device_manager::mmio::MMIO_LEN;
1617
use crate::device_manager::resources::ResourceAllocator;
1718
use crate::devices::legacy::EventFdTrigger;
19+
use crate::snapshot::Persist;
1820
use crate::vmm_config::machine_config::MAX_SUPPORTED_VCPUS;
1921

2022
#[derive(Debug)]
@@ -150,6 +152,33 @@ impl CpuContainer {
150152
}
151153
}
152154

155+
impl<'a> Persist<'a> for CpuContainer {
156+
type State = CpuContainerState;
157+
type ConstructorArgs = CpuContainerConstructorArgs<'a>;
158+
type Error = CpuContainerError;
159+
160+
fn save(&self) -> Self::State {
161+
Self::State {
162+
mmio_address: self.mmio_address.0,
163+
gsi: self.gsi,
164+
cpu_devices: self.cpu_devices.clone(),
165+
selected_cpu: self.selected_cpu,
166+
}
167+
}
168+
169+
fn restore(
170+
_constructor_args: Self::ConstructorArgs,
171+
state: &Self::State,
172+
) -> std::result::Result<Self, Self::Error> {
173+
Self::from_parts(
174+
GuestAddress(state.mmio_address),
175+
state.gsi,
176+
state.selected_cpu,
177+
state.cpu_devices.clone(),
178+
)
179+
}
180+
}
181+
153182
impl Aml for CpuContainer {
154183
fn append_aml_bytes(&self, v: &mut Vec<u8>) {
155184
// CPU hotplug controller
@@ -220,6 +249,19 @@ impl Aml for CpuContainer {
220249
}
221250
}
222251

252+
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
253+
pub struct CpuContainerState {
254+
pub mmio_address: u64,
255+
pub gsi: u32,
256+
pub cpu_devices: Vec<CpuDevice>,
257+
pub selected_cpu: u8,
258+
}
259+
260+
#[derive(Debug, Clone)]
261+
pub struct CpuContainerConstructorArgs<'a> {
262+
pub vm: &'a VmFd,
263+
}
264+
223265
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
224266
pub struct CpuDevice {
225267
/// The ID of this CPU, matches APICID

src/vmm/src/vmm_config/hotplug.rs

+2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ pub enum HotplugVcpuError {
3838
VcpuStart(StartVcpusError),
3939
/// No seccomp filter for thread category: {0}
4040
MissingSeccompFilters(String),
41+
/// Cannot hotplug vCPUs after restoring from snapshot
42+
RestoredFromSnapshot,
4143
}
4244

4345
/// Config for hotplugging vCPUS

tests/integration_tests/functional/test_api.py

+1
Original file line numberDiff line numberDiff line change
@@ -1146,6 +1146,7 @@ def test_get_full_config_after_restoring_snapshot(microvm_factory, uvm_nano):
11461146

11471147
# Start the microvm.
11481148
uvm_nano.start()
1149+
uvm_nano.wait_for_up()
11491150

11501151
# Add a tx rate limiter to the net device.
11511152
tx_rl = {

0 commit comments

Comments
 (0)