Repository navigation
openvmm: pin state changes that fail instead of panicking on a lost unit - #1448
Merged
Pedro Henrique Penna (ppenna) merged 3 commits intoNov 10, 2026
Merged
Conversation
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)
}" (#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>
The pinned OpenVMM no longer panics the VM worker when a state unit does not answer a request, as when its task has ended: the stop, start rollback, reset, save, restore, or time advance fails, naming the unit, which then never starts again. Say so where the design describes failures: unlike a device that never finishes, a device whose worker has ended fails the capture's quiesce, and the source terminates with nothing published (5.3.3); and such a device fails the state change that it does not answer and never starts again, so the VM does not run again either (5.6.2). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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>
Contributor
There was a problem hiding this comment.
🔵 Needs a closer look
The pin changes dependency-ordered lifecycle transitions, rollback, and teardown, while exact-revision OpenVMM CI remains incomplete and prerequisite PR #5013 is still unmerged.
No open issues 🎉
What changed in this PR
Documents the OpenVMM lost-unit failure handling that addresses #1363: unanswered state changes return errors rather than panic, and an unrecoverable snapshot quiesce terminates the source without publishing a snapshot.
Changes:
- Updates the roadmap’s recorded pin and maps commits 243–245, including upstream dependencies and fixup placement.
- Clarifies snapshot behavior when a device worker has ended.
- Documents that a failed device cannot restart or allow the VM to run again.
| File | Description |
|---|---|
doc/openvmm-upstream-roadmap.md |
Records the new pin and maps the three lost-unit handling commits. |
doc/design/5.6-shutdown-and-failure-semantics.md |
Defines terminal failure behavior for an ended device worker. |
doc/design/5.3-snapshot-protocol.md |
Clarifies source termination and non-publication after a failed quiesce. |
Review effort: Balanced
Pedro Henrique Penna (ppenna)
deleted the
agents/issue-1363-state-unit-lost-task
branch
November 10, 2026 15:11
This was referenced Nov 10, 2026
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.
Fixes #1363
What and why
A registered OpenVMM state unit whose task has exited, by dropping its request
receiver or by panicking in a build that unwinds, made the VM worker panic with
UnitRecvError { .., source: Channel(Closed) }at the next stop, save, reset,restore, or time advance that sent it a request, and in the rollback of a
failed start, instead of returning an error. OpenVMM's builds abort on panic,
so the process ended without reporting the failure or tearing the VM down. A
microVM stops its state units for every snapshot quiesce, pause, and teardown,
saves them for every capture, and restores them and advances their time for
every restore. Only
startalready failed instead, leaving the unit in aterminal state. Only a unit whose task has already failed triggers this, so the
panic replaced an error that was fatal anyway, but the requester never saw it.
Root cause, in OpenVMM:
StateUnits::run_op(vmm_core/state_unit/src/lib.rs),which
stop,reset,save,restore,advance_time, and the start rollbackof the chain's position 015 use, panicked on a
UnitRecvErrorfrom a unit thatthe set still held, by a stated policy that unit failure is not recoverable, and
StateUnits::stopreturned(), so its callers could not see a failure.microsoft/openvmm#5013 keeps the failure fatal without the panic, the option
that the issue recommends, in three commits on
6ddd6ca50, the head offeature/microvm/dev:2fcd1573d,state_unit: roll back a partial start without panicking on a lost unit: the rollback stops the units that started again in reversedependency order, as state_unit: roll back a partial start instead of panicking openvmm#4872's later revisions do. A started
unit that a unit that is not stopped depends on, such as a unit whose start is
uncertain, is not stopped, a started unit whose stop is not answered is left
in the terminal
StartUncertainstate, and the units then stay running, sothat a later stop stops them.
5a750a862,state_unit: fail a stop instead of panicking when a unit is lost:StateUnits::stopreturns an error, as state_unit: fail a stop instead of panicking when a unit is lost openvmm#4937 proposesupstream, and leaves a unit that does not answer in a new terminal state,
StopUncertain, with its dependencies running, since it may still use them.The callers handle the error: a pause fails and the VM worker then tears the
VM down,
VmRpc::Pausebecomes failable, a snapshot quiesce whose units donot all stop fails with the new
SnapshotQuiesceError::Unrecoverable, onwhich the controller terminates the source with nothing published, and
OpenHCL reports a failed stop to its requester.
a8a0e3517,state_unit: fail a reset, save, restore, or time advance on a lost unit:run_opfails the operation, naming the unit, and leaves it inthe operation's terminal state, so that a later start, reset, save, restore,
or time advance fails too.
Commits:
openvmm: pin state changes that fail instead of panicking on a lost unitmoves the gitlink, and nothing else, from
6ddd6ca50, which openvmm: pin the handling of state units in a terminal state #1435 pins, toa8a0e35178f61c0e82cbe465b7ebf70e0f0bb543, the head of[DO NOT REVIEW] state_unit: fail a state change instead of panicking when a unit is lost openvmm#5013; its message carries the evidence below. NVX needs no
coordinated change: the control-session vectors, source excerpts, contract
versions, and CPU profiles that nvx-rs copies from OpenVMM match the
revision's.
doc: say that a device whose worker has ended fails its state changeaddsthe failure to the design: such a device fails a capture's quiesce, and the
source terminates with nothing published
(5.3.3),
and it fails the state change that it does not answer and never starts again
(5.6.2).
doc: map the lost-unit state changes in the OpenVMM roadmapmaps position243 as a PR09 fixup, to fold into position 015, and its description of the
snapshot rollback into PR17's position 072, and positions 244 and 245 as not
yet assigned, and moves the pin to position 245, 114 commits past the mapped
tip. All three conflict with upstream main, so they follow PR09 upstream.
Scope, with #1181's other children:
state; this extends its check to the new terminal states, and its stop of
such a unit to a unit whose stop is uncertain.
stop, item (d) of OpenVMM/microVM umbrella: snapshot quiesce, rollback, and device restoration in the VM worker #1181's notes, with its conditions: the units stay running
whenever the rollback leaves a started unit running, a unit that failed to
confirm its start is still stopped, the teardown stops a running set while
the VM is paused (openvmm: pin the handling of state units in a terminal state #1435), and a reset checks for terminal units before it
asserts that the units are stopped (openvmm: pin the handling of state units in a terminal state #1435).
test_uncertain_start_blocks_dependency_rollbackcovers the test that it asked for: a started dependency of a unit whose start
is uncertain keeps the units running, and a later stop stops it.
and not only after an incomplete rollback, as #4872's later revisions do,
which none of OpenVMM/microVM umbrella: snapshot quiesce, rollback, and device restoration in the VM worker #1181's children takes.
Decisions:
request and stays terminal, the issue's recommended option. Alternatives: make
it recoverable, which needs a definition of the state of the units that
already changed, or keep the panic as the worker's contract.
StopUncertain,ResetUncertain,SaveUncertain,RestoreUncertain, andAdvanceTimeUncertain, besideStartUncertain. Alternative: one sharedstate. Reason: the state names the request that went unanswered; both refuse
the same later requests.
VmRpc::Pause. Chosen: failable, as the chain madeVmRpc::Resume.Alternatives: drop the reply, or panic on the failed stop, as #4937's worker
does. Reason: the requester sees the error.
SnapshotQuiesceError::Unrecoverable,on which the controller terminates the source. Alternatives:
RollbackSafe,after which the source would resume, which it cannot, or
Rejected, whichrefuses a request before anything is quiesced.
while the VM does not, which a failed stop, or a start whose rollback did not
stop every unit, leaves. Alternative: keep serving and fail later requests, as
openvmm: pin the handling of state units in a terminal state #1435 does for a terminal unit in a stopped set. Reason: such units can never
start again, so the VM can never run again.
diagnostic save, a restart, or servicing, and keep serving, as the chain does
after a failed start. Alternative: tear down, as #4937 does. NVX does not use
OpenHCL.
state_unit, which fail without the fix;no NVX scenario can make a unit's task end.
Changes to existing tests, all in microsoft/openvmm#5013 (NVX's
check_test_changes.pylists none): thestate_unittests that stop units,among them #1435's in
tests/terminal.rs, unwrap the stop's result; thesnapshot boundary filter's tests count
Pauseamong the failable requests; andthe start rollback's tests share their recording unit with the new tests.
Landing order: land microsoft/openvmm#5013 first, by fast-forwarding
feature/microvm/devto the exact pinned revision,a8a0e35178f61c0e82cbe465b7ebf70e0f0bb543, since OpenVMM's merge buttonsquashes and would leave the pin off the branch; then merge this pull request.
Validation
Locally, on Windows, OpenVMM at the pin:
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-depswithRUSTDOCFLAGS=-D warningsfor those packages, without warnings;cargo check -p vmm_tests --tests; andcargo xtask fmt --fix, clean. Eachof the three commits builds them and passes the
state_unittests, 24, 28,and 33.
6ddd6ca50, two of therollback's four panic in
run_opwithUnitRecvErrorand the rollback stopsthe dependency in the other two; at
2fcd1573d, three of the stop's fourpanic in
run_op; at5a750a862, four of the five reset, save, restore, andtime advance tests panic in
run_opwithUnitRecvError { name: "lost", source: Channel(Closed) }.UnitBuilder::buildwhose receiveris dropped: at
6ddd6ca50, its stop, reset, save, restore, and time advancepanic in
run_opwithUnitRecvError { name: "lost", source: Channel(Closed) },and its start fails; at the pin, all six fail with an error naming the unit.
python scripts\local_gates.py --base origin/dev --run: test-changes (0changes to existing tests), markdown-links, test-layout, openvmm-vectors, and
openvmm-cpu-profiles passed. nvx-cli, with
NVX_RSnaming a local release build: passed, with 2188 tests (88 skipped), the adversarial suite's 84 (11 skipped), and the help commands. openvmm-ci: waits for OpenVMM'sCI of [DO NOT REVIEW] state_unit: fail a state change instead of panicking when a unit is lost openvmm#5013.
python scripts\openvmm_pin.py check-roadmap --worktree,check-branch --topic,python scripts\check_markdown_links.py, andgit diff --check: passed.On the test hosts, all virtual machines, with this branch in a temporary
worktree and OpenVMM built from the pinned tree, whose binary differs from
that of release
v0.1.0-dev.995230d0ebb4, which provided the guest artifacts.The hosts built
e9e626604, whose tree is the pin's: the pin rewords two ofits commit messages.
6ddd6ca50UnitRecvErrorunderhill_core: 599 passed, clippy and rustdoc cleanUnitRecvErrorstate_unit: 33 passedThe scenarios were
guest-boot,lifecycle,managed-lifecycle,sandbox-lifecycle,restore-downtime,snapshot-core, andsnapshot-rollback, with 1 and 2 vCPUs, which exercise the changed stop on apause, a capture's quiesce, and teardown, the rollback's start, and a restore's
restore and time advance.
Left to CI: the full microVM suites on KVM, MSHV, and WHP, the runtime adapter,
the performance gates, and OpenVMM's own CI of the pin, its unit tests and VMM
tests.
CI: run 42823643715
passed, with the full microVM suites on KVM, MSHV, and WHP, the runtime
adapter, the performance gate, and OpenVMM's CI of the pin. OpenVMM's
run 42822151781
of
a8a0e3517passed every job butrun vmm-tests [x64-windows-amd-snp],whose Hyper-V OpenHCL SEV-SNP memstat and TPM tests timed out after 600 s, as
a Hyper-V OpenHCL SEV-SNP TPM test did in that job of microsoft/openvmm#5012's
run, without this change.