mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
vm-migration: add protocol versioning
Add protocol-side support for migration protocol versioning. Use the existing 6-byte Start command header, which was previously zero padding, to carry the sender's migration protocol version without changing the wire layout. Store the version as a little-endian u16 in the first two bytes and ignore the remaining four bytes. A zeroed command header continues to mean a legacy v0 sender. This keeps the message flow unchanged for rollout: Start is still followed by plain OK or Error (Aborted), and no new command is needed. On-behalf-of: SAP leander.kohler@sap.com Signed-off-by: Leander Kohler <leander.kohler@cyberus-technology.de>
This commit is contained in:
committed by
Rob Bradford
parent
181d29ee90
commit
b27faaaa45
@@ -73,8 +73,22 @@
|
||||
//! Source->>Destination: Complete
|
||||
//! Destination-->>Source: OK
|
||||
//! ```
|
||||
//!
|
||||
//! ## Protocol Versioning
|
||||
//!
|
||||
//! `Start` carries the sender's migration protocol version.
|
||||
//! A zeroed version field is treated as legacy protocol `v0`.
|
||||
//!
|
||||
//! The destination validates that version and replies with a plain `OK` or
|
||||
//! `Error`.
|
||||
//!
|
||||
//! Only the current and immediately previous protocol versions are
|
||||
//! supported. Compatibility is one-way, from older protocol versions
|
||||
//! to newer ones.
|
||||
|
||||
use std::io::{Read, Write};
|
||||
use std::mem::size_of;
|
||||
use std::ops::RangeInclusive;
|
||||
use std::{mem, slice};
|
||||
|
||||
use itertools::Itertools;
|
||||
@@ -127,18 +141,33 @@ pub enum Command {
|
||||
CompletePaused = 8,
|
||||
}
|
||||
|
||||
/// Newest migration protocol version sent by this implementation.
|
||||
pub const CURRENT_PROTOCOL_VERSION: u16 = 0;
|
||||
|
||||
/// Returns the current migration protocol version and the previous version, if any.
|
||||
pub fn supported_protocol_versions() -> RangeInclusive<u16> {
|
||||
CURRENT_PROTOCOL_VERSION.saturating_sub(1)..=CURRENT_PROTOCOL_VERSION
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, Copy, Clone)]
|
||||
pub struct Request {
|
||||
command: Command,
|
||||
padding: [u8; 6],
|
||||
length: u64, // Length of payload for command excluding the Request struct
|
||||
command_headers: [u8; 6],
|
||||
/// Length of payload for command excluding the Request struct
|
||||
length: u64,
|
||||
}
|
||||
|
||||
// SAFETY: Request contains a series of integers with no implicit padding
|
||||
unsafe impl ByteValued for Request {}
|
||||
|
||||
impl Request {
|
||||
fn encode_sender_version(version: u16) -> [u8; 6] {
|
||||
let mut command_headers = [0; 6];
|
||||
command_headers[..size_of::<u16>()].copy_from_slice(&version.to_le_bytes());
|
||||
command_headers
|
||||
}
|
||||
|
||||
pub fn new(command: Command, length: u64) -> Self {
|
||||
Self {
|
||||
command,
|
||||
@@ -148,7 +177,11 @@ impl Request {
|
||||
}
|
||||
|
||||
pub fn start() -> Self {
|
||||
Self::new(Command::Start, 0)
|
||||
Self {
|
||||
command: Command::Start,
|
||||
command_headers: Self::encode_sender_version(CURRENT_PROTOCOL_VERSION),
|
||||
length: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn state(length: u64) -> Self {
|
||||
@@ -189,6 +222,10 @@ impl Request {
|
||||
self.length
|
||||
}
|
||||
|
||||
pub fn command_headers(&self) -> &[u8; 6] {
|
||||
&self.command_headers
|
||||
}
|
||||
|
||||
pub fn read_from(fd: &mut dyn Read) -> Result<Request, MigratableError> {
|
||||
let mut request = Request::default();
|
||||
fd.read_exact(Self::as_mut_slice(&mut request))
|
||||
@@ -485,7 +522,21 @@ impl MemoryRangeTable {
|
||||
|
||||
#[cfg(test)]
|
||||
mod unit_tests {
|
||||
use crate::protocol::{MemoryRange, MemoryRangeTable};
|
||||
use crate::protocol::{Command, MemoryRange, MemoryRangeTable, Request};
|
||||
|
||||
#[test]
|
||||
fn test_start_request_ignores_residual_command_headers_bytes() {
|
||||
let request = Request {
|
||||
command: Command::Start,
|
||||
command_headers: [1, 0, 0xaa, 0xbb, 0xcc, 0xdd],
|
||||
length: 0,
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
u16::from_le_bytes([request.command_headers()[0], request.command_headers()[1]]),
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_memory_range_table_from_dirty_ranges_iter() {
|
||||
|
||||
Reference in New Issue
Block a user