Files
substrate/cmd
Benjamin Elder 327386942f microvm: auto-select chv restore mode / prepare for chv v53 (#847)
## What

Make the micro-VM runtime choose how it restores guest RAM based on the
cloud-hypervisor it is actually driving, so we can support v53.

## Why

cloud-hypervisor 53.0.0 changed on-demand restore in two ways: the
userfaultfd
handler now background-prefaults every registered page (`#8150`), and a
snapshot is
refused while that runs (`#8556`). Together these make
`memory_restore_mode=OnDemand`
— what we use today — **unusable** on v53: the prefault storm starves
the guest and
its readiness probe never passes. The actor never comes up.

cloud-hypervisor ships minor releases and rarely backports fixes to a
patch branch,
so pinning v52 means running an unmaintained hypervisor indefinitely. We
need to be
able to keep up.

Restoring eagerly sidesteps both problems: it reads only the snapshot's
populated
extents and registers no userfaultfd, so nothing prefaults and nothing
gates a later
snapshot. But eager is the wrong choice on v52, where on-demand costs a
tenth of the
memory. So this is a per-VMM decision, not a configuration knob.

## How

- `vmm.ping` already answers before every restore and its reply carries
the version;
we were discarding it. `Ping`/`WaitReady` now return it — no extra
process, no
  extra round trip, nothing to cache.
- Pick the mode from that version. The affected range is **bounded**
(`prefaultingSince`/`prefaultingUntil`) rather than "53 and up forever",
so when a
release stops prefaulting unconditionally, on-demand's smaller footprint
comes back
  by moving one constant.
- An unreadable version restores eagerly and logs it. The two ways to be
wrong are not
equal: guessing on-demand on an affected version leaves the actor unable
to start,
  while guessing eager only costs memory.
- Eager makes the snapshot self-contained, so two things that follow
from it: skip the
merge (it would copy the whole resident set onto the restore source for
nothing), and
drop the staged memory image (nothing pages from it or merges against it
afterwards).

## Measurements

kind, arm64, 2 GiB guest, counter demo. Each config measured
identically, no flags set.

| | idle memfd | VMM RSS | pause (median) | per-actor disk |
|---|---|---|---|---|
| v52, before and after this PR | 16 MiB | 20–21 MiB | ~0.26 s | 158 MiB
|
| v53, before this PR | — | — | — | **actor never becomes ready** |
| v53, with this PR | 158 MiB | 162 MiB | ~0.32 s | 158 MiB |

On v52 the runtime detects `52.0.0`, selects on-demand, and reproduces
the current
shipped behaviour exactly — this is a no-op on what we run today. On v53
it detects
`53.0.0`, selects eager, and the actor works.

Within v53, the two follow-on changes are worth their own line:

| | v53 pause (median) | v53 per-actor disk |
|---|---|---|
| mode selection only | ~0.69 s | 318 MiB |
| + skip the merge | ~0.45 s | 318 MiB |
| + drop the staged image | ~0.32 s | **158 MiB** |

## Testing

- Unit tests for version parsing and selection, including the real
`vmm.ping` payloads
from both binaries we ship, and a test documenting how to retire the
workaround.
- e2e on kind against **both** binaries with no flags set: v52 →
on-demand, v53 → eager,
  counter continuity across five pause/resume cycles each.
- `go test -race ./cmd/ateom-microvm/...` green; builds for linux and
darwin.

----

NOTE: This PR does not upgrade chv to v53+ by default, I'd like to close
the remaining gaps first.
2026-08-11 19:52:08 -07:00
..
2026-08-10 23:28:07 -07:00