Skip to content

Commit 2d491df

Browse files
committed
wip: refactor: break apart guest mem restoration
Do it in two steps: First restore the mappings, and then in a second step after registering all the mapping to the VM, create the uffd and send the handshake. Signed-off-by: Patrick Roy <[email protected]>
1 parent 8bcfc56 commit 2d491df

File tree

3 files changed

+94
-123
lines changed

3 files changed

+94
-123
lines changed

src/vmm/src/builder.rs

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,17 @@
44
//! Enables pre-boot setup, instantiation and booting of a Firecracker VMM.
55
66
use std::fmt::Debug;
7-
use std::io;
7+
use std::fs::File;
8+
use std::io::{self};
89
#[cfg(feature = "gdb")]
910
use std::sync::mpsc;
1011
use std::sync::{Arc, Mutex};
1112

1213
use event_manager::{MutEventSubscriber, SubscriberOps};
1314
use libc::EFD_NONBLOCK;
1415
use linux_loader::cmdline::Cmdline as LoaderKernelCmdline;
15-
use userfaultfd::Uffd;
16+
#[cfg(target_arch = "aarch64")]
17+
use linux_loader::loader::pe::PE as Loader;
1618
use utils::time::TimestampUs;
1719
#[cfg(target_arch = "aarch64")]
1820
use vm_superio::Rtc;
@@ -50,14 +52,19 @@ use crate::devices::virtio::vsock::{Vsock, VsockUnixBackend};
5052
use crate::gdb;
5153
use crate::initrd::{InitrdConfig, InitrdError};
5254
use crate::logger::{debug, error};
53-
use crate::persist::{MicrovmState, MicrovmStateError};
55+
use crate::persist::{
56+
GuestMemoryFromFileError, MicrovmState, MicrovmStateError, RestoreFromSnapshotGuestMemoryError,
57+
send_uffd_handshake,
58+
};
5459
use crate::resources::VmResources;
5560
use crate::seccomp::BpfThreadMap;
5661
use crate::snapshot::Persist;
5762
use crate::vmm_config::instance_info::InstanceInfo;
5863
use crate::vmm_config::machine_config::MachineConfigError;
64+
use crate::vmm_config::snapshot::{MemBackendConfig, MemBackendType};
5965
use crate::vstate::kvm::Kvm;
60-
use crate::vstate::memory::GuestRegionMmap;
66+
use crate::vstate::memory;
67+
use crate::vstate::memory::GuestMemory;
6168
use crate::vstate::vcpu::{Vcpu, VcpuError};
6269
use crate::vstate::vm::Vm;
6370
use crate::{EventManager, Vmm, VmmError, device_manager};
@@ -409,13 +416,11 @@ pub enum BuildMicrovmFromSnapshotError {
409416
///
410417
/// An `Arc` reference of the built `Vmm` is also plugged in the `EventManager`, while another
411418
/// is returned.
412-
#[allow(clippy::too_many_arguments)]
413419
pub fn build_microvm_from_snapshot(
414420
instance_info: &InstanceInfo,
415421
event_manager: &mut EventManager,
416422
microvm_state: MicrovmState,
417-
guest_memory: Vec<GuestRegionMmap>,
418-
uffd: Option<Uffd>,
423+
mem_backend: &MemBackendConfig,
419424
seccomp_filters: &BpfThreadMap,
420425
vm_resources: &mut VmResources,
421426
) -> Result<Arc<Mutex<Vmm>>, BuildMicrovmFromSnapshotError> {
@@ -429,11 +434,54 @@ pub fn build_microvm_from_snapshot(
429434
)
430435
.map_err(StartMicrovmError::Internal)?;
431436

437+
let track_dirty_pages = vm_resources.machine_config.track_dirty_pages;
438+
439+
let mem_backend_path = &mem_backend.backend_path;
440+
let mem_state = &microvm_state.vm_state.memory;
441+
442+
let guest_memory = match mem_backend.backend_type {
443+
MemBackendType::File => {
444+
if vm_resources.machine_config.huge_pages.is_hugetlbfs() {
445+
panic!(
446+
"{:?}",
447+
RestoreFromSnapshotGuestMemoryError::File(
448+
GuestMemoryFromFileError::HugetlbfsSnapshot,
449+
)
450+
);
451+
}
452+
453+
let mem_file = File::open(mem_backend_path).unwrap();
454+
memory::snapshot_file(mem_file, mem_state.regions(), track_dirty_pages).unwrap()
455+
}
456+
MemBackendType::Uffd => memory::anonymous(
457+
mem_state.regions(),
458+
track_dirty_pages,
459+
vm_resources.machine_config.huge_pages,
460+
)
461+
.unwrap(),
462+
};
463+
432464
vmm.vm
433465
.register_memory_regions(guest_memory)
434466
.map_err(VmmError::Vm)
435467
.map_err(StartMicrovmError::Internal)?;
436-
vmm.uffd = uffd;
468+
469+
vmm.uffd = match mem_backend.backend_type {
470+
MemBackendType::File => None,
471+
MemBackendType::Uffd => {
472+
let (uffd, mut mappings) = vmm.vm.create_uffd().unwrap();
473+
474+
#[allow(deprecated)]
475+
mappings.iter_mut().for_each(|mapping| {
476+
mapping.page_size = vm_resources.machine_config.huge_pages.page_size();
477+
mapping.page_size_kib = vm_resources.machine_config.huge_pages.page_size();
478+
});
479+
480+
send_uffd_handshake(mem_backend_path, &mappings, &uffd).unwrap();
481+
482+
Some(uffd)
483+
}
484+
};
437485

438486
#[cfg(target_arch = "x86_64")]
439487
{

src/vmm/src/persist.rs

Lines changed: 5 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ use std::sync::{Arc, Mutex};
1313

1414
use semver::Version;
1515
use serde::{Deserialize, Serialize};
16-
use userfaultfd::{FeatureFlags, Uffd, UffdBuilder};
1716
use vmm_sys_util::sock_ctrl_msg::ScmSocket;
1817

1918
#[cfg(target_arch = "aarch64")]
@@ -33,10 +32,9 @@ use crate::utils::u64_to_usize;
3332
use crate::vmm_config::boot_source::BootSourceConfig;
3433
use crate::vmm_config::instance_info::InstanceInfo;
3534
use crate::vmm_config::machine_config::{HugePageConfig, MachineConfigError, MachineConfigUpdate};
36-
use crate::vmm_config::snapshot::{CreateSnapshotParams, LoadSnapshotParams, MemBackendType};
35+
use crate::vmm_config::snapshot::{CreateSnapshotParams, LoadSnapshotParams};
3736
use crate::vstate::kvm::KvmState;
38-
use crate::vstate::memory;
39-
use crate::vstate::memory::{GuestMemoryState, GuestRegionMmap, MemoryError};
37+
use crate::vstate::memory::MemoryError;
4038
use crate::vstate::vcpu::{VcpuSendEventError, VcpuState};
4139
use crate::vstate::vm::VmState;
4240
use crate::{EventManager, Vmm, vstate};
@@ -93,7 +91,7 @@ pub struct MicrovmState {
9391
/// E.g. Guest memory contents for a region of `size` bytes can be found in the
9492
/// backend at `offset` bytes from the beginning, and should be copied/populated
9593
/// into `base_host_address`.
96-
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
94+
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
9795
pub struct GuestRegionUffdMapping {
9896
/// Base host virtual address where the guest memory contents for this
9997
/// region should be copied/populated.
@@ -373,37 +371,11 @@ pub fn restore_from_snapshot(
373371
// Some sanity checks before building the microvm.
374372
snapshot_state_sanity_check(&microvm_state)?;
375373

376-
let mem_backend_path = &params.mem_backend.backend_path;
377-
let mem_state = &microvm_state.vm_state.memory;
378-
379-
let (guest_memory, uffd) = match params.mem_backend.backend_type {
380-
MemBackendType::File => {
381-
if vm_resources.machine_config.huge_pages.is_hugetlbfs() {
382-
return Err(RestoreFromSnapshotGuestMemoryError::File(
383-
GuestMemoryFromFileError::HugetlbfsSnapshot,
384-
)
385-
.into());
386-
}
387-
(
388-
guest_memory_from_file(mem_backend_path, mem_state, track_dirty_pages)
389-
.map_err(RestoreFromSnapshotGuestMemoryError::File)?,
390-
None,
391-
)
392-
}
393-
MemBackendType::Uffd => guest_memory_from_uffd(
394-
mem_backend_path,
395-
mem_state,
396-
track_dirty_pages,
397-
vm_resources.machine_config.huge_pages,
398-
)
399-
.map_err(RestoreFromSnapshotGuestMemoryError::Uffd)?,
400-
};
401374
builder::build_microvm_from_snapshot(
402375
instance_info,
403376
event_manager,
404377
microvm_state,
405-
guest_memory,
406-
uffd,
378+
&params.mem_backend,
407379
seccomp_filters,
408380
vm_resources,
409381
)
@@ -448,16 +420,6 @@ pub enum GuestMemoryFromFileError {
448420
HugetlbfsSnapshot,
449421
}
450422

451-
fn guest_memory_from_file(
452-
mem_file_path: &Path,
453-
mem_state: &GuestMemoryState,
454-
track_dirty_pages: bool,
455-
) -> Result<Vec<GuestRegionMmap>, GuestMemoryFromFileError> {
456-
let mem_file = File::open(mem_file_path)?;
457-
let guest_mem = memory::snapshot_file(mem_file, mem_state.regions(), track_dirty_pages)?;
458-
Ok(guest_mem)
459-
}
460-
461423
/// Error type for [`guest_memory_from_uffd`]
462424
#[derive(Debug, thiserror::Error, displaydoc::Display)]
463425
pub enum GuestMemoryFromUffdError {
@@ -473,60 +435,7 @@ pub enum GuestMemoryFromUffdError {
473435
Send(#[from] vmm_sys_util::errno::Error),
474436
}
475437

476-
fn guest_memory_from_uffd(
477-
mem_uds_path: &Path,
478-
mem_state: &GuestMemoryState,
479-
track_dirty_pages: bool,
480-
huge_pages: HugePageConfig,
481-
) -> Result<(Vec<GuestRegionMmap>, Option<Uffd>), GuestMemoryFromUffdError> {
482-
let (guest_memory, backend_mappings) =
483-
create_guest_memory(mem_state, track_dirty_pages, huge_pages)?;
484-
485-
let mut uffd_builder = UffdBuilder::new();
486-
487-
uffd_builder.require_features(FeatureFlags::EVENT_REMOVE);
488-
489-
let uffd = uffd_builder
490-
.close_on_exec(true)
491-
.non_blocking(true)
492-
.user_mode_only(false)
493-
.create()
494-
.map_err(GuestMemoryFromUffdError::Create)?;
495-
496-
for mem_region in guest_memory.iter() {
497-
uffd.register(mem_region.as_ptr().cast(), mem_region.size() as _)
498-
.map_err(GuestMemoryFromUffdError::Register)?;
499-
}
500-
501-
send_uffd_handshake(mem_uds_path, &backend_mappings, &uffd)?;
502-
503-
Ok((guest_memory, Some(uffd)))
504-
}
505-
506-
fn create_guest_memory(
507-
mem_state: &GuestMemoryState,
508-
track_dirty_pages: bool,
509-
huge_pages: HugePageConfig,
510-
) -> Result<(Vec<GuestRegionMmap>, Vec<GuestRegionUffdMapping>), GuestMemoryFromUffdError> {
511-
let guest_memory = memory::anonymous(mem_state.regions(), track_dirty_pages, huge_pages)?;
512-
let mut backend_mappings = Vec::with_capacity(guest_memory.len());
513-
let mut offset = 0;
514-
for mem_region in guest_memory.iter() {
515-
#[allow(deprecated)]
516-
backend_mappings.push(GuestRegionUffdMapping {
517-
base_host_virt_addr: mem_region.as_ptr() as u64,
518-
size: mem_region.size(),
519-
offset,
520-
page_size: huge_pages.page_size(),
521-
page_size_kib: huge_pages.page_size(),
522-
});
523-
offset += mem_region.size() as u64;
524-
}
525-
526-
Ok((guest_memory, backend_mappings))
527-
}
528-
529-
fn send_uffd_handshake(
438+
pub(crate) fn send_uffd_handshake(
530439
mem_uds_path: &Path,
531440
backend_mappings: &[GuestRegionUffdMapping],
532441
uffd: &impl AsRawFd,
@@ -595,7 +504,6 @@ mod tests {
595504
use crate::vmm_config::balloon::BalloonDeviceConfig;
596505
use crate::vmm_config::net::NetworkInterfaceConfig;
597506
use crate::vmm_config::vsock::tests::default_config;
598-
use crate::vstate::memory::GuestMemoryRegionState;
599507

600508
fn default_vmm_with_devices() -> Vmm {
601509
let mut event_manager = EventManager::new().expect("Cannot create EventManager");
@@ -692,24 +600,6 @@ mod tests {
692600
)
693601
}
694602

695-
#[test]
696-
fn test_create_guest_memory() {
697-
let mem_state = GuestMemoryState {
698-
regions: vec![GuestMemoryRegionState {
699-
base_address: 0,
700-
size: 0x20000,
701-
}],
702-
};
703-
704-
let (_, uffd_regions) =
705-
create_guest_memory(&mem_state, false, HugePageConfig::None).unwrap();
706-
707-
assert_eq!(uffd_regions.len(), 1);
708-
assert_eq!(uffd_regions[0].size, 0x20000);
709-
assert_eq!(uffd_regions[0].offset, 0);
710-
assert_eq!(uffd_regions[0].page_size, HugePageConfig::None.page_size());
711-
}
712-
713603
#[test]
714604
fn test_send_uffd_handshake() {
715605
#[allow(deprecated)]

src/vmm/src/vstate/vm.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@ use std::path::Path;
1212
use std::sync::Arc;
1313

1414
use kvm_ioctls::VmFd;
15+
use userfaultfd::{FeatureFlags, Uffd, UffdBuilder};
1516
use vmm_sys_util::eventfd::EventFd;
1617

1718
pub use crate::arch::{ArchVm as Vm, ArchVmError, VmState};
1819
use crate::logger::info;
1920
use crate::persist::CreateSnapshotError;
2021
use crate::utils::u64_to_usize;
2122
use crate::vmm_config::snapshot::SnapshotType;
23+
use crate::persist::GuestRegionUffdMapping;
2224
use crate::vstate::memory::{
2325
GuestMemory, GuestMemoryExtension, GuestMemoryMmap, GuestMemoryRegion, GuestRegionMmap,
2426
KvmRegion,
@@ -185,6 +187,37 @@ impl Vm {
185187
&self.common.guest_memory
186188
}
187189

190+
pub(crate) fn create_uffd(
191+
&self,
192+
) -> Result<(Uffd, Vec<GuestRegionUffdMapping>), userfaultfd::Error> {
193+
let mut uffd_builder = UffdBuilder::new();
194+
let mut mappings = Vec::new();
195+
196+
uffd_builder.require_features(FeatureFlags::EVENT_REMOVE);
197+
198+
let uffd = uffd_builder
199+
.close_on_exec(true)
200+
.non_blocking(true)
201+
.user_mode_only(false)
202+
.create()?;
203+
204+
let mut offset = 0;
205+
206+
for mem_region in self.common.guest_memory.iter() {
207+
uffd.register(mem_region.as_ptr().cast(), mem_region.size() as _)?;
208+
mappings.push(GuestRegionUffdMapping {
209+
base_host_virt_addr: mem_region.as_ptr() as u64,
210+
size: mem_region.size(),
211+
offset,
212+
..Default::default()
213+
});
214+
215+
offset += mem_region.size() as u64;
216+
}
217+
218+
Ok((uffd, mappings))
219+
}
220+
188221
/// Resets the KVM dirty bitmap for each of the guest's memory regions.
189222
pub fn reset_dirty_bitmap(&self) {
190223
self.guest_memory().iter().for_each(|region| {

0 commit comments

Comments
 (0)