diff --git a/Cargo.lock b/Cargo.lock index b10909c26..f9ff2c9b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -108,6 +108,15 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bitflags" version = "0.5.0" @@ -215,6 +224,12 @@ dependencies = [ "bitflags 1.2.1", ] +[[package]] +name = "crc64" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55626594feae15d266d52440b26ff77de0e22230cf0c113abe619084c1ddc910" + [[package]] name = "credibility" version = "0.1.3" @@ -1128,6 +1143,34 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +[[package]] +name = "versionize" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7429cf68de8f091b667d27323ed323afd39584a56d533995b12ddd748e5e6ca9" +dependencies = [ + "bincode", + "crc64", + "proc-macro2", + "quote", + "serde", + "serde_derive", + "syn", + "versionize_derive", + "vmm-sys-util", +] + +[[package]] +name = "versionize_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "140aa9fd298f667ea50fa1cb0d8530076924079285c623b18b8f8a1c28386b4a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "vfio-bindings" version = "0.2.0" @@ -1295,6 +1338,8 @@ dependencies = [ "serde_derive", "serde_json", "thiserror", + "versionize", + "versionize_derive", "vm-memory", ] diff --git a/vm-migration/Cargo.toml b/vm-migration/Cargo.toml index 8ba5a6013..a620c0731 100644 --- a/vm-migration/Cargo.toml +++ b/vm-migration/Cargo.toml @@ -10,4 +10,6 @@ thiserror = "1.0" serde = {version = ">=1.0.27", features = ["rc"] } serde_derive = ">=1.0.27" serde_json = ">=1.0.9" +versionize = "0.1.6" +versionize_derive = "0.1.4" vm-memory = { version = "0.5.0", features = ["backend-mmap", "backend-atomic"] } diff --git a/vm-migration/src/lib.rs b/vm-migration/src/lib.rs index 4fc39866e..b233df2c4 100644 --- a/vm-migration/src/lib.rs +++ b/vm-migration/src/lib.rs @@ -12,9 +12,21 @@ extern crate vm_memory; use anyhow::anyhow; use serde::{Deserialize, Serialize}; use thiserror::Error; +use versionize::{VersionMap, Versionize}; pub mod protocol; +/// Global VMM version for versioning +const MAJOR_VERSION: u16 = 15; +const MINOR_VERSION: u16 = 0; +const VMM_VERSION: u16 = MAJOR_VERSION << 12 | MINOR_VERSION & 0b1111; + +pub trait VersionMapped { + fn version_map() -> VersionMap { + VersionMap::new() + } +} + #[derive(Error, Debug)] pub enum MigratableError { #[error("Failed to pause migratable component: {0}")] @@ -77,6 +89,19 @@ impl SnapshotDataSection { }) } + /// Generate versioned state + pub fn to_versioned_state(&self) -> Result + where + T: Versionize + VersionMapped, + { + T::deserialize( + &mut self.snapshot.as_slice(), + &T::version_map(), + VMM_VERSION, + ) + .map_err(|e| MigratableError::Restore(anyhow!("Error deserialising: {} {}", self.id, e))) + } + /// Create from state that can be serialized pub fn new_from_state(id: &str, state: &T) -> Result where @@ -92,6 +117,24 @@ impl SnapshotDataSection { Ok(snapshot_data) } + + /// Create from versioned state + pub fn new_from_versioned_state(id: &str, state: &T) -> Result + where + T: Versionize + VersionMapped, + { + let mut snapshot = Vec::new(); + state + .serialize(&mut snapshot, &T::version_map(), VMM_VERSION) + .map_err(|e| MigratableError::Snapshot(anyhow!("Error serialising: {} {}", id, e)))?; + + let snapshot_data = SnapshotDataSection { + id: format!("{}-section", id), + snapshot, + }; + + Ok(snapshot_data) + } } /// A Snapshottable component's snapshot is a tree of snapshots, where leafs @@ -138,6 +181,17 @@ impl Snapshot { Ok(snapshot_data) } + /// Create from versioned state + pub fn new_from_versioned_state(id: &str, state: &T) -> Result + where + T: Versionize + VersionMapped, + { + let mut snapshot_data = Snapshot::new(id); + snapshot_data.add_data_section(SnapshotDataSection::new_from_versioned_state(id, state)?); + + Ok(snapshot_data) + } + /// Add a sub-component's Snapshot to the Snapshot. pub fn add_snapshot(&mut self, snapshot: Snapshot) { self.snapshots @@ -159,6 +213,17 @@ impl Snapshot { .ok_or_else(|| MigratableError::Restore(anyhow!("Missing section for {}", id)))? .to_state() } + + /// Generate versioned state + pub fn to_versioned_state(&self, id: &str) -> Result + where + T: Versionize + VersionMapped, + { + self.snapshot_data + .get(&format!("{}-section", id)) + .ok_or_else(|| MigratableError::Restore(anyhow!("Missing section for {}", id)))? + .to_versioned_state() + } } /// A snapshottable component can be snapshotted.