vmm: memory_manager: add on-demand snapshot restore via userfaultfd

When memory_restore_mode=ondemand is specified on the restore command,
the memory manager creates a userfaultfd descriptor, registers each
guest RAM range for missing-page fault interception, and spawns a
handler thread that serves page faults from the snapshot file using
UFFDIO_COPY. This avoids reading the entire memory-ranges file into
guest RAM before restore completes.

The handler uses epoll to multiplex the userfaultfd and a stop eventfd
for clean shutdown. Concurrent faults from multiple vCPUs are handled
by treating EEXIST as a benign race and waking blocked threads with
UFFDIO_WAKE. Once all pages have been served the handler exits
automatically. If the handler thread panics the VMM is signalled to
exit since the VM cannot continue without page fault service.

MemoryZone gains a backing_page_size field so the handler resolves
fault granularity from the zone rather than the top-level config.

Errors from the UFFD setup path use a structured UffdError enum
and a new MigratableError::OnDemandRestore variant, with a From
impl to keep call sites concise.

The seccomp filter is updated to allow the userfaultfd syscall and
the four uffd ioctls (UFFDIO_API, UFFDIO_COPY, UFFDIO_REGISTER,
UFFDIO_WAKE) under the VMM thread profile.

Signed-off-by: Shayon Mukherjee <shayonj@gmail.com>
This commit is contained in:
Shayon Mukherjee
2026-03-13 08:16:33 -07:00
committed by Rob Bradford
parent bf85af907e
commit c417924a29
5 changed files with 512 additions and 24 deletions

View File

@@ -14,6 +14,38 @@ mod bitpos_iterator;
mod context;
pub mod protocol;
#[derive(Error, Debug)]
pub enum UffdError {
#[error("Snapshot ranges are not page-aligned")]
UnalignedRanges,
#[error("Failed to create userfaultfd")]
Create(#[source] std::io::Error),
#[error("Cannot translate GPA {gpa:#x} to host address")]
GpaTranslation { gpa: u64 },
#[error("Failed to register region at {addr:#x}+{len:#x}")]
Register {
addr: u64,
len: u64,
#[source]
source: std::io::Error,
},
#[error("Region at {addr:#x}+{len:#x} missing COPY/WAKE support")]
MissingIoctlSupport { addr: u64, len: u64 },
#[error("Failed to spawn handler thread")]
SpawnThread(#[source] std::io::Error),
#[error("Handler terminated before startup completed")]
HandlerStartup,
#[error("Handler failed after startup")]
HandlerFailed(#[source] std::io::Error),
}
#[derive(Error, Debug)]
pub enum MigratableError {
#[error("Failed to pause migratable component")]
@@ -34,6 +66,9 @@ pub enum MigratableError {
#[error("Failed to receive migratable component snapshot")]
MigrateReceive(#[source] anyhow::Error),
#[error("On-demand restore failed")]
OnDemandRestore(#[source] UffdError),
#[error("Socket error")]
MigrateSocket(#[source] std::io::Error),