runtime-adapter: a KVM clone whose restore brings 2 or more CPUs online can stop answering once its sandbox starts, so the `vm_clone` processor-target scenarios time out (flaky NVX microVM tests / Linux / KVM / Guests and sandboxes)
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 25/100
- Issue type
- Bug
- Clarity
- Needs clarification
- Activity status
- Active
- Domain
- ci-cd, testing-qa
Research direction
Start from the failing scenario a_generation_refuses_clones_of_fewer_processors_than_its_template_booted_with in tests/runtime-adapter/tests/vm_clone.rs (L2557-L2603) and the clone repair steps in nvx-rs/src/guest/clone/repair.rs. Reproduce with python3 scripts/nvx.py test-runtime-adapter --backend kvm on a KVM runner. First read OpenVMM's log around the 0x4e MSR reads and snapshot request. Done means the 4-CPU clone answers the host after sandbox=started and the scenario exits 0.
Written by the indexing model from the issue text.
Description
Summary
The dev push run for 80d4821 (#1041) failed in NVX microVM tests / Linux / KVM / Guests and sandboxes because the runtime adapter scenario vm_clone a_generation_refuses_clones_of_fewer_processors_than_its_template_booted_with timed out. Its restore brought a clone from 2 to 4 online CPUs. The clone then answered the host's activation, mapping table, and start, and answered nothing more for 30 s, without exiting. OpenVMM logged nothing after the guest acknowledged the restore.
The same signature failed the sister scenario one_generation_serves_clones_of_each_processor_count_from_its_boot_online_count_to_its_capacity once before, on 2026-10-29. That was also a target of 4 CPUs on azure-kvm-1, and nobody reported it. Across the CI runs since the two scenarios were added, they failed 2 of 94 Linux/KVM jobs, and never failed on Linux/MSHV or Windows/WHP.
#1041 did not cause this. It changes only a doc comment and unit tests in nvx-rs/src/guest/sys/rooted.rs, and the earlier occurrence predates it.
The root cause is unknown. A clone discards its console (#996), so CI's evidence cannot tell whether the guest kernel, the guest agent, or OpenVMM stalled. Restore-time CPU activation alone does not explain it: the Alpine scenarios that bring up to 7 CPUs online at restore, through the same time-ABI repair, passed all 353 KVM runs in the same period. What a clone adds is:
- its layered root;
- its control session and sandbox start;
- repair step 7's copy of the clone's mounts, which #1020 found runs right after the start.
Incident
- Workflow: CI run 41060782829, attempt 1,
pushtodev. - Head:
80d482192b24ca419181e8d2f0e6ef0b4fe637b1, the merge of #1041, with OpenVMMbcfc47cffas itsopenvmmgitlink. Push runs ondevqueue rather than cancel each other, so this run was not superseded. The nextdevpush run, 41060946185 for78c626d(#1043), queued behind it. - Failed job:
NVX microVM tests / Linux / KVM / Guests and sandboxes, stepRun runtime adapter scenarios on Linux. The step runspython3 scripts/nvx.py test-runtime-adapter --backend kvm(run-nvx-microvm-tests.yml#L1302-L1308). This was the only failure among the suite's 154 scenarios, and the job's earlier steps passed. - Runner: self-hosted
azure-kvm-1(self-hosted,linux,kvm,virtual-machine,general), an Azure VM with an Intel Xeon Platinum 8370C (intel.icelake-sp.v1) on Linux 6.6.159.1-1.azl3.doctorH1-H4 passed. - Downstream:
Required status checkfailed.Publish development release,Persist performance baseline,Package nvx crate, andPublish nvx cratewere skipped.Performance regression gateruns only for pull requests.
The scenario (vm_clone.rs#L2557-L2603) captures a template of 4 processors, 2 of them online at boot. Its first two starts behaved as expected: a target of 1 was refused, and a start without a target started. The start with --processors 4 exited with 8 rather than 0. Decisive output, trimmed; the suite's summary, which the log interleaves with it, is left out:
thread 'a_generation_refuses_clones_of_fewer_processors_than_its_template_booted_with' (2596630) panicked at tests/common/mod.rs:276:9:
assertion `left == right` failed: start ["--template", "/tmp/runtime-adapter-cl-boot-2596205-0/store", "--generation", "1", "--distro", "erofs:/tmp/runtime-adapter-cl-boot-2596205-0/platform/ubuntu-distro.erofs", "--distro-identity", "15161d82-9dfa-6e3a-1eeb-850cbeced4f2", "--upper", "ram", "--processors", "4"] ended with exit status: 8
stdout:
generation=1
lease=106f1f600dce344244b894a1fd40a161
downtime=5476109857
source=monotonic
pid=2597033
root=ready
build=0.1.0
table=applied
mapped=0
sandbox=started
failure.outcome=unknown
failure.evidence=timeout
failure.stage=commit
failure.progress=started
failure.status=none
failure.teardown=terminated
failure.code=timed-out
released=106f1f600dce344244b894a1fd40a161
quarantined=no
stderr:
runtime-adapter-host: the guest agent did not answer once the clone's sandbox started: the NVXS operation timed out; see the VMM's log /tmp/runtime-adapter-cl-m-2596205-1/store/openvmm.log
the end of the VMM's log:
0.000869178s INFO openvmm_entry: version="0.3.0+gbcfc47cff"
0.001672803s INFO openvmm_entry::microvm::restore: time ABI restore preflight passed downtime_ns=0x146e93697 source=HostMonotonic generation=0x1 cpu_profile="intel.icelake-sp.v1"
…
0.036183768s INFO openvmm_entry: restoring VM from snapshot
0.036681236s INFO state_unit::start: state-unit start complete duration=329.342µs
0.172408945s ERROR virt_kvm::arch::x86_64: unrecognized msr read index=0x4e
0.240362473s ERROR virt_kvm::arch::x86_64: unrecognized msr read index=0x4e
0.299495657s WARN openvmm_entry::vm_controller::microvm: ignoring microVM snapshot request because no destination is configured
left: Some(8)
right: Some(0)
The test prints the last 8 KiB of OpenVMM's log (common/mod.rs#L44-L45). This log is shorter and starts at OpenVMM's first line, so it is the whole log: OpenVMM wrote nothing between 0.299 s and the host's termination. The job's artifact nvx-microvm-tests-kvm-guests keeps none of the adapter's /tmp/runtime-adapter-* stores, so nothing else of the clone survives.
History
I checked the runtime adapter step of every NVX microVM tests / * / Guests and sandboxes job in the 446 CI runs created from 2026-10-26 to this run, and the logs of all 34 jobs in which that step failed. The two scenarios first ran on 2026-10-27, on #926's branch. This signature appears twice, both times at a target of 4 CPUs on azure-kvm-1:
| Run | Event | Head, OpenVMM | Job | Scenario | CPUs brought online | Outcome |
|---|---|---|---|---|---|---|
| 41060782829, attempt 1 | push to dev, #1041 |
80d4821, bcfc47c |
124081243899 | a_generation_refuses_clones_of_fewer_processors_than_its_template_booted_with, 4 of 4, 2 online at boot |
2-3 | This incident. The sister scenario's 4-CPU clone passed in the same job. |
| 40808087687, attempt 1 | workflow_dispatch, esaurez/857-ci-20261028 |
f22d9ed, 832d5bb |
123234102710 | one_generation_serves_clones_of_each_processor_count_from_its_boot_online_count_to_its_capacity (vm_clone.rs#L2496-L2555), 4 of 4, 1 online at boot |
1-3 | The same output: sandbox=started, then failure.stage=commit, failure.evidence=timeout, failure.status=none. Three 0x4e reads, then OpenVMM's log ends with the snapshot request at 0.239 s. Its 2-CPU clone, and a_generation_refuses…, passed in that job. Attempt 2 passed. |
How often the two scenarios passed, by the job's runtime adapter step, in the runs whose head has them:
| Backend | Jobs | Passed | Failed with this signature | Failed on other scenarios |
|---|---|---|---|---|
| Linux/KVM | 94 | 85 | 2 | 7 |
| Linux/MSHV | 107 | 105 | 0 | 2 |
| Windows/WHP | 69 | 64 | 0 | 5 |
-
The 85 KVM passes ran on
azure-kvm-1(28),azure-kvm-2(28),azure-kvm-4(15), andazure-kvm-5(14). Every KVM runner has the same CPU model and Azure Linux 3 kernel series. If failures landed on runners in proportion to their jobs, both would land onazure-kvm-1about one time in ten. -
Targets of 2 CPUs, which bring one CPU online, have not failed. Both failures brought 2 or 3 online.
-
Restore-time activation without a clone has not failed. The Alpine scenarios
restore-processorsandplatform-restore-processorsrestore a snapshot captured withmaxcpus=1:restore-processorsrestores capacity 8 at 2, 4, and 8 CPUs.platform-restore-processorsrestores capacity 4 at 2 and 4 CPUs (microvm_tests.py#L5156-L5170, #L328-L329).
They bring the CPUs online through sysfs and finish the restore with
nvx-time restore-finish --new-cpus, which hands step 7 to the same daemon. In the KVMShard 1 of 2jobs, which have run both scenarios since 2026-10-23, of the CI runs created since 2026-10-27, 353 ran these scenarios to the end and passed, 78 of them onazure-kvm-1, and none failed them.
Root cause
The root cause is not known yet. Permalinks point to 80d4821 and to OpenVMM's bcfc47c; the linked NVX files are unchanged on the current dev head, 78c626d.
What the evidence establishes:
- The repair brought the CPUs online and acknowledged the restore.
- The guest's repair brings each offline CPU of the target's prefix online through sysfs before the acknowledgement (repair.rs#L202-L235).
- Each
unrecognized msr read index=0x4eis one CPU coming up: Linux readsMSR_PPIN_CTLas it identifies each Ice Lake CPU, and OpenVMM refuses the read. There are two here and three in the earlier occurrence, one for each CPU that the restore brought online. - The snapshot-request warning is repair step 6, the acknowledgement through the snapshot port (daemon.rs#L937-L941). A clone has no snapshot destination, so OpenVMM answers it by releasing the boundary (microvm.rs#L127-L136, #L539-L554).
- Both lines appear in clones that pass, too.
- The clone worked after that. OpenVMM holds the host's input on the clone's control console until the acknowledgement releases it. The guest agent then assembled the root, applied the mapping table, and started the sandbox, as
root=ready,table=applied, andsandbox=startedshow (runtime-adapter-host.rs#L2722-L2740). - Then nothing answered.
- The host's
ping, thenecho, on the clone's session had 30 s (runtime-adapter-host.rs#L488). - The guest agent's worker answers both at once (worker.rs#L990-L991), and its start only sets a flag (order.rs#L57-L61).
- Neither request got an answer, the VM never exited (
failure.status=none), and OpenVMM wrote nothing more before the host terminated it (failure.teardown=terminated). - So the guest, or the control console's path through OpenVMM, stalled within moments of the start.
- The host's
- Repair step 7 runs in that window.
- The repair hands step 7 to the
nvx-timedaemon. The daemon starts it in a child atSCHED_IDLE150 ms after the acknowledgement, and raises it to normal priority 100 ms later (mod.rs#L94-L97, daemon.rs#L597-L639, #L684). - The child runs the restore checks, pinning itself to each new CPU in turn (checks.rs#L734-L763, #L936-L957).
- It then runs
grace_period_release(daemon.rs#L351-L365). That function forcesrcu_normal=1and unshares a private mount namespace, which copies every mount of the clone. It then mounts and unmounts a tmpfs there to force a normal RCU grace period. - Throughout, the stall detectors stay suppressed until step 7 releases them, or until their 21 s timeout passes.
- #1020's instrumented reproduction showed step 7 starting only once the clone had started and served its first probes.
- The repair hands step 7 to the
- It needs a clone and a target of 4. The Alpine scenarios above bring up to 7 CPUs online through the same repair and step 7, on the same runners, without failing. Targets of 2 have not failed either.
Candidates, none confirmed:
- A newly activated vCPU on these nested-KVM Azure VMs stops making progress or taking interrupts. The guest then stalls once its work, an IPI, or step 7's grace period depends on that vCPU.
- An interaction between step 7 and the clone's root or sandbox start. Step 7 pins itself to the new CPUs, copies the clone's mounts, and forces a grace period while the stall detectors are suppressed.
- A stall in the control console's path, in OpenVMM's broker or the guest's virtio console.
- A fault specific to
azure-kvm-1, as #211's TSC warps were specific toazure-kvm-4.
Reproduction
Not reproduced yet. The scenario fails in about 1 of 50 KVM jobs, so a reproduction needs a loop on a Linux/KVM Azure VM host, as #1020's did on nvx-kvm-test. Run it with the guest artifacts and OpenVMM staged as CI stages them, and with the guest agent built by cargo build --locked --release --features guest --target x86_64-unknown-linux-musl --bin runtime-adapter-guest:
cd tests/runtime-adapter
NVX_HYPERVISOR=kvm NVX_ARTIFACTS_DIR="$(git rev-parse --show-toplevel)" \
RUNTIME_ADAPTER_GUEST="$PWD/target/x86_64-unknown-linux-musl/release/runtime-adapter-guest" \
cargo test --locked --features host --test vm_clone -- --include-ignored --exact \
a_generation_refuses_clones_of_fewer_processors_than_its_template_booted_with
A reproduction under today's diagnostics would show only the same timeout. A clone launches with --virtio-console none (launch.rs#L692-L693), so the run also needs:
- what the guest writes: a console that a reader keeps, as #996 proposes, or instrumented guest programs, as in #1020;
- at the timeout, before the host terminates OpenVMM: OpenVMM's VP states and the kernel stacks of its threads.
Impact
80d4821has a red required check, and neither a development release nor a performance baseline. The nextdevpush run, 41060946185 for78c626d, contains80d4821and runs the same scenarios.- On a pull request, the hang turns
Required status checkred until someone reruns the job. - Each KVM run takes the risk at each clone start that brings CPUs online. There are three per run: targets of 2 and 4 in
one_generation_serves…, and 4 ina_generation_refuses…. Two of the three bring 2 or more CPUs online. - If it is not specific to the CI runners, a clone started with more CPUs than its template had online at boot can stop answering right after it starts, with no record of why.
Suggested fix
- Make the next occurrence diagnosable before changing behavior:
- give the adapter's clones a console that CI keeps (#996);
- record OpenVMM's VP states, and its threads' kernel stacks, when a clone's session times out, before the host terminates the VMM;
- keep the stores of a failed scenario in the job's artifact.
- Reproduce in a loop as above. Then compare runs with step 7's grace-period release, or its namespace copy, delayed or skipped, to tell an interaction with step 7 from a vCPU problem.
- Watch where it lands. If later occurrences stay on
azure-kvm-1, check that VM as #211 didazure-kvm-4. - Do not retry the scenario or lengthen its 30 s: either would hide a clone that stops answering.
Related work
- #849 and #926 added per-clone processor targets and both scenarios.
- #936 added the guest's repair.
- #801 (#796), with microsoft/openvmm#4735, lets platform snapshots activate vCPUs at restore. It was validated on bare-metal KVM, MSHV, and WHP hosts only, while CI's KVM runners are Azure VMs running nested KVM.
- #1020, closed by #1031, is another intermittent KVM
vm_clonefailure, caused by step 7's copy of the clone's mounts. #1020 suggests keepinggrace_period_releasefrom copying the workload's mounts, which no issue tracks yet. - #211 and #253 are earlier anomalies of restore-time CPU activation on the Azure runner VMs. Every KVM occurrence of #211 ran on one VM.
- #996 is the clone console that this diagnosis lacks.
- #1018 rewrites the time daemon and its checks for time ABI 2, step 7 included.
- #1042 (#1038) fixes an OpenVMM VM-time keeper that could spin with its lock held, which would stall the VMM silently. Its triggers, timeouts far beyond the host clock and Hyper-V synthetic timers on WHP, do not match this KVM clone, whose restore came 5.5 s after its capture.
- Dominant language
- Rust
- Stars
- 758
- Forks
- 48
- Avg merge
- 18h 27m
- Merged PRs (30d)
- 446
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from microsoft/nvx
-
Difficulty 1/5 Under an hour Newbie friendliness 85/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 69/100
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 35/100
microsoft/nvx#1055 · 1 comment ·
Maintainers usually reply within 1 day
-
ci: move the performance history's commits off devPossibly taken @ppenna claimed this today. Open
Difficulty 4/5 3-5 days Newbie friendliness 25/100
Maintainers usually reply within 1 day
-
ci: OpenVMM's KVP IC test boots Windows Server 2022, whose COM activation race can fail `GetIpInfo` with 0x80080005 (flaky OpenVMM vmm-tests / Windows / WHP)Possibly taken @ppenna claimed this 1 day ago. Open
microsoft/nvx#1035 · 1 assignee ·
Maintainers usually reply within 1 day
Similar issues
-
good first issue help wanted
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
servo/rust-smallvec#827 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
nowledge-co/hawdb#1400 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
calcit-lang/caps#16 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
penso/herdr-gpui#934 ·
Maintainers usually reply within 1 day
-
🎉 Add BossanovaOpen
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
jaywcjlove/awesome-mac#4096 ·
Maintainers usually reply within 1 day