Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/memory.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,9 @@ there will be fewer virtualisation related page faults. Unlike using
`hugepages=on` a specific number of huge pages do not need to be allocated by
the kernel.

A restore with `memory_restore_mode=copyonwrite` does not apply this option to
the restored memory.

By default this option is turned on.

_Example_
Expand Down
11 changes: 9 additions & 2 deletions docs/snapshot_restore.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,15 @@ Current constraints for `memory_restore_mode=copyonwrite`:
`id`+`size` zone is fine), resizable RAM (`hotplug_size`, virtio-mem), KSM
(`mergeable=on`), `--pvmemcontrol`, device passthrough
(`--device`/`--user-device`/`--vdpa`), and snapshot ranges that are not
page-aligned single-region extents. Each region's `reserve` policy and the
THP policy are re-applied to the mapped region.
page-aligned single-region extents. Each region's `reserve` policy is
re-applied to the mapped region. THP is not. A page the guest writes becomes
a private anonymous copy of one host base page (4 KiB on x86_64) either way.
On a filesystem with large folios, `MADV_HUGEPAGE` lets read faults map
PMD-sized file folios (2 MiB on x86_64), which speeds up random reads of
read-mostly guest memory (by about 3.5% in one measurement). But a read fault
that misses the page cache can then request readahead of two PMD-sized
folios, about 4 MiB, which made the restore about 1.5 times slower on an idle
host and about 5 times slower with 16 VMs restoring at once.
- A snapshot memory file shorter than the saved ranges is rejected up front (it
would otherwise fault `SIGBUS` at run time).
- `prefault` is rejected.
Expand Down
24 changes: 6 additions & 18 deletions vmm/src/memory_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1050,7 +1050,7 @@ impl MemoryManager {
"snapshot memory file is shorter than the saved ranges",
)));
}
do_mmap_cow_saved_regions(&guest_memory, &memory_file, saved_regions, self.thp)?;
do_mmap_cow_saved_regions(&guest_memory, &memory_file, saved_regions)?;
// Landlock applies after the restore, so the pagemap is opened here.
self.cow_source = match PageMap::open() {
Ok(pagemap) => Some(CowSource {
Expand Down Expand Up @@ -4157,13 +4157,11 @@ fn is_restore_cow_compatible(guest_memory: &GuestMemory, saved_regions: &MemoryR
}

// Maps each saved range over its guest RAM window, re-applying the owning
// region's reserve policy and the THP policy. Ranges must have passed
// `is_restore_cow_compatible`.
// region's reserve policy. Ranges must have passed `is_restore_cow_compatible`.
fn do_mmap_cow_saved_regions(
guest_memory: &GuestMemory,
memory_file: &File,
saved_regions: &MemoryRangeTable,
thp: bool,
) -> Result<(), Error> {
let mut file_offset: u64 = 0;
for range in saved_regions.ranges() {
Expand Down Expand Up @@ -4196,16 +4194,6 @@ fn do_mmap_cow_saved_regions(
if ret == libc::MAP_FAILED {
return Err(Error::SnapshotMmap(io::Error::last_os_error()));
}
if thp {
// SAFETY: ret/length name the private mapping just installed above.
let adv = unsafe { libc::madvise(ret, length, libc::MADV_HUGEPAGE) };
if adv != 0 {
warn!(
"Restore (mode=copyonwrite): MADV_HUGEPAGE failed: {}",
io::Error::last_os_error()
);
}
}
file_offset = file_offset.checked_add(range.length).ok_or_else(|| {
Error::SnapshotMmap(io::Error::other("snapshot range file offset overflow"))
})?;
Expand Down Expand Up @@ -4453,7 +4441,7 @@ mod tests {
});

assert!(is_restore_cow_compatible(&gm, &table));
do_mmap_cow_saved_regions(&gm, &file, &table, false).unwrap();
do_mmap_cow_saved_regions(&gm, &file, &table).unwrap();

assert_eq!(gm.read_obj::<u8>(GuestAddress(0)).unwrap(), 0xab);
assert_eq!(gm.read_obj::<u8>(GuestAddress(page - 1)).unwrap(), 0xab);
Expand Down Expand Up @@ -4481,7 +4469,7 @@ mod tests {
gpa: 0,
length: 16 * page,
});
do_mmap_cow_saved_regions(&gm, &file, &ranges, false).unwrap();
do_mmap_cow_saved_regions(&gm, &file, &ranges).unwrap();
let source = CowSource {
file,
ranges,
Expand Down Expand Up @@ -4551,7 +4539,7 @@ mod tests {
}

#[test]
fn cow_restore_honors_region_reserve_and_thp() {
fn cow_restore_honors_region_reserve() {
let page = get_page_size();
let mut file = tempfile::tempfile().unwrap();
file.write_all(&vec![0xcdu8; page as usize]).unwrap();
Expand All @@ -4573,7 +4561,7 @@ mod tests {
gpa: 0,
length: page,
});
do_mmap_cow_saved_regions(&gm, &file, &table, true).unwrap();
do_mmap_cow_saved_regions(&gm, &file, &table).unwrap();
assert_eq!(gm.read_obj::<u8>(GuestAddress(0)).unwrap(), 0xcd);
}
}
Expand Down
Loading