Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion doc/design/5.5-restore-protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,11 @@ port access, as every byte of the packet does. The flag was a reserved bit,
which a guest that predates it rejects, so such a guest fails a restore that
hands it a record rather than ignore the record, and restores without a record
are unchanged. Such a restore always sets `ACK_REQUIRED`, so the guest has read
the record before host input reaches it.
the record before host input reaches it. NVX supplies a record at every launch
of a platform clone, whose guest activates from it as soon as it runs, without
waiting for the host, which only asks for the clone's Ready on the control
channel; a template key's activation contract refuses a template whose guest
takes its record otherwise.

## 5.5.6 Generations

Expand Down
181 changes: 101 additions & 80 deletions nvx-rs/README.md

Large diffs are not rendered by default.

22 changes: 8 additions & 14 deletions nvx-rs/public-api/linux.txt
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ method nvx::activation::ActivationRecord::encode
method nvx::activation::ActivationRecord::new
method nvx::activation::ActivationRecord::restore
method nvx::activation::ActivationRecord::version
const nvx::activation::CONTRACT_VERSION
struct nvx::activation::Configuration
method nvx::activation::Configuration::check
field nvx::activation::Configuration::export
Expand Down Expand Up @@ -614,6 +615,7 @@ variant nvx::guest::cgroup::Scope::Execution
variant nvx::guest::cgroup::Scope::Workload
mod nvx::guest::clone
struct nvx::guest::clone::Activation
method nvx::guest::clone::Activation::accept
method nvx::guest::clone::Activation::complete
method nvx::guest::clone::Activation::deadline
method nvx::guest::clone::Activation::established
Expand All @@ -625,48 +627,39 @@ method nvx::guest::clone::Activation::tick
method nvx::guest::clone::Activation::with_resources
enum nvx::guest::clone::ActivationAction
variant nvx::guest::clone::ActivationAction::Acknowledge
variant nvx::guest::clone::ActivationAction::Apply
field nvx::guest::clone::ActivationAction::Apply::0
variant nvx::guest::clone::ActivationAction::Continue
variant nvx::guest::clone::ActivationAction::Join
variant nvx::guest::clone::ActivationAction::Ready
field nvx::guest::clone::ActivationAction::Ready::0
variant nvx::guest::clone::ActivationAction::Serve
enum nvx::guest::clone::ActivationCause
variant nvx::guest::clone::ActivationCause::Changed
variant nvx::guest::clone::ActivationCause::Cold
variant nvx::guest::clone::ActivationCause::Completion
variant nvx::guest::clone::ActivationCause::Expired
variant nvx::guest::clone::ActivationCause::Failed
field nvx::guest::clone::ActivationCause::Failed::0
variant nvx::guest::clone::ActivationCause::Missing
variant nvx::guest::clone::ActivationCause::Record
field nvx::guest::clone::ActivationCause::Record::0
variant nvx::guest::clone::ActivationCause::Request
variant nvx::guest::clone::ActivationCause::Reset
variant nvx::guest::clone::ActivationCause::Resources
field nvx::guest::clone::ActivationCause::Resources::actual
field nvx::guest::clone::ActivationCause::Resources::expected
variant nvx::guest::clone::ActivationCause::Start
variant nvx::guest::clone::ActivationCause::Transaction
field nvx::guest::clone::ActivationCause::Transaction::accepted
field nvx::guest::clone::ActivationCause::Transaction::offered
struct nvx::guest::clone::ActivationFailure
method nvx::guest::clone::ActivationFailure::cause
method nvx::guest::clone::ActivationFailure::state
enum nvx::guest::clone::ActivationInput
variant nvx::guest::clone::ActivationInput::Activate
field nvx::guest::clone::ActivationInput::Activate::0
variant nvx::guest::clone::ActivationInput::Await
variant nvx::guest::clone::ActivationInput::Flow
variant nvx::guest::clone::ActivationInput::Request
variant nvx::guest::clone::ActivationInput::Reset
variant nvx::guest::clone::ActivationInput::TooLong
field nvx::guest::clone::ActivationInput::TooLong::0
enum nvx::guest::clone::ActivationState
variant nvx::guest::clone::ActivationState::Activating
variant nvx::guest::clone::ActivationState::Bootstrap
variant nvx::guest::clone::ActivationState::Applied
variant nvx::guest::clone::ActivationState::Failed
variant nvx::guest::clone::ActivationState::Pending
variant nvx::guest::clone::ActivationState::Ready
variant nvx::guest::clone::ActivationState::Restricted
variant nvx::guest::clone::ActivationState::Started
trait nvx::guest::clone::AdmissionFence
method nvx::guest::clone::AdmissionFence::close
Expand Down Expand Up @@ -814,6 +807,7 @@ variant nvx::guest::clone::RepairStage::Reseed
variant nvx::guest::clone::RepairStage::Restore
struct nvx::guest::clone::Repaired
method nvx::guest::clone::Repaired::acknowledged_at
method nvx::guest::clone::Repaired::activation_record
method nvx::guest::clone::Repaired::cpus
method nvx::guest::clone::Repaired::generation_id
method nvx::guest::clone::Repaired::memory
Expand Down Expand Up @@ -2695,10 +2689,10 @@ method nvx::host::template::ClassIdentity::from_bytes
trait nvx::host::template::CloneControl
type nvx::host::template::CloneControl::Error
type nvx::host::template::CloneControl::Session
method nvx::host::template::CloneControl::activate
method nvx::host::template::CloneControl::attach
method nvx::host::template::CloneControl::lost
method nvx::host::template::CloneControl::map
method nvx::host::template::CloneControl::ready
method nvx::host::template::CloneControl::refused
method nvx::host::template::CloneControl::start
struct nvx::host::template::CloneFailure
Expand Down
3 changes: 2 additions & 1 deletion nvx-rs/public-api/windows.txt
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ method nvx::activation::ActivationRecord::encode
method nvx::activation::ActivationRecord::new
method nvx::activation::ActivationRecord::restore
method nvx::activation::ActivationRecord::version
const nvx::activation::CONTRACT_VERSION
struct nvx::activation::Configuration
method nvx::activation::Configuration::check
field nvx::activation::Configuration::export
Expand Down Expand Up @@ -1631,10 +1632,10 @@ method nvx::host::template::ClassIdentity::from_bytes
trait nvx::host::template::CloneControl
type nvx::host::template::CloneControl::Error
type nvx::host::template::CloneControl::Session
method nvx::host::template::CloneControl::activate
method nvx::host::template::CloneControl::attach
method nvx::host::template::CloneControl::lost
method nvx::host::template::CloneControl::map
method nvx::host::template::CloneControl::ready
method nvx::host::template::CloneControl::refused
method nvx::host::template::CloneControl::start
struct nvx::host::template::CloneFailure
Expand Down
16 changes: 14 additions & 2 deletions nvx-rs/src/activation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,22 @@ mod tests;

