Skip to content

Commit f81a332

Browse files
committed
refactor: cleanup vmm::snapshot module
Implement the suggestions from #4523 to give a cleaner API for the snapshot module. Conceptually, the change is that we now store the thing we are snapshotting inside of the `Snapshot` struct. Implementation-wise, this allows us to deserialize the snapshot data in a single pass (instead of having to read it twice, once for deserialization and once for CRC validation), which means we do not have to first query the snapshot file for the size of the snapshot. Closes #4691, #4523, #5195 Signed-off-by: Patrick Roy <[email protected]>
1 parent 72fda20 commit f81a332

File tree

21 files changed

+226
-307
lines changed

21 files changed

+226
-307
lines changed

src/firecracker/src/main.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use vmm::persist::SNAPSHOT_VERSION;
2828
use vmm::resources::VmResources;
2929
use vmm::seccomp::BpfThreadMap;
3030
use vmm::signal_handler::register_signal_handlers;
31-
use vmm::snapshot::{Snapshot, SnapshotError};
31+
use vmm::snapshot::{SnapshotError, get_format_version};
3232
use vmm::vmm_config::instance_info::{InstanceInfo, VmState};
3333
use vmm::vmm_config::metrics::{MetricsConfig, MetricsConfigError, init_metrics};
3434
use vmm::{EventManager, FcExitCode, HTTP_MAX_PAYLOAD_SIZE};
@@ -538,8 +538,8 @@ fn print_snapshot_data_format(snapshot_path: &str) -> Result<(), SnapshotVersion
538538
let mut snapshot_reader =
539539
File::open(snapshot_path).map_err(SnapshotVersionError::OpenSnapshot)?;
540540

541-
let data_format_version = Snapshot::get_format_version(&mut snapshot_reader)
542-
.map_err(SnapshotVersionError::SnapshotVersion)?;
541+
let data_format_version =
542+
get_format_version(&mut snapshot_reader).map_err(SnapshotVersionError::SnapshotVersion)?;
543543

544544
println!("v{}", data_format_version);
545545
Ok(())

src/snapshot-editor/src/edit_vmstate.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ fn edit(
5151
output_path: &PathBuf,
5252
f: impl Fn(MicrovmState) -> Result<MicrovmState, EditVmStateError>,
5353
) -> Result<(), EditVmStateError> {
54-
let (microvm_state, _) = open_vmstate(vmstate_path)?;
55-
let microvm_state = f(microvm_state)?;
54+
let snapshot = open_vmstate(vmstate_path)?;
55+
let microvm_state = f(snapshot.data)?;
5656
save_vmstate(microvm_state, output_path)?;
5757
Ok(())
5858
}

src/snapshot-editor/src/info.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
use std::path::PathBuf;
55

66
use clap::Subcommand;
7-
use semver::Version;
87
use vmm::persist::MicrovmState;
8+
use vmm::snapshot::Snapshot;
99

1010
use crate::utils::*;
1111

@@ -50,27 +50,27 @@ pub fn info_vmstate_command(command: InfoVmStateSubCommand) -> Result<(), InfoVm
5050

5151
fn info(
5252
vmstate_path: &PathBuf,
53-
f: impl Fn(&MicrovmState, Version) -> Result<(), InfoVmStateError>,
53+
f: impl Fn(&Snapshot<MicrovmState>) -> Result<(), InfoVmStateError>,
5454
) -> Result<(), InfoVmStateError> {
55-
let (vmstate, version) = open_vmstate(vmstate_path)?;
56-
f(&vmstate, version)?;
55+
let snapshot = open_vmstate(vmstate_path)?;
56+
f(&snapshot)?;
5757
Ok(())
5858
}
5959

