From 20ba271b6cdac5d4ee68cdf5c770391e4caebf29 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Tue, 25 Feb 2020 02:38:08 +0100 Subject: [PATCH] vmm: memory_manager: Implement the Transportable trait This implements the send() function of the Transportable trait, so that the guest memory regions can be saved into one file per region. This will need to be extended for live migration, as it will require other transport methods and the recv() function will need to be implemented too. Signed-off-by: Samuel Ortiz --- Cargo.lock | 53 +++++++++++++++++++++++++++++++ vmm/Cargo.toml | 1 + vmm/src/memory_manager.rs | 67 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 119 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index abdce585b..7cefee64c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -436,6 +436,17 @@ dependencies = [ "libc", ] +[[package]] +name = "idna" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "ipnetwork" version = "0.15.1" @@ -554,6 +565,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" + [[package]] name = "memchr" version = "2.3.3" @@ -649,6 +666,12 @@ dependencies = [ "vm-memory", ] +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + [[package]] name = "pin-utils" version = "0.1.0-alpha.4" @@ -1211,6 +1234,24 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +dependencies = [ + "matches", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" +dependencies = [ + "smallvec", +] + [[package]] name = "unicode-width" version = "0.1.7" @@ -1229,6 +1270,17 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +[[package]] +name = "url" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" +dependencies = [ + "idna", + "matches", + "percent-encoding", +] + [[package]] name = "utf8-ranges" version = "1.0.4" @@ -1448,6 +1500,7 @@ dependencies = [ "serde_json", "signal-hook", "tempfile", + "url", "vfio", "vm-allocator", "vm-device", diff --git a/vmm/Cargo.toml b/vmm/Cargo.toml index b77d3e610..56c4e7117 100644 --- a/vmm/Cargo.toml +++ b/vmm/Cargo.toml @@ -32,6 +32,7 @@ seccomp = { git = "https://github.com/firecracker-microvm/firecracker", tag = "v serde = {version = ">=1.0.27", features = ["rc"] } serde_derive = ">=1.0.27" serde_json = ">=1.0.9" +url = "2.1.1" vfio = { path = "../vfio", optional = true } vm-allocator = { path = "../vm-allocator" } vm-device = { path = "../vm-device" } diff --git a/vmm/src/memory_manager.rs b/vmm/src/memory_manager.rs index ace1c798b..58a7fe6a0 100644 --- a/vmm/src/memory_manager.rs +++ b/vmm/src/memory_manager.rs @@ -17,10 +17,11 @@ use std::io; use std::os::unix::io::FromRawFd; use std::path::PathBuf; use std::sync::{Arc, Mutex}; +use url::Url; use vm_allocator::{GsiApic, SystemAllocator}; use vm_memory::guest_memory::FileOffset; use vm_memory::{ - mmap::MmapRegionError, Address, Error as MmapError, GuestAddress, GuestAddressSpace, + mmap::MmapRegionError, Address, Bytes, Error as MmapError, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryAtomic, GuestMemoryLoadGuard, GuestMemoryMmap, GuestMemoryRegion, GuestRegionMmap, GuestUsize, MmapRegion, }; @@ -971,5 +972,67 @@ impl Snapshottable for MemoryManager { } } -impl Transportable for MemoryManager {} +impl Transportable for MemoryManager { + fn send( + &self, + _snapshot: &Snapshot, + destination_url: &str, + ) -> std::result::Result<(), MigratableError> { + let url = Url::parse(destination_url).map_err(|e| { + MigratableError::MigrateSend(anyhow!("Could not parse destination URL: {}", e)) + })?; + + match url.scheme() { + "file" => { + let vm_memory_snapshot_path = url + .to_file_path() + .map_err(|_| { + MigratableError::MigrateSend(anyhow!( + "Could not convert file URL to a file path" + )) + }) + .and_then(|path| { + if !path.is_dir() { + return Err(MigratableError::MigrateSend(anyhow!( + "Destination is not a directory" + ))); + } + Ok(path) + })?; + + if let Some(guest_memory) = &*self.snapshot.lock().unwrap() { + guest_memory.with_regions_mut(|index, region| { + let mut memory_region_path = vm_memory_snapshot_path.clone(); + memory_region_path.push(format!("memory-region-{}", index)); + + // Create the snapshot file for the region + let mut memory_region_file = OpenOptions::new() + .read(true) + .write(true) + .create_new(true) + .open(memory_region_path) + .map_err(|e| MigratableError::MigrateSend(e.into()))?; + + guest_memory + .write_to( + region.start_addr(), + &mut memory_region_file, + region.len().try_into().unwrap(), + ) + .map_err(|e| MigratableError::MigrateSend(e.into()))?; + + Ok(()) + })?; + } + } + _ => { + return Err(MigratableError::MigrateSend(anyhow!( + "Unsupported VM transport URL scheme: {}", + url.scheme() + ))) + } + } + Ok(()) + } +} impl Migratable for MemoryManager {}