Repository navigation
[DO NOT REVIEW] state_unit: fail a state change instead of panicking when a unit is lost - #5013
Merged
Pedro Henrique Penna (ppenna) merged 3 commits intoNov 10, 2026
Conversation
When a start fails, its rollback stops the units that did start with run_op, which panics when one of them does not answer, as when its task exited after it started (microsoft/nvx#1363). The rollback also stops a started unit while a unit that depends on it may still run, such as a unit whose start is uncertain, or a unit that ran before a hot-add. Stop the started units in reverse dependency order, as the later revisions of #4872 do: a started unit is not sent a stop request while a unit that depends on it is not stopped, unless that unit was removed, and a started unit whose stop is blocked or not answered is left in the terminal StartUncertain state. Such a unit may still run, so the units then stay running, so that StateUnits::stop can stop it. A unit that failed to confirm its start is still stopped with the units that started, and can start again. Tests: a dependency of a unit whose start is uncertain, a started unit whose task exits before the rollback stops it, a new dependency of a running unit, and a unit that drops the rollback's stop request each fail the start and stay running, and a later stop stops them. Before the change, two of them panic in run_op with UnitRecvError, and the other two stop the dependency. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
A unit whose task has exited, or that drops its stop request, makes
StateUnits::stop panic the VM worker with "UnitRecvError { name: ..,
source: Channel(Closed) }": run_op treats an unanswered request as an
unrecoverable unit failure (microsoft/nvx#1363). OpenVMM's profiles
abort on panic, so the process ends without reporting the failure or
tearing the VM down. A microVM stops its units when it quiesces for a
snapshot, and whenever it pauses or tears down.
Stop the units in reverse dependency order without panicking, with the
ordered stop of the start rollback, which moves to a module that both
share, as #4937 proposes upstream. A unit whose stop
request is not answered is left in a new terminal state, StopUncertain,
and the units that it depends on are not sent a stop request, since it
may still use them. StateUnits::stop then returns the failures, and the
units stay running, so that a later stop can stop them, as once that
unit is removed. A unit removed during a stop, even while it waits for a
unit that depends on it, is not a failure, and no longer keeps its
dependencies running. A stop also stops a unit in a terminal state,
which keeps its marker, and while a unit is in one, a later start,
reset, save, restore, or time advance fails.
The callers handle the error:
- LoadedVm::pause fails, leaving the VM paused and state units running.
A reset, pulse save and restore, VMRS dump, or pause reports the
failure, and the worker then tears the VM down, since those units can
never start again: it does so whenever state units run while the VM
does not, as a failed stop, or a start whose rollback did not stop
every unit, leaves them.
- VmRpc::Pause becomes failable, as VmRpc::Resume is in this chain, so
that the REPL, the ttrpc PauseVm handler, the VM controller's disk
snapshot, and Petri report a failed pause. During a microVM snapshot
boundary it fails, as a resume does, rather than being dropped.
- A microVM snapshot quiesce whose units do not all stop fails with the
new SnapshotQuiesceError::Unrecoverable, and the controller
terminates the source, which can neither roll back nor resume, with
nothing published.
- A worker restart whose VM cannot stop reports the failure and tears
the VM down, and PCI resource assignment returns the error.
- The teardown, a failed snapshot boundary, a failed restore readiness
event, and an expired restore gate log a failed stop, since the VM
ends anyway.
- OpenHCL reports a failed stop to the requester of a pause, which
UhVmRpc::Pause now allows, a diagnostic save, a worker restart, or a
servicing request, and keeps serving, as after a failed start.
Tests: a lost unit fails the stop, keeps its dependency running, and
fails a later start, reset, save, restore, or time advance, while the
units run and after another stop; a unit whose stop is unanswered stops
when asked again and stays terminal; a unit removed during a stop does
not keep its dependency running, even while it waits behind a unit whose
stop is unanswered. At the previous commit, the first two scenarios,
and the removal behind an unanswered stop, panic in run_op with
UnitRecvError.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
StateUnits::reset, save, restore, and advance_time panic the VM worker when a unit does not answer their request, as when its task has exited (microsoft/nvx#1363): run_op treats that as an unrecoverable unit failure. A microVM saves its state units for a snapshot, and restores them and advances their time when it restores one. Have run_op fail the operation instead, naming the unit in the operation's StateTransitionError, and leave the unit in the operation's terminal state, ResetUncertain, SaveUncertain, RestoreUncertain, or AdvanceTimeUncertain, since its state is unknown, so that a later start, reset, save, restore, or time advance fails too. A unit removed during the operation still does not fail it. run_op now serves only these failable requests, so it returns each unit's result as an error. Tests: a unit built with UnitBuilder::build whose receiver is dropped fails a save, a reset, a restore, and a time advance, naming the unit, while the other units complete it, and stays terminal; a unit removed during a save does not fail it. At the previous commit, the first four panic in run_op with UnitRecvError { name: "lost", source: Channel(Closed) }. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna)
requested a review
from a team
as a code owner
November 10, 2026 13:10
Pedro Henrique Penna (ppenna)
merged commit Nov 10, 2026
a8a0e35
into
feature/microvm/dev
73 of 74 checks passed
syeedali
pushed a commit
to syeedali/nvx
that referenced
this pull request
Nov 11, 2026
A state unit whose task has exited makes the VM worker panic at its
next stop, save, reset, restore, or time advance, or in the rollback
of a failed start, with "UnitRecvError { .., source: Channel(Closed)
}" (microsoft#1363): StateUnits::run_op treats a request that a unit does not
answer as an unrecoverable unit failure. OpenVMM's profiles abort on
panic, so the process ends without reporting the failure or tearing
the VM down. A microVM stops its units for every snapshot quiesce,
pause, and teardown, saves them for every capture, and restores them
and advances their time for every restore. Only a start already failed
instead, leaving the unit in a terminal state.
Pin a8a0e35178f61c0e82cbe465b7ebf70e0f0bb543, the head of
microsoft/openvmm#5013, three commits on 6ddd6ca50, the head of
feature/microvm/dev:
- 2fcd1573d state_unit: roll back a partial start without panicking on
a lost unit
- 5a750a862 state_unit: fail a stop instead of panicking when a unit
is lost
- a8a0e3517 state_unit: fail a reset, save, restore, or time advance
on a lost unit
A unit that does not answer a stop, a start's rollback, a reset, a
save, a restore, or a time advance now fails that request, named in
the error, and is left in a terminal state that fails every later
start, reset, save, restore, and time advance. Its dependencies keep
running when it does not stop, since it may still use them, and the
units then stay running for a later stop. The VM worker tears the VM
down when state units run while it is paused, VmRpc::Pause reports a
failed stop, and a snapshot quiesce whose units do not all stop fails
with SnapshotQuiesceError::Unrecoverable, on which the controller
terminates the source with nothing published.
Evidence: OpenVMM state_unit tests that follow the issue's
reproduction, a unit built with UnitBuilder::build whose receiver is
dropped, panic with "UnitRecvError { name: "lost", source:
Channel(Closed) }" in a stop, reset, save, restore, and time advance at
6ddd6ca50, and return an error naming the unit at the pin, on
nvx-kvm-test (Linux) and nvx-whp-test (Windows), both virtual machines,
and on a local Windows host. With the pin, the guest-boot, lifecycle,
managed-lifecycle, sandbox-lifecycle, restore-downtime, snapshot-core,
and snapshot-rollback scenarios pass with 1 and 2 vCPUs on
nvx-kvm-test (KVM), nvx-mshv-test (MSHV), and nvx-whp-test (WHP). The
OpenVMM tests of the changed packages pass, 505 on Windows and 599 on
Linux, where underhill_core is built and tested too.
The control-session vectors, the source excerpts, the contract
versions, and the CPU profiles that nvx-rs copies from OpenVMM match
the revision's.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
syeedali
pushed a commit
to syeedali/nvx
that referenced
this pull request
Nov 11, 2026
Map positions 243-245, 2fcd1573d, 5a750a862, and a8a0e3517, which microsoft/openvmm#5013 proposes for feature/microvm/dev on its head, position 242, 6ddd6ca50, which microsoft/openvmm#5012 merged with position 241, and move NVX's pin to position 245, 114 commits past the mapped tip. Position 243 has the rollback of a partial start, which PR09's position 015 adds, stop the units that started again in reverse dependency order, without panicking on a unit whose task does not answer, as microsoft/openvmm#4872's head does. It is therefore a PR09 fixup, recorded as positions 241 and 242 are: the DAG's PR09 fixup step, PR09's summary row, graph node, and mapping count it, and the fold sentence after the DAG sends it into position 015, and its description of the snapshot rollback into PR17's position 072. Positions 244 and 245 have a stop, and a reset, save, restore, or time advance, fail instead of panicking when a unit does not answer, which no mapped commit adds, so they stay unassigned, with remarks on the chain commits whose callers they change. microsoft/openvmm#4937 proposes position 244's change for upstream's code, so the paragraph on it no longer says that it proposes no chain commit. Replaying them on upstream main, ba105dd7f, conflicts in all three files of position 243, 12 of the 20 of position 244, and two of the three of position 245, since upstream main lacks the start rollback of position 015 and the other chain commits that they build on. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
syeedali
pushed a commit
to syeedali/nvx
that referenced
this pull request
Nov 11, 2026
The NVX pin moves to cd5cd3814, position 246, 115 commits past the mapped tip. Position 246, which microsoft/openvmm#5016 proposes for feature/microvm/dev on its head, a8a0e3517, is unassigned. It adds the PCI counterparts of two MMIO tests of the virtio restore: a restore whose active queue lacks its progress fails before the device sees any state, and a save before the first start returns the restored queue progress and private state. It also corrects the module documentation of the virtio-net saved-state tests, which still described the deferred restore of device-private state. The Scope also says that microsoft/openvmm#5013 merged positions 243-245. Position 246 changes only the test modules of PR13's positions 039 and 048, the first of which positions 213 and 218 also extend. On upstream main, both of its files conflict, since upstream main lacks both test modules, so it follows PR13 upstream. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Keeps a state unit whose task has ended from panicking the VM worker, for microsoft/nvx#1363.
StateUnits::run_op, whichstop,reset,save,restore,advance_time, and the rollback of a failed start use, panicked withUnitRecvError { .., source: Channel(Closed) }when a unit did not answer its request, as when its task has exited, by a stated policy that unit failure is not recoverable. OpenVMM's profiles abort on panic, so the process ended without reporting the failure or tearing the VM down. Onlystartalready failed instead, leaving the unit in the terminalStartUncertainstate.Three commits on
6ddd6ca50, the head offeature/microvm/dev. microsoft/nvx#1448 pins this head.Roll back a partial start without panicking on a lost unit (
2fcd1573d)run_op, so a started unit whose task then exits panics it. It also stops a started unit while a unit that depends on it may still run, such as a unit whose start is uncertain or, in a hot-add, a unit that already ran.StartUncertain. Such a unit may still run, so the units then stay running, and a laterStateUnits::stopstops it. A unit that failed to confirm its start is still stopped with the units that started, and can start again.tests/start.rs, a dependency of a unit whose start is uncertain, a started unit whose task exits before the rollback stops it, a new dependency of a running unit, and a unit that drops the rollback's stop request each fail the start and stay running, and a later stop stops them. At6ddd6ca50, two of them panic inrun_opwithUnitRecvError, and the rollback stops the dependency in the other two.Fail a stop instead of panicking when a unit is lost (
5a750a862)StateUnits::stopreturned()and panicked inrun_opon a unit that did not answer. A microVM stops its units for every snapshot quiesce, pause, and teardown.anyhow::Result<()>. A unit whose stop request is not answered is left in a new terminal state,StopUncertain, and the units that it depends on are not sent a stop request, since it may still use them; the units then stay running, so that a later stop can stop them. A unit removed during the stop, even while it waits behind a unit whose stop is not answered, is not a failure and no longer keeps its dependencies running. A stop also stops a unit in a terminal state, which keeps its marker.LoadedVm::pausereturns the error, leaving the VM paused with state units running. A reset, a pulse save and restore, a VMRS dump, a pause, a worker restart, and PCI resource assignment report it, and the worker then tears the VM down, since those units can never start again: it does so whenever state units run while the VM does not, as a failed stop, or a start whose rollback did not stop every unit, leaves them.VmRpc::Pausebecomes failable, as this chain madeVmRpc::Resume, so that the REPL, the ttrpcPauseVmhandler, the VM controller's disk snapshot, and petri report a failed pause. During a microVM snapshot boundary it fails, as a resume does, rather than being dropped.SnapshotQuiesceError::Unrecoverable, and the VM controller terminates the source, which can neither roll back nor resume, with nothing published.UhVmRpc::Pausenow allows, a diagnostic save, a worker restart, or a servicing request, and keeps serving, as after a failed start; state_unit: fail a stop instead of panicking when a unit is lost #4937 tears it down instead.tests/stop.rs, a lost unit fails the stop, keeps its dependency running, and fails a later start, reset, save, restore, and time advance, while the units run and after another stop; a unit whose stop is not answered stops when asked again and stays terminal; and a unit removed during a stop does not keep its dependency running, even while it waits behind a unit whose stop is not answered. At the previous commit, the first two, and the removal behind an unanswered stop, panic inrun_opwithUnitRecvError. The existing tests that stop units, among them [DO NOT REVIEW] state_unit: handle a unit in a terminal state instead of panicking #5012's intests/terminal.rs, unwrap the result, and the snapshot filter's tests countPauseamong the failable requests.Fail a reset, save, restore, or time advance on a lost unit (
a8a0e3517)reset,save,restore, andadvance_timereach the same panic inrun_op. A microVM saves its state units for every snapshot, and restores them and advances their time for every restore.run_opfails the operation instead, naming the unit in the operation'sStateTransitionError, and leaves the unit in the operation's terminal state,ResetUncertain,SaveUncertain,RestoreUncertain, orAdvanceTimeUncertain, since its state is unknown, so that a later start, reset, save, restore, or time advance fails too. A unit removed during the operation still does not fail it.run_opnow serves only these failable requests.tests/lost.rs, a unit built withUnitBuilder::buildwhose receiver is dropped, as in the issue's reproduction, fails a save, a reset, a restore, and a time advance, naming the unit, while the other units complete it, and stays terminal; a unit removed during a save does not fail it. At the previous commit, the first four panic inrun_opwithUnitRecvError { name: "lost", source: Channel(Closed) }.Decisions
VmRpc::Pausebecomes failable, like this chain'sVmRpc::Resume, rather than dropping its reply or panicking on the failed stop, as state_unit: fail a stop instead of panicking when a unit is lost #4937's worker does.SnapshotQuiesceError::Unrecoverable, since neitherRollbackSafe, after which the source resumes, norRejected, which refuses the request before it quiesces anything, describes a source that can never run again.Validation
At this head,
a8a0e3517, with Rust 1.97.1:cargo nextest run --profile agent -p state_unit -p openvmm_core -p openvmm_defs -p openvmm_entry -p petri, 505 passed;cargo clippy --all-targetsandcargo doc --no-deps(RUSTDOCFLAGS=-D warnings) for those packages, without warnings;cargo check -p vmm_tests --tests; andcargo xtask fmt --fix, clean. Each commit builds them, and passes thestate_unittests, 24, 28, and 33.underhill_coreadded, without warnings, 599 passed.6ddd6ca50, a stop, reset, save, restore, and time advance of a unit whose receiver is dropped panic withUnitRecvError { name: "lost", source: Channel(Closed) }, while its start fails; at this head all six fail with an error naming the unit, locally on Windows and on NVX's KVM and WHP test VMs.The Linux checks and the test VMs ran
e9e626604, this head before two of its commit messages were reworded, with the same tree.Land by fast-forwarding
feature/microvm/devto this head,a8a0e35178f61c0e82cbe465b7ebf70e0f0bb543, since this repository's merge button squashes and would leave NVX's pin off the branch.