60-
fn info_version(_: &MicrovmState, version: Version) -> Result<(), InfoVmStateError> {
61-
println!("v{version}");
60+
fn info_version(snapshot: &Snapshot<MicrovmState>) -> Result<(), InfoVmStateError> {
61+
println!("v{}", snapshot.version());
6262
Ok(())
6363
}
6464

65-
fn info_vcpu_states(state: &MicrovmState, _: Version) -> Result<(), InfoVmStateError> {
66-
for (i, state) in state.vcpu_states.iter().enumerate() {
65+
fn info_vcpu_states(snapshot: &Snapshot<MicrovmState>) -> Result<(), InfoVmStateError> {
66+
for (i, state) in snapshot.data.vcpu_states.iter().enumerate() {
6767
println!("vcpu {i}:");
6868
println!("{state:#?}");
6969
}
7070
Ok(())
7171
}
7272

73-
fn info_vmstate(vmstate: &MicrovmState, _version: Version) -> Result<(), InfoVmStateError> {
74-
println!("{vmstate:#?}");
73+
fn info_vmstate(snapshot: &Snapshot<MicrovmState>) -> Result<(), InfoVmStateError> {
74+
println!("{:#?}", snapshot.data);
7575
Ok(())
7676
}

src/snapshot-editor/src/utils.rs

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,15 @@
44
use std::fs::{File, OpenOptions};
55
use std::path::PathBuf;
66

7-
use semver::Version;
8-
use vmm::persist::{MicrovmState, SNAPSHOT_VERSION};
7+
use vmm::persist::MicrovmState;
98
use vmm::snapshot::Snapshot;
10-
use vmm::utils::u64_to_usize;
119

1210
// Some errors are only used in aarch64 code
1311
#[allow(unused)]
1412
#[derive(Debug, thiserror::Error, displaydoc::Display)]
1513
pub enum UtilsError {
1614
/// Can not open snapshot file: {0}
1715
VmStateFileOpen(std::io::Error),
18-
/// Can not retrieve metadata for snapshot file: {0}
19-
VmStateFileMeta(std::io::Error),
2016
/// Can not load snapshot: {0}
2117
VmStateLoad(vmm::snapshot::SnapshotError),
2218
/// Can not open output file: {0}
@@ -26,11 +22,9 @@ pub enum UtilsError {
2622
}
2723

2824
#[allow(unused)]
29-
pub fn open_vmstate(snapshot_path: &PathBuf) -> Result<(MicrovmState, Version), UtilsError> {
25+
pub fn open_vmstate(snapshot_path: &PathBuf) -> Result<Snapshot<MicrovmState>, UtilsError> {
3026
let mut snapshot_reader = File::open(snapshot_path).map_err(UtilsError::VmStateFileOpen)?;
31-
let metadata = std::fs::metadata(snapshot_path).map_err(UtilsError::VmStateFileMeta)?;
32-
let snapshot_len = u64_to_usize(metadata.len());
33-
Snapshot::load(&mut snapshot_reader, snapshot_len).map_err(UtilsError::VmStateLoad)
27+
Snapshot::load(&mut snapshot_reader).map_err(UtilsError::VmStateLoad)
3428
}
3529

3630
// This method is used only in aarch64 code so far
@@ -42,9 +36,9 @@ pub fn save_vmstate(microvm_state: MicrovmState, output_path: &PathBuf) -> Resul
4236
.truncate(true)
4337
.open(output_path)
4438
.map_err(UtilsError::OutputFileOpen)?;
45-
let mut snapshot = Snapshot::new(SNAPSHOT_VERSION);
39+
let mut snapshot = Snapshot::new(microvm_state);
4640
snapshot
47-
.save(&mut output_file, &microvm_state)
41+
.save(&mut output_file)
4842
.map_err(UtilsError::VmStateSave)?;
4943
Ok(())
5044
}

src/vmm/src/arch/aarch64/regs.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -546,8 +546,8 @@ mod tests {
546546

547547
let mut buf = vec![0; 10000];
548548

549-
Snapshot::serialize(&mut buf.as_mut_slice(), &v).unwrap();
550-
let restored: Aarch64RegisterVec = Snapshot::deserialize(&mut buf.as_slice()).unwrap();
549+
Snapshot::new(&v).save(&mut buf.as_mut_slice()).unwrap();
550+
let restored: Aarch64RegisterVec = Snapshot::load(&mut buf.as_slice()).unwrap().data;
551551

552552
for (old, new) in v.iter().zip(restored.iter()) {
553553
assert_eq!(old, new);
@@ -572,11 +572,11 @@ mod tests {
572572

573573
let mut buf = vec![0; 10000];
574574

575-
Snapshot::serialize(&mut buf.as_mut_slice(), &v).unwrap();
575+
Snapshot::new(&v).save(&mut buf.as_mut_slice()).unwrap();
576576

577577
// Total size of registers according IDs are 16 + 16 = 32,
578578
// but actual data size is 8 + 16 = 24.
579-
Snapshot::deserialize::<_, Aarch64RegisterVec>(&mut buf.as_slice()).unwrap_err();
579+
Snapshot::<Aarch64RegisterVec>::load(&mut buf.as_slice()).unwrap_err();
580580
}
581581

582582
#[test]
@@ -595,10 +595,10 @@ mod tests {
595595

596596
let mut buf = vec![0; 10000];
597597

598-
Snapshot::serialize(&mut buf.as_mut_slice(), &v).unwrap();
598+
Snapshot::new(v).save(&mut buf.as_mut_slice()).unwrap();
599599

600600
// 4096 bit wide registers are not supported.
601-
Snapshot::deserialize::<_, Aarch64RegisterVec>(&mut buf.as_slice()).unwrap_err();
601+
Snapshot::<Aarch64RegisterVec>::load(&mut buf.as_slice()).unwrap_err();
602602
}
603603

604604
#[test]

src/vmm/src/arch/x86_64/vm.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -306,8 +306,10 @@ mod tests {
306306
let (_, mut vm) = setup_vm_with_memory(0x1000);
307307
vm.setup_irqchip().unwrap();
308308
let state = vm.save_state().unwrap();
309-
Snapshot::serialize(&mut snapshot_data.as_mut_slice(), &state).unwrap();
310-
let restored_state: VmState = Snapshot::deserialize(&mut snapshot_data.as_slice()).unwrap();
309+
Snapshot::new(state)
310+
.save(&mut snapshot_data.as_mut_slice())
311+
.unwrap();
312+
let restored_state: VmState = Snapshot::load(&mut snapshot_data.as_slice()).unwrap().data;
311313

312314
vm.restore_state(&restored_state).unwrap();
313315
}

src/vmm/src/device_manager/persist.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -768,7 +768,9 @@ mod tests {
768768
let entropy_config = EntropyDeviceConfig::default();
769769
insert_entropy_device(&mut vmm, &mut cmdline, &mut event_manager, entropy_config);
770770

771-
Snapshot::serialize(&mut buf.as_mut_slice(), &vmm.mmio_device_manager.save()).unwrap();
771+
Snapshot::new(vmm.mmio_device_manager.save())
772+
.save(&mut buf.as_mut_slice())
773+
.unwrap();
772774

773775
// We only want to keep the device map from the original MmioDeviceManager.
774776
vmm.mmio_device_manager.soft_clone()
@@ -777,7 +779,7 @@ mod tests {
777779

778780
let mut event_manager = EventManager::new().expect("Unable to create EventManager");
779781
let vmm = default_vmm();
780-
let device_states: DeviceStates = Snapshot::deserialize(&mut buf.as_slice()).unwrap();
782+
let device_states: DeviceStates = Snapshot::load(&mut buf.as_slice()).unwrap().data;
781783
let vm_resources = &mut VmResources::default();
782784
let restore_args = MMIODevManagerConstructorArgs {
783785
mem: vmm.vm.guest_memory(),

src/vmm/src/devices/virtio/balloon/persist.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,15 +194,17 @@ mod tests {
194194
// Create and save the balloon device.
195195
let balloon = Balloon::new(0x42, false, 2, false).unwrap();
196196

197-
Snapshot::serialize(&mut mem.as_mut_slice(), &balloon.save()).unwrap();
197+
Snapshot::new(balloon.save())
198+
.save(&mut mem.as_mut_slice())
199+
.unwrap();
198200

199201
// Deserialize and restore the balloon device.
200202
let restored_balloon = Balloon::restore(
201203
BalloonConstructorArgs {
202204
mem: guest_mem,
203205
restored_from_file: true,
204206
},
205-
&Snapshot::deserialize(&mut mem.as_slice()).unwrap(),
207+
&Snapshot::load(&mut mem.as_slice()).unwrap().data,
206208
)
207209
.unwrap();
208210

src/vmm/src/devices/virtio/block/virtio/persist.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,9 @@ mod tests {
186186
// Save the block device.
187187
let mut mem = vec![0; 4096];
188188

189-
Snapshot::serialize(&mut mem.as_mut_slice(), &block.save()).unwrap();
189+
Snapshot::new(block.save())
190+
.save(&mut mem.as_mut_slice())
191+
.unwrap();
190192
}
191193

192194
#[test]
@@ -229,12 +231,14 @@ mod tests {
229231
// Save the block device.
230232
let mut mem = vec![0; 4096];
231233

232-
Snapshot::serialize(&mut mem.as_mut_slice(), &block.save()).unwrap();
234+
Snapshot::new(block.save())
235+
.save(&mut mem.as_mut_slice())
236+
.unwrap();
233237

234238
// Restore the block device.
235239
let restored_block = VirtioBlock::restore(
236240
BlockConstructorArgs { mem: guest_mem },
237-
&Snapshot::deserialize(&mut mem.as_slice()).unwrap(),
241+
&Snapshot::load(&mut mem.as_slice()).unwrap().data,
238242
)
239243
.unwrap();
240244

src/vmm/src/devices/virtio/net/persist.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,9 @@ mod tests {
195195

196196
// Create and save the net device.
197197
{
198-
Snapshot::serialize(&mut mem.as_mut_slice(), &net.save()).unwrap();
198+
Snapshot::new(net.save())
199+
.save(&mut mem.as_mut_slice())
200+
.unwrap();
199201

200202
// Save some fields that we want to check later.
201203
id = net.id.clone();
@@ -215,7 +217,7 @@ mod tests {
215217
mem: guest_mem,
216218
mmds: mmds_ds,
217219
},
218-
&Snapshot::deserialize(&mut mem.as_slice()).unwrap(),
220+
&Snapshot::load(&mut mem.as_slice()).unwrap().data,
219221
) {
220222
Ok(restored_net) => {
221223
// Test that virtio specific fields are the same.

0 commit comments

Comments
 (0)