vmm: Add support for sending and receiving migration if VM is paused

This is tested by:

Source VMM:

target/debug/cloud-hypervisor --kernel ~/src/linux/vmlinux \
--pmem file=~/workloads/focal.raw --cpus boot=1 \
--memory size=2048M \
--cmdline"root=/dev/pmem0p1 console=ttyS0" --serial tty --console off \
--api-socket=/tmp/api1 -v

Destination VMM:

target/debug/cloud-hypervisor --api-socket=/tmp/api2 -v

And the following commands:

target/debug/ch-remote --api-socket=/tmp/api1 pause
target/debug/ch-remote --api-socket=/tmp/api2 receive-migration unix:/tmp/foo &
target/debug/ch-remote --api-socket=/tmp/api1 send-migration unix:/tmp/foo
target/debug/ch-remote --api-socket=/tmp/api2 resume

The VM is then responsive on the destination VMM.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
This commit is contained in:
Rob Bradford
2020-11-04 15:00:23 +00:00
committed by Samuel Ortiz
parent dfe2dadb3e
commit ca60adda70
2 changed files with 315 additions and 7 deletions

View File

@@ -58,7 +58,7 @@ use std::ffi::CString;
#[cfg(target_arch = "x86_64")]
use std::fmt;
use std::fs::{File, OpenOptions};
use std::io::{self, Write};
use std::io::{self, Read, Write};
use std::io::{Seek, SeekFrom};
use std::num::Wrapping;
use std::ops::Deref;
@@ -67,10 +67,11 @@ use std::{result, str, thread};
use url::Url;
use vm_device::Bus;
use vm_memory::{
Address, Bytes, GuestAddress, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryMmap,
GuestRegionMmap,
Address, Bytes, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryAtomic,
GuestMemoryMmap, GuestMemoryRegion, GuestRegionMmap,
};
use vm_migration::{
protocol::{MemoryRange, MemoryRangeTable},
Migratable, MigratableError, Pausable, Snapshot, SnapshotDataSection, Snapshottable,
Transportable,
};
@@ -1607,6 +1608,68 @@ impl Vm {
pub fn balloon_size(&self) -> u64 {
self.device_manager.lock().unwrap().balloon_size()
}
pub fn receive_memory_regions<F>(
&mut self,
ranges: &MemoryRangeTable,
fd: &mut F,
) -> std::result::Result<(), MigratableError>
where
F: Read,
{
let guest_memory = self.memory_manager.lock().as_ref().unwrap().guest_memory();
let mem = guest_memory.memory();
for range in ranges.regions() {
mem.read_exact_from(GuestAddress(range.gpa), fd, range.length as usize)
.map_err(|e| {
MigratableError::MigrateReceive(anyhow!(
"Error transferring memory to socket: {}",
e
))
})?;
}
Ok(())
}
pub fn send_memory_regions<F>(
&mut self,
ranges: &MemoryRangeTable,
fd: &mut F,
) -> std::result::Result<(), MigratableError>
where
F: Write,
{
let guest_memory = self.memory_manager.lock().as_ref().unwrap().guest_memory();
let mem = guest_memory.memory();
for range in ranges.regions() {
mem.write_all_to(GuestAddress(range.gpa), fd, range.length as usize)
.map_err(|e| {
MigratableError::MigrateSend(anyhow!(
"Error transferring memory to socket: {}",
e
))
})?;
}
Ok(())
}
pub fn memory_range_table(&self) -> std::result::Result<MemoryRangeTable, MigratableError> {
let mut table = MemoryRangeTable::default();
let guest_memory = self.memory_manager.lock().as_ref().unwrap().guest_memory();
guest_memory.memory().with_regions_mut(|_, region| {
table.push(MemoryRange {
gpa: region.start_addr().raw_value(),
length: region.len() as u64,
});
Ok(())
})?;
Ok(table)
}
}
impl Pausable for Vm {