pub use self::codec::Field;

/// The version of the activation contract that this build speaks: the schema of an
/// [`ActivationRecord`], which every record names. A record of another version is refused.
/// The version of the schema of an [`ActivationRecord`] that this build speaks, which every
/// record names. A record of another version is refused.
pub const VERSION: u16 = 1;

/// The version of the activation contract that this build speaks, which the compatibility key of
/// a template holds: the schema of an [`ActivationRecord`], at [`VERSION`], and how a clone of
/// the template receives it, so that a template whose guest takes its record another way restores
/// no clone of this build.
///
/// Version 1 sent the record in the adapter's Activate, on the clone's control session, once the
/// host attached. Version 2 hands it to the clone's guest at the end of its restore packet, which
/// OpenVMM seals before the first restored vCPU runs, from `--restore-activation-record`, and the
/// host asks for the clone's Ready instead: a guest of version 1, which predates the packet's
/// activation record, refuses a packet that carries one, or awaits an Activate that never comes.
pub const CONTRACT_VERSION: u64 = 2;

/// The most bytes of a record's encoding, so that a restore packet could carry the same bytes.
pub const MAX_RECORD_BYTES: usize = 1024;

Expand Down
5 changes: 3 additions & 2 deletions nvx-rs/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@
//! Each of these four must match the pinned OpenVMM's, which the ignored test
//! `tests/openvmm_vectors.rs` checks in the jobs that check out the OpenVMM submodule. The key
//! also holds the version of the activation contract,
//! [`activation::VERSION`](crate::activation::VERSION), through which each clone receives the
//! settings of its own launch.
//! [`activation::CONTRACT_VERSION`](crate::activation::CONTRACT_VERSION): the schema of the
//! activation record through which each clone receives the settings of its own launch, and how it
//! receives it, at the end of its restore packet.
//!
//! The build script includes this file to check bundled artifacts, so it must not depend on the
//! rest of the crate.
Expand Down
100 changes: 57 additions & 43 deletions nvx-rs/src/guest/clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,16 @@
//! reports them as violations of the time ABI, which stop the VM, with the time ABI's status,
//! unless the ABI is report-only, since the daemon enforces it.
//!
//! It returns the [`Repaired`] clone: its new VM generation ID, its number of online CPUs, the
//! size of its online memory blocks, and when it acknowledged the restore, which an activation
//! checks against the resources that it expects. The repair records how far it got, so that it
//! never brings a CPU online twice nor adds a memory block twice, and runs the reseed and the
//! finish at most once, whoever retries it: a failure at or after the reseed is final. It never
//! touches the fence. A failure is a [`RepairError`], which names the [`RepairStage`] that
//! failed, its [`RepairCause`], and the repair's [`RepairProgress`], and selects the status with
//! which the caller ends the VM, [`RepairError::status`], which `nvx::protocol::status`
//! documents.
//! It returns the [`Repaired`] clone: its new VM generation ID, its number of online CPUs and the
//! size of its online memory blocks, which an activation checks against the resources that it
//! expects, when it acknowledged the restore, and the activation record that its restore packet
//! carried, from which the activation takes the clone's configuration. The repair records how far
//! it got, so that it never brings a CPU online twice nor adds a memory block twice, and runs the
//! reseed and the finish at most once, whoever retries it: a failure at or after the reseed is
//! final. It never touches the fence. A failure is a [`RepairError`], which names the
//! [`RepairStage`] that failed, its [`RepairCause`], and the repair's [`RepairProgress`], and
//! selects the status with which the caller ends the VM, [`RepairError::status`], which
//! `nvx::protocol::status` documents.
//!
//! # Strict mode
//!
Expand All @@ -106,44 +107,54 @@
//!
//! # Activation
//!
//! A repaired clone then takes its own configuration from the host, in one Activate, on a
//! restricted control session, through an [`Activation`] of the [`Repaired`] clone and of the
//! caller's fence, which stays closed until the activation is ready, so that no execution starts
//! before. The activation is a state machine without I/O, independent of any framing: the adapter
//! passes it what it receives on the clone's NVXS session, each an [`ActivationInput`], and does
//! what each [`ActivationAction`] that it returns says. Its [`ActivationState`]s, in order, are:
//! A repaired clone then takes its own configuration from the activation record that its restore
//! packet carried, [`Repaired::activation_record`], through an [`Activation`] of the
//! [`Repaired`] clone and of the caller's fence, which stays closed until the activation is
//! ready, so that no execution starts before. The host supplied the record at the clone's launch,
//! and OpenVMM sealed it in the packet before the first restored vCPU ran, so the clone takes
//! nothing of the host to activate. The activation is a state machine without I/O, independent of
//! any framing: the adapter applies the record that the activation accepts, passes the activation
//! what it receives on the clone's NVXS session, each an [`ActivationInput`], and does what each
//! [`ActivationAction`] that it returns says. Its [`ActivationState`]s, in order, are:
//!
//! 1. the transport bootstrap: the clone awaits the reset that the restore queued for the session
//! that the guest attached before the capture, which the adapter acknowledges;
//! 2. restricted: the session takes only flow control and one Activate, and anything else, a
//! request or another reset, fails the activation. The activation decodes the Activate's
//! record with [`ActivationRecord::decode`](crate::activation::ActivationRecord::decode), and
//! accepts it only as a clone's, with a restore section, that expects the CPU count and the
//! memory size that the repair reports, before anything has a side effect. An Activate whose
//! record the adapter's framing refused unread, as longer than a record can be, fails as a
//! record that long does;
//! 3. activating: the adapter applies the accepted record with `apply`, which
//! 1. pending: [`Activation::accept`] decodes the record with
//! [`ActivationRecord::decode`](crate::activation::ActivationRecord::decode), and accepts it
//! only as a clone's, with a restore section, that expects the CPU count and the memory size
//! that the repair reports, before anything has a side effect. A packet without a record fails
//! the activation. The adapter accepts the record as soon as the clone runs, without waiting
//! for the host;
//! 2. activating: the adapter applies the accepted record with `apply`, which
//! activates the root and the workload with its settings, as below, passes the activation
//! what the session received meanwhile, which came before Ready, then reports the result to
//! [`Activation::complete`];
//! 4. ready, with the mappings pending: the activation completed, opened the fence, and returned
//! what Ready reports beside the adapter's own identities, such as its build, a
//! [`ReadyActivation`](crate::activation::ReadyActivation): that the activation applied, with
//! the record's transaction ID. The host's mapping table, which the record announced, and the
//! start of the sandbox follow in the adapter's order, as after a cold boot;
//! 3. applied, if the application completed before the host asked for Ready in its current
//! session: the activation awaits the host's request, which makes it ready;
//! 4. ready, with the mappings pending: the application completed, and the host asked for Ready,
//! which opened the fence, and got what Ready reports beside the adapter's own identities, such
//! as its build, a [`ReadyActivation`](crate::activation::ReadyActivation): that the activation
//! applied, with the record's transaction ID. The host's mapping table, which the record
//! announced, and the start of the sandbox follow in the adapter's order, as after a cold boot;
//! 5. started: the adapter's order started the sandbox, which [`Activation::start`] records.
//!
//! The host retries an Activate with the same transaction ID and a byte-equal record, and such a
//! retry joins the activation while it runs, or gets its cached result afterwards, without
//! activating again, also in a later session, such as once the host's attachment ended, whose
//! reset a ready clone takes. A changed record, another transaction, another reset before the
//! The clone's control session runs beside the activation. The clone takes the reset that the
//! restore queued for the session that the guest attached before the capture, which the adapter
//! acknowledges, and the host then asks for the clone's Ready, first in each of its sessions:
//! OpenVMM's broker refuses the guest's records while no host is attached, which the guest cannot
//! tell, so the guest answers the host's request rather than sending Ready unasked. A request for
//! Ready that comes while the record applies joins the activation, whose completion answers it,
//! one that comes once the record applied makes the activation ready, and gets its Ready, and one
//! that comes once it is ready gets the established Ready, without activating again: the clone is
//! ready only once a Ready answers the host, so that its deadline ends a clone whose host never
//! asks. A reset of the session, such as once the host's attachment ended, changes nothing of the
//! activation, whose record came with the restore, so that a later session gets the same Ready:
//! it abandons only the requests for Ready of the earlier session. Any other request before the
//! clone is ready, and an activation that failed, wholly or partway, are final. The activation's
//! deadline, which the caller chooses, starts at the restore's acknowledgement, or, for a restore
//! that the host did not gate until one, when the caller makes the activation, and neither input
//! nor retries extend it: the adapter wakes by it, and [ticks](Activation::tick) the activation
//! then, however slowly its input trickles in, and whether or not its output drains. A failure
//! is an [`ActivationFailure`], which names the [`ActivationState`] in which the activation failed
//! and its [`ActivationCause`], and which every later call returns again. The activation never
//! that the host did not gate until one, when the caller makes the activation, and nothing
//! extends it: the adapter wakes by it, and [ticks](Activation::tick) the activation then,
//! however slowly its input trickles in, and whether or not its output drains. A failure is an
//! [`ActivationFailure`], which names the [`ActivationState`] in which the activation failed and
//! its [`ActivationCause`], and which every later call returns again. The activation never
//! powers the VM off: the adapter reports the failure, and ends the VM.
//!
//! The activation's application, `apply` and its types, is NVX's internal reference code of the
Expand Down Expand Up @@ -253,11 +264,14 @@
//! }
//! };
//! println!("{} CPUs, {} bytes of memory", repaired.cpus(), repaired.memory());
//! // Its activation takes the host's Activate, and opens the fence once the clone is
//! // ready: the agent drives it with what its control session receives.
//! // Its activation takes the activation record of the restore packet, and opens the
//! // fence once the clone is ready: the agent applies the record, and serves the clone's
//! // control session.
//! let timeout = Duration::from_secs(30);
//! let activation = Activation::new(&repaired, fence, timeout, Instant::now());
//! println!("activate by {:?}", activation.deadline());
//! let mut activation = Activation::new(&repaired, fence, timeout, Instant::now());
//! let record = activation.accept(Instant::now())?;
//! let mappings = record.configuration().mappings;
//! println!("activate {mappings} mappings by {:?}", activation.deadline());
//! }
//! }
//! # Ok(())
Expand Down
Loading
Loading