vmm: add memory reserve option to opt out of MAP_NORESERVE - #8350
Conversation
5b5083f to
2018879
Compare
phip1611
left a comment
There was a problem hiding this comment.
This looks sensible to me and the writeup is nice - thanks! I think this could also be useful for us.
I'll wait with my approval as I'd like to hear @rbradford opinion first
|
|
||
| ### `reserve` | ||
|
|
||
| Specifies if the memory for this zone should be `mmap(2)`-ed _without_ the |
There was a problem hiding this comment.
Not sure if a duplicate section here make sense - perhaps we can have on generic reserve section and then link to it?
Not sure what the best thing is to do here. In case other sections are also already duplicated here, I'm fine with it
| thp: bool, | ||
| ) -> Result<MmapRegion<AtomicBitmap>, Error> { | ||
| let mut mmap_flags = libc::MAP_NORESERVE; | ||
| let mut mmap_flags = if reserve { 0 } else { libc::MAP_NORESERVE }; |
There was a problem hiding this comment.
I'd like to understand why CH decided to go with libc::MAP_NORESERVE in the first place. @rbradford
There was a problem hiding this comment.
It's been like that since the beginning and that came from vm-memory defaults. It does allow overcommit strategies - for huge pages it might make sense to default to on. Something to consider in the future.
|
Maybe we should make the huge page integration tests turn this on since that's the most likely place to use them? |
Yes, I agree that's a good idea. I've updated the integration test to address this. |
|
The change to the integration test should probably be a separate commit. |
Cloud Hypervisor maps guest RAM with MAP_NORESERVE, so the kernel never reserves the backing pages at mmap time. On a host whose hugepage pool cannot satisfy every guest, a VM is created successfully and then takes a SIGBUS when the guest faults a page the pool can no longer back. This is the failure mode reported in cloud-hypervisor#5730 and cloud-hypervisor#7387. As noted on cloud-hypervisor#5730, checking free pool headroom up front is not a reliable fix: another process can consume pages between the check and the fault. Add a reserve=on parameter to --memory and --memory-zone (default off, preserving the current MAP_NORESERVE behaviour). When set, the region is mapped without MAP_NORESERVE, so the kernel reserves the backing pages (swap, or huge pages for hugepage-backed memory) at mmap time, atomically with the mapping. An over-committed configuration then fails cleanly at VM creation with an mmap ENOMEM instead of crashing the guest later. Unlike prefault it does not fault the memory in, so it does not slow down boot. This mirrors QEMU's memory-backend reserve property, which has the same name and meaning (reserve=off maps with MAP_NORESERVE). reserve is threaded through the same mmap paths as the existing prefault option, and is exposed in the OpenAPI schema, CLI help and docs. The top-level --memory reserve=on path is unchanged: the default zone is synthesised from MemoryConfig and inherits its reserve value. Assisted-by: Claude Code (Opus 4.8) Signed-off-by: Ian Klemm <[email protected]>
Turn reserve=on for the hugepage-backed memory zone in the UFFD snapshot/restore integration test. Hugepages are the most likely place to want reserve (an over-committed huge page pool is exactly the case that otherwise SIGBUSes the guest), so this is the natural test to give the option real coverage, as suggested in review. It exercises the reserve mmap path twice: once on the source VM boot and once on the demand-paged restore. The existing skip guard already requires the 256 free 2MiB pages this zone needs, and the source VM is killed before the restore VM is started, so reserving from the pool never has to back two VMs at once. Assisted-by: Claude Code (Opus 4.8) Signed-off-by: Ian Klemm <[email protected]>
Summary
Adds a
reserve=on|offoption to--memoryand--memory-zone(defaultoff). When enabled, guest RAM is mapped withoutMAP_NORESERVE, so thekernel reserves the backing pages (swap, or huge pages for hugepage-backed
memory) at
mmaptime. An over-committed configuration then fails cleanly atVM creation with an
mmapENOMEMinstead of succeeding and later delivering aSIGBUSto the guest.Default
offpreserves today's behaviour exactly.Motivation
Cloud Hypervisor maps guest RAM with
MAP_NORESERVE, so the kernel neverreserves the backing pages up front. With hugepage-backed memory on a host whose
pool cannot satisfy every guest, the VM boots fine and then dies with
SIGBUSthe moment the guest faults a page the pool can no longer back. This is the
failure mode in #5730 and #7387.
As noted on #5730, checking free hugepage headroom before boot is not a reliable
fix, because another process can consume pages between the check and the fault:
reserve=onsidesteps that race: the reservation is made by the kernelatomically with the
mmap, so the pages are committed to this mapping andcannot be stolen. Over-booking is reported as a clean
ENOMEMat create time,where the caller can handle it, rather than as a guest crash later.
Design
memory-backendreserveproperty, which carries the samename and meaning (
reserve=offmaps withMAP_NORESERVE). CH keeps thedefault at
offto preserve existing behaviour; QEMU defaults toon.create_ram_region:let mut mmap_flags = if reserve { 0 } else { libc::MAP_NORESERVE };prefaultoption (boot,restore, hotplug), carried on the runtime
MemoryZone.prefault, it does not populate or fault the memory in, so it does notslow down boot; it only reserves.
--memory(global) and--memory-zonegranularity. Thetop-level
--memory reserve=onpath is unchanged: the default zone issynthesised from
MemoryConfigand inherits itsreservevalue.Included
--memory/--memory-zoneparsing and CLI helpMemoryConfig/MemoryZoneConfigfields (serde, defaultfalse)MemoryConfig,MemoryZoneConfig)docs/memory.md(### reservefor both, mirroring### prefault)reserve=onparsing (global and per-zone)Testing
cargo +nightly fmt --all --check: cleancargo test -p vmm test_mem: passes (global and per-zonereserve=onparse)MAP_SHAREDhugepage path: withreserve=onan over-bookingmmapreturnsENOMEM; withreserve=off(default) the same config maps successfully and the guest takes
SIGBUSonfault. A CH functional test over-booking a hugepage zone returns
Mmap(OutOfMemory)at create withreserve=on, and is unchanged withreserve=off.Notes
accounting beyond what the kernel already does for a bound mapping.
reserveis a--memory/--memory-zoneoption only; it is not a restoreoption and does not interact with
--restore prefault.