Skip to content

Commit 12fa176

Browse files
committed
refactor: move snapshot_memory_to_file into struct Vm
struct Vm encapsulates the memory, so having this function live there makes sense. It also prepares us for the future where multiple GuestMemoryMmap objects need to be managed. Signed-off-by: Patrick Roy <[email protected]>
1 parent e026e54 commit 12fa176

File tree

2 files changed

+86
-83
lines changed

2 files changed

+86
-83
lines changed

src/vmm/src/persist.rs

Lines changed: 5 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,13 @@ use crate::utils::u64_to_usize;
3434
use crate::vmm_config::boot_source::BootSourceConfig;
3535
use crate::vmm_config::instance_info::InstanceInfo;
3636
use crate::vmm_config::machine_config::{HugePageConfig, MachineConfigError, MachineConfigUpdate};
37-
use crate::vmm_config::snapshot::{
38-
CreateSnapshotParams, LoadSnapshotParams, MemBackendType, SnapshotType,
39-
};
37+
use crate::vmm_config::snapshot::{CreateSnapshotParams, LoadSnapshotParams, MemBackendType};
4038
use crate::vstate::kvm::KvmState;
4139
use crate::vstate::memory;
42-
use crate::vstate::memory::{GuestMemoryExtension, GuestMemoryState, GuestRegionMmap, MemoryError};
40+
use crate::vstate::memory::{GuestMemoryState, GuestRegionMmap, MemoryError};
4341
use crate::vstate::vcpu::{VcpuSendEventError, VcpuState};
4442
use crate::vstate::vm::VmState;
45-
use crate::{EventManager, Vmm, mem_size_mib, vstate};
43+
use crate::{EventManager, Vmm, vstate};
4644

4745
/// Holds information related to the VM that is not part of VmState.
4846
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
@@ -164,7 +162,8 @@ pub fn create_snapshot(
164162

165163
snapshot_state_to_file(&microvm_state, &params.snapshot_path)?;
166164

167-
snapshot_memory_to_file(vmm, &params.mem_file_path, params.snapshot_type)?;
165+
vmm.vm
166+
.snapshot_memory_to_file(&params.mem_file_path, params.snapshot_type)?;
168167

169168
// We need to mark queues as dirty again for all activated devices. The reason we
170169
// do it here is because we don't mark pages as dirty during runtime
@@ -210,81 +209,6 @@ fn snapshot_state_to_file(
210209
.map_err(|err| SnapshotBackingFile("sync_all", err))
211210
}
212211

213-
/// Takes a snapshot of the virtual machine running inside the given [`Vmm`] and saves it to
214-
/// `mem_file_path`.
215-
///
216-
/// If `snapshot_type` is [`SnapshotType::Diff`], and `mem_file_path` exists and is a snapshot file
217-
/// of matching size, then the diff snapshot will be directly merged into the existing snapshot.
218-
/// Otherwise, existing files are simply overwritten.
219-
fn snapshot_memory_to_file(
220-
vmm: &Vmm,
221-
mem_file_path: &Path,
222-
snapshot_type: SnapshotType,
223-
) -> Result<(), CreateSnapshotError> {
224-
use self::CreateSnapshotError::*;
225-
226-
// Need to check this here, as we create the file in the line below
227-
let file_existed = mem_file_path.exists();
228-
229-
let mut file = OpenOptions::new()
230-
.write(true)
231-
.create(true)
232-
.truncate(false)
233-
.open(mem_file_path)
234-
.map_err(|err| MemoryBackingFile("open", err))?;
235-
236-
// Determine what size our total memory area is.
237-
let mem_size_mib = mem_size_mib(vmm.vm.guest_memory());
238-
let expected_size = mem_size_mib * 1024 * 1024;
239-
240-
if file_existed {
241-
let file_size = file
242-
.metadata()
243-
.map_err(|e| MemoryBackingFile("get_metadata", e))?
244-
.len();
245-
246-
// Here we only truncate the file if the size mismatches.
247-
// - For full snapshots, the entire file's contents will be overwritten anyway. We have to
248-
// avoid truncating here to deal with the edge case where it represents the snapshot file
249-
// from which this very microVM was loaded (as modifying the memory file would be
250-
// reflected in the mmap of the file, meaning a truncate operation would zero out guest
251-
// memory, and thus corrupt the VM).
252-
// - For diff snapshots, we want to merge the diff layer directly into the file.
253-
if file_size != expected_size {
254-
file.set_len(0)
255-
.map_err(|err| MemoryBackingFile("truncate", err))?;
256-
}
257-
}
258-
259-
// Set the length of the file to the full size of the memory area.
260-
file.set_len(expected_size)
261-
.map_err(|e| MemoryBackingFile("set_length", e))?;
262-
263-
match snapshot_type {
264-
SnapshotType::Diff => {
265-
let dirty_bitmap = vmm.vm.get_dirty_bitmap().map_err(DirtyBitmap)?;
266-
vmm.vm
267-
.guest_memory()
268-
.dump_dirty(&mut file, &dirty_bitmap)
269-
.map_err(Memory)
270-
}
271-
SnapshotType::Full => {
272-
let dump_res = vmm.vm.guest_memory().dump(&mut file).map_err(Memory);
273-
if dump_res.is_ok() {
274-
vmm.vm.reset_dirty_bitmap();
275-
vmm.vm.guest_memory().reset_dirty();
276-
}
277-
278-
dump_res
279-
}
280-
}?;
281-
282-
file.flush()
283-
.map_err(|err| MemoryBackingFile("flush", err))?;
284-
file.sync_all()
285-
.map_err(|err| MemoryBackingFile("sync_all", err))
286-
}
287-
288212
/// Validates that snapshot CPU vendor matches the host CPU vendor.
289213
///
290214
/// # Errors

src/vmm/src/vstate/vm.rs

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
// found in the THIRD-PARTY file.
77

88
use std::collections::HashMap;
9+
use std::fs::OpenOptions;
10+
use std::io::Write;
11+
use std::path::Path;
912
use std::sync::Arc;
1013

1114
use kvm_bindings::{KVM_MEM_LOG_DIRTY_PAGES, kvm_userspace_memory_region};
@@ -14,12 +17,14 @@ use vmm_sys_util::eventfd::EventFd;
1417

1518
pub use crate::arch::{ArchVm as Vm, ArchVmError, VmState};
1619
use crate::logger::info;
20+
use crate::persist::CreateSnapshotError;
1721
use crate::utils::u64_to_usize;
22+
use crate::vmm_config::snapshot::SnapshotType;
1823
use crate::vstate::memory::{
19-
Address, GuestMemory, GuestMemoryMmap, GuestMemoryRegion, GuestRegionMmap,
24+
Address, GuestMemory, GuestMemoryExtension, GuestMemoryMmap, GuestMemoryRegion, GuestRegionMmap,
2025
};
2126
use crate::vstate::vcpu::VcpuError;
22-
use crate::{DirtyBitmap, Vcpu};
27+
use crate::{DirtyBitmap, Vcpu, mem_size_mib};
2328

2429
/// Architecture independent parts of a VM.
2530
#[derive(Debug)]
@@ -203,6 +208,80 @@ impl Vm {
203208
})?;
204209
Ok(bitmap)
205210
}
211+
212+
/// Takes a snapshot of the virtual machine running inside the given [`Vmm`] and saves it to
213+
/// `mem_file_path`.
214+
///
215+
/// If `snapshot_type` is [`SnapshotType::Diff`], and `mem_file_path` exists and is a snapshot
216+
/// file of matching size, then the diff snapshot will be directly merged into the existing
217+
/// snapshot. Otherwise, existing files are simply overwritten.
218+
pub(crate) fn snapshot_memory_to_file(
219+
&self,
220+
mem_file_path: &Path,
221+
snapshot_type: SnapshotType,
222+
) -> Result<(), CreateSnapshotError> {
223+
use self::CreateSnapshotError::*;
224+
225+
// Need to check this here, as we create the file in the line below
226+
let file_existed = mem_file_path.exists();
227+
228+
let mut file = OpenOptions::new()
229+
.write(true)
230+
.create(true)
231+
.truncate(false)
232+
.open(mem_file_path)
233+
.map_err(|err| MemoryBackingFile("open", err))?;
234+
235+
// Determine what size our total memory area is.
236+
let mem_size_mib = mem_size_mib(self.guest_memory());
237+
let expected_size = mem_size_mib * 1024 * 1024;
238+
239+
if file_existed {
240+
let file_size = file
241+
.metadata()
242+
.map_err(|e| MemoryBackingFile("get_metadata", e))?
243+
.len();
244+
245+
// Here we only truncate the file if the size mismatches.
246+
// - For full snapshots, the entire file's contents will be overwritten anyway. We have
247+
// to avoid truncating here to deal with the edge case where it represents the
248+
// snapshot file from which this very microVM was loaded (as modifying the memory file
249+
// would be reflected in the mmap of the file, meaning a truncate operation would zero
250+
// out guest memory, and thus corrupt the VM).
251+
// - For diff snapshots, we want to merge the diff layer directly into the file.
252+
if file_size != expected_size {
253+
file.set_len(0)
254+
.map_err(|err| MemoryBackingFile("truncate", err))?;
255+
}
256+
}
257+
258+
// Set the length of the file to the full size of the memory area.
259+
file.set_len(expected_size)
260+
.map_err(|e| MemoryBackingFile("set_length", e))?;
261+
262+
match snapshot_type {
263+
SnapshotType::Diff => {
264+
let dirty_bitmap = self.get_dirty_bitmap().map_err(DirtyBitmap)?;
265+
self.guest_memory()
266+
.dump_dirty(&mut file, &dirty_bitmap)
267+
.map_err(Memory)
268+
}
269+
SnapshotType::Full => {
270+
let dump_res = self.guest_memory().dump(&mut file).map_err(Memory);
271+
if dump_res.is_ok() {
272+
self.reset_dirty_bitmap();
273+
self.guest_memory().reset_dirty();
274+
}
275+
276+
dump_res
277+
}
278+
}?;
279+
280+
file.flush()
281+
.map_err(|err| MemoryBackingFile("flush", err))?;
282+
file.sync_all()
283+
.map_err(|err| MemoryBackingFile("sync_all", err))
284+
}
206285
}
207286

208287
#[cfg(test)]

0 commit comments

Comments
 (0)