Repository navigation
Adopt bind-once microVM image slots - #406
Enrique Saurez (esaurez) wants to merge 1 commit into
Conversation
6da62e9 to
b1a86cb
Compare
There was a problem hiding this comment.
🟡 Changes recommended
Standard benchmarks are mislabeled as ABI 3, and stale host-control sockets can prevent sandbox deprovisioning.
2 open findings
What changed in this PR
Adopts OpenVMM’s bind-once image-slot ABI and integrates it across NVX guest tooling, managed sandboxes, testing, benchmarks, and documentation.
Changes:
- Pins OpenVMM image-slot support and adds ABI-3 guest activation and host control.
- Extends sandbox, restore, CI, and benchmark workflows.
- Documents the new ABI, constraints, and operational model.
| File | Description |
|---|---|
openvmm |
Pins the image-slot OpenVMM revision. |
SOURCE-MANIFEST.json |
Advertises microVM ABI 3. |
scripts/nvx.py |
Adds image-slot CLI operations. |
scripts/nvx_tools/sandbox.py |
Configures sandbox image slots. |
scripts/nvx_tools/sandbox_lifecycle.py |
Persists slots and exposes host control. |
scripts/nvx_tools/host_control.py |
Implements the host-control client. |
scripts/nvx_tools/control_session.py |
Shares local-stream transport logic. |
scripts/nvx_tools/microvm_tests.py |
Adds cross-backend slot scenarios. |
scripts/nvx_tools/benchmark.py |
Adds image-slot boot benchmarking. |
scripts/nvx_tools/build.py |
Installs the guest slot helper. |
scripts/nvx_tools/build_constants.py |
Updates the supported ABI version. |
scripts/test_nvx_tools.py |
Tests CLI, lifecycle, and benchmarks. |
scripts/test_microvm_tests.py |
Tests restore activation and helper behavior. |
scripts/test_guest_time.py |
Tests restore-packet slot targets. |
kernel/patches/0003-microvm-virtio-mmio-shared-status.patch |
Maps shared status for four slots. |
guest/common/nvx-image-slot |
Discovers stable read-only slot devices. |
guest/common/nvx-time.c |
Parses restore image-slot targets. |
guest/common/nvx-snapshot |
Activates slots during restore repair. |
guest/common/nvx-init-agent |
Supports slot-aware scratch-only workloads. |
guest/common/nvx-virtio-restore-probe |
Adds image-slot restore probing. |
.github/actions/run-benchmark/action.yml |
Runs image-slot benchmarks. |
.github/actions/check-quality/action.yml |
Lints the new shell helper. |
doc/usage.md |
Documents CLI options. |
doc/run.md |
Documents runtime workflows. |
doc/setup.md |
Updates validation commands. |
doc/build.md |
Updates manifest ABI documentation. |
doc/benchmarks.md |
Documents benchmark methodology. |
doc/design/time-abi.md |
Defines the restore-packet field. |
doc/design/snapshot-and-restore.md |
Defines restore-time activation. |
doc/design/machine-and-device-abi.md |
Defines ABI-3 slot topology. |
doc/design/current-limits.md |
Records image-slot limits. |
doc/design/goals.md |
Updates ABI goals. |
doc/design/integrated-implementation-status.md |
Updates implementation status. |
doc/design/remaining-production-work.md |
Distinguishes slots from general volumes. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
b1a86cb to
ac5d342
Compare
ac5d342 to
59219c5
Compare
Pedro Henrique Penna (ppenna)
left a comment
There was a problem hiding this comment.
IRQ budget: this spends the last dedicated lines
I checked whether the image slots overlap with, or run us out of, dedicated IRQ lines when other devices, notably virtio-fs, are in use. They never overlap, but this design consumes essentially every remaining dedicated IRQ line on the microVM.
The MP table routes only ISA IRQs 0, 1 and 3–15, and every virtio line is edge-triggered, so devices can't safely share a line. After the PIT (0) and the RTC (8), 13 lines remain for virtio devices. This PR changes these:
| ISA IRQ | ABI 2 | ABI 3 (this PR) |
|---|---|---|
| 1 | free | image slot 0 |
| 13 | second virtio-fs share | image slot 1 |
| 14 | free | image slot 2 |
| 15 | free | image slot 3 |
| 5 or 10 | free (whichever virtio-net doesn't use on that backend) | still free |
- 12 of 13 lines used. A fully populated ABI-3 machine (virtio-net, first share, console, control console, four sandbox disks and four image slots) uses 12 of the 13 lines. The spare is IRQ 5 on KVM/MSHV and IRQ 10 on WHP, so no line is left that is free on every backend.
- Reserved even when idle. All four slot IRQs stay reserved with
--image-slot-boot-count 1, because a restore may activate up to four slots. - Already costing a feature. Slot 0 takes the second share's MMIO window and status word, and slot 1 takes its IRQ 13, so a VM with image slots gets only one virtio-fs share.
- No room to grow. Any future fixed device (vsock, RNG, a second NIC, more slots, or a second share next to the slots) has no line left. Getting more lines, for example by routing IOAPIC pins 16–23 through the MP table (the IOAPIC already has 24 pins), would be yet another machine-ABI change.
Machine ABI
This also bumps the microVM machine ABI to 3. New guest-visible addresses, IRQs and command-line tokens require a new ABI version, so any design that adds fixed transports will. We'd rather not bump the machine ABI for this, so it's worth exploring whether image delivery can fit in ABI 2 without new guest-visible transports.
What holds up
- Overlap is rejected at every layer. OpenVMM refuses image slots plus a second share in both discovery and machine validation, which the worker runs on every launch, restores included. An ABI-2 snapshot can't activate slots, and NVX rejects more than one
--mountwith slots. - Nothing in the guest competes for the new lines. IDE/ATA, parallel port, serial, ACPI and PCI are off. The PS/2 driver is built in but never claims IRQ 1, because unclaimed ports read
0xFFand it finds no controller.
Test gaps
- No CI boot populates every line. The image-slots scenario boots only the console, the dormant first-share slot and the slots. Managed sandboxes with slots are covered only by unit tests.
- Two of the new lines never fire. The scenario binds and reads only slots 0 and 3, so slots 1 and 2 (IRQs 13 and 14) never raise an interrupt.
Merge conflict
dev has moved the OpenVMM pin to 3e1f5bd4 (WHP CPUID change). This PR's 5ffe5c22 is based on 762bc1c7 and lacks that commit, so the promotion needs rebasing onto 3e1f5bd4. Resolving the gitlink in this PR's favor would drop that change.
59219c5 to
32c710e
Compare
32c710e to
4ac1fb5
Compare
c793a0e to
9393894
Compare
|
Heads-up for your next rebase onto That breaks the scratch-only image-slot boot here without a textual conflict. The agent merges cleanly, but When rebasing, set lowerdirs=
if [ "$distro" = true ]; then
lowerdirs=$layers/distro
fiThe same zero-layer case shows up as a textual conflict in |
|
Closing: the image-backed runtime plan in #535 uses role-keyed blocks and leaves multiple image slots out of scope. The branch stays, so existing pins remain fetchable. |



Summary
Pins OpenVMM to nanvix/openvmm#116 (
67b641d8), which adds bind-once read-only virtio-blk image slots to the microVM profile as part of ABI 2, and adopts them in NVX.nanvix/openvmm#116 is based on OpenVMM
mainf4566bab, the revisiondevalready pins (it includes the merged #124 and #125), so this promotion adds exactly #116's four commits:f5c8dab0virtio_blk: add bind-once image slots0a3dc07emicrovm: add bind-once read-only image slots4eb09487microvm: fold image slots into ABI 267b641d8microvm: harden host-control framing, logging, and cleanupNo other fork changes are included. Dependency state: nanvix/openvmm#116 is open.
SOURCE-MANIFEST.json,aci_edge_sandboxes, and the benchmark history dimensions are unchanged. A machine that does not declare slots keeps its device topology. Image slot 0 reuses the MMIO window and status word of the second virtio-fs share, and slot 1 reuses its IRQ 13, so a slot-declaring launch accepts at most one--mount.--image-slot-boot-countfor managed sandboxes,--restore-image-slots,query-image-slots, andbind-image-slot. Slot launches may omit thedistrolayer; other launches keep the existing layer requirement. A managed configuration that declares slots uses configuration format 6, which earlier releases reject.stopanddeprovisionremove the socket.nvx-timereads the image-slot target from restore packet v4 (flag bit 4, header byte 7), validates it, and passes it tonvx-snapshot. It never finishes a restore with a target by itself;/dev/nvx-imageNlinks. Placeholders stay unbound, andnvx-image-slotfails at once for an inactive slot or a window that holds a live share. Managed sandboxes probe slots only when the launch declares them. The Azure Linux initramfs also installsnvx-image-slot.benchmark --suite image-slot-bootcompares launches without slots and with one or four active slots.Rebase onto
devThis is one commit on
dev3468901f; the previous head was59219c57onb1b5c5bc.devmoved the pin to Harden source provenance integrity #124's head and then tomainf4566babonce Harden source provenance integrity #124 and ci: materialize Windows release provenance from run head #125 merged. guest: add Ubuntu userland support #116 was rebased onto thatmain, and this commit pins its head, so nodevchange is dropped.aci_edge_sandboxes2-or-3 acceptance, the per-suite benchmark ABI, and the ABI-3 documentation. The benchmark series areno-slots,slots-b1, andslots-b4.devnow hands OpenVMM its control capability in a prepared pipe, because OpenVMM reads it without blocking as soon as it starts (Managed start can fail when OpenVMM reads its control capability before NVX writes it #440). The image-slot scenario and the boot-cost benchmark wrote it after the spawn, so they now usecapability_pipe()as well.sandbox_lifecycle.start's failed-start cleanup, which keepsdev's outcome reporting and also removes the host-control socket.Review fixes:
deprovisionrefused a stalehost-control.sockleft by a killed slot sandbox, andstopdid not remove it.nvx-image-slot, which the default suite's image-slot scenario needs.bind-image-slotaccepted identities up to 4096 bytes; OpenVMM accepts 1 to 1024.already_bound.bind-image-slotno longer prechecks the media path, which broke idempotent recovery after the path was removed. It passes the absolute path, and OpenVMM validates the media only when the slot is empty.--layerrow matches the CLI, the run guide notes that earlier releases reject format 6, the filesystem architecture describes the scratch-only root and the single live share of a slot launch, and the time ABI separates an image-slot-only restore from a restore with nothing to activate.Validation
Linux (Pedro, Azure Linux 3.0, Python 3.12), on
c793a0e0(9393894dchanges only documentation): the nineunittestmodules of the CI CLI validation passed 807 tests (34 skipped);test_adversarial.py,test_hosts.py, andnvx.py verifypassed.Linux/MSHV, with OpenVMM
67b641d8: the image-slot runs used874fd540; the other runs used3b885041, which differs only in the scenario's stricter rebind check and thebind-image-slotclient. The head9393894ddiffers from874fd540only indev's documentation and baseline-data commits, the host-control client tests, and documentation. Host: AMD EPYC 9V74, Linux6.6.148-200.mshv-b1f02da.azl3.build-openvmm --backend mshv(musl, Rust 1.95.0): OpenVMM SHA-256b2c02064….build-guestfor Alpine and Azure Linux: initramfsd5773270…and0565e5fd….This host's hypervisor cannot present the built-in
amd.genoa.v1profile that--cpu-profile autoselects (it lackstsa_l1_no_supportandtsa_sq_no_support). A test-only wrapper therefore passed--cpu-profile hostto cold microVM boots. Restores, which keep the snapshot's profile, and thecpu-profile-fallbackscenario ran unchanged.test-microvm --scenario image-slotspassed on Alpine and on Azure Linux: boot withB = 1, bind, idempotent rebind, rejection of a different identity, and a guest read; then capture an empty-slot snapshot, restore with--restore-image-slots 4(NVX-IMAGE-SLOTS-ACTIVE-OK: count=4before acknowledgement), and bind and read slots 1, 2, and 3. A deliberately wrong expectation for slot 2 failed the scenario.test-microvm --scenario managed-lifecycleand the default suite (39 runs, 362 s, includingcpu-profile-fallbackand the L3/L4 egress protocol and port-range acceptance) passed.benchmark --suite image-slot-boot --processors 1 --warmups 3 --runs 21, cold boot toALPINE-MICROVM-BOOT-OK:no-slotsslots-b1slots-b4Configurations ran in consecutive blocks, not interleaved.
Local (Windows), on the same change before the last
devrebase:ruff check,ruff format --check,pyright(Linux and Windows),shellcheck,shfmt, andnvx.py verifyare clean. The nineunittestmodules ran 838 tests; the 74 failures and errors are exactly those ofdevon the same machine. New tests fail without their fixes and pass with them (host-control endpoint,deprovisionof a stale socket, and both capability-pipe launchers). Onc793a0e0,ruff,pyright, andnvx.py verifyare clean (andnvx.py verifyon9393894d), and thetest_control_session.pyandtest_microvm_tests.pyfailures on this machine are exactly those ofdev. The new host-control tests fail when the client's request-ID, response-size, or request-size check is removed.OpenVMM
67b641d8: Windowsnextestpassed 583 tests; Linuxcargo testpassed 621. Clippy, fmt, and rustdoc are clean (details in nanvix/openvmm#116).CI on
9393894d: the KVM, MSHV, and WHP microVM jobs (including the image-slot scenario), platform jobs, OpenVMM unit and vmm-tests, NVX CLI on Linux and Windows, and the performance regression gate passed. The lint job could not pull its pinned shellcheck image from Docker Hub (toomanyrequests) on three attempts; its checks pass locally (ruff,compileall,shellcheck,pyrightfor Linux and Windows,ruff format,shfmt, the PowerShell parse, and the Specula tests on Linux).Not covered here: no boot populates every IRQ line at once.