vm-migration: Add versioned versions of snapshot & state API

This will allow progressive porting of parts of the VMM to using
versioned state. A new trait VersionMapped is introduced with a default
implementation that returns an empty VersionMap to ease implementation.

If a struct is updated the the trait will need to be fully implemented
with VersionMapped::version_map() returning a new VersionMap handling
the change.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
This commit is contained in:
Rob Bradford
2021-05-05 15:16:19 +00:00
parent 2eeeaf5753
commit bbda08717f
3 changed files with 112 additions and 0 deletions

View File

@@ -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<T>(&self) -> Result<T, MigratableError>
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<T>(id: &str, state: &T) -> Result<Self, MigratableError>
where
@@ -92,6 +117,24 @@ impl SnapshotDataSection {
Ok(snapshot_data)
}
/// Create from versioned state
pub fn new_from_versioned_state<T>(id: &str, state: &T) -> Result<Self, MigratableError>
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<T>(id: &str, state: &T) -> Result<Self, MigratableError>
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<T>(&self, id: &str) -> Result<T, MigratableError>
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.