Skip to content

Commit cb4e7cf

Browse files
committed
refactor: Make guest_memory_from_uffd unit-testable
Factor out the parts of the function that do not require holding a valid UFFD object into their own functions for which we can then write unittests. Also write the unittests. Signed-off-by: Patrick Roy <[email protected]>
1 parent ba78599 commit cb4e7cf

File tree

1 file changed

+95
-11
lines changed

1 file changed

+95
-11
lines changed

src/vmm/src/persist.rs

Lines changed: 95 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ pub struct MicrovmState {
9393
/// E.g. Guest memory contents for a region of `size` bytes can be found in the
9494
/// backend at `offset` bytes from the beginning, and should be copied/populated
9595
/// into `base_host_address`.
96-
#[derive(Clone, Debug, Serialize)]
96+
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
9797
pub struct GuestRegionUffdMapping {
9898
/// Base host virtual address where the guest memory contents for this
9999
/// region should be copied/populated.
@@ -516,7 +516,8 @@ fn guest_memory_from_uffd(
516516
enable_balloon: bool,
517517
huge_pages: HugePageConfig,
518518
) -> Result<(GuestMemoryMmap, Option<Uffd>), GuestMemoryFromUffdError> {
519-
let guest_memory = GuestMemoryMmap::from_state(None, mem_state, track_dirty_pages, huge_pages)?;
519+
let (guest_memory, backend_mappings) =
520+
create_guest_memory(mem_state, track_dirty_pages, huge_pages)?;
520521

521522
let mut uffd_builder = UffdBuilder::new();
522523

@@ -533,24 +534,43 @@ fn guest_memory_from_uffd(
533534
.create()
534535
.map_err(GuestMemoryFromUffdError::Create)?;
535536

537+
for mem_region in guest_memory.iter() {
538+
uffd.register(mem_region.as_ptr().cast(), mem_region.size() as _)
539+
.map_err(GuestMemoryFromUffdError::Register)?;
540+
}
541+
542+
send_uffd_handshake(mem_uds_path, &backend_mappings, &uffd)?;
543+
544+
Ok((guest_memory, Some(uffd)))
545+
}
546+
547+
fn create_guest_memory(
548+
mem_state: &GuestMemoryState,
549+
track_dirty_pages: bool,
550+
huge_pages: HugePageConfig,
551+
) -> Result<(GuestMemoryMmap, Vec<GuestRegionUffdMapping>), GuestMemoryFromUffdError> {
552+
let guest_memory = GuestMemoryMmap::from_state(None, mem_state, track_dirty_pages, huge_pages)?;
536553
let mut backend_mappings = Vec::with_capacity(guest_memory.num_regions());
537554
for (mem_region, state_region) in guest_memory.iter().zip(mem_state.regions.iter()) {
538-
let host_base_addr = mem_region.as_ptr();
539-
let size = mem_region.size();
540-
541-
uffd.register(host_base_addr.cast(), size as _)
542-
.map_err(GuestMemoryFromUffdError::Register)?;
543555
backend_mappings.push(GuestRegionUffdMapping {
544-
base_host_virt_addr: host_base_addr as u64,
545-
size,
556+
base_host_virt_addr: mem_region.as_ptr() as u64,
557+
size: mem_region.size(),
546558
offset: state_region.offset,
547559
page_size_kib: huge_pages.page_size_kib(),
548560
});
549561
}
550562

563+
Ok((guest_memory, backend_mappings))
564+
}
565+
566+
fn send_uffd_handshake(
567+
mem_uds_path: &Path,
568+
backend_mappings: &[GuestRegionUffdMapping],
569+
uffd: &impl AsRawFd,
570+
) -> Result<(), GuestMemoryFromUffdError> {
551571
// This is safe to unwrap() because we control the contents of the vector
552572
// (i.e GuestRegionUffdMapping entries).
553-
let backend_mappings = serde_json::to_string(&backend_mappings).unwrap();
573+
let backend_mappings = serde_json::to_string(backend_mappings).unwrap();
554574

555575
let socket = UnixStream::connect(mem_uds_path)?;
556576
socket.send_with_fd(
@@ -588,11 +608,13 @@ fn guest_memory_from_uffd(
588608
uffd.as_raw_fd(),
589609
)?;
590610

591-
Ok((guest_memory, Some(uffd)))
611+
Ok(())
592612
}
593613

594614
#[cfg(test)]
595615
mod tests {
616+
use std::os::unix::net::UnixListener;
617+
596618
use utils::tempfile::TempFile;
597619

598620
use super::*;
@@ -607,6 +629,7 @@ mod tests {
607629
use crate::vmm_config::balloon::BalloonDeviceConfig;
608630
use crate::vmm_config::net::NetworkInterfaceConfig;
609631
use crate::vmm_config::vsock::tests::default_config;
632+
use crate::vstate::memory::GuestMemoryRegionState;
610633
use crate::Vmm;
611634

612635
fn default_vmm_with_devices() -> Vmm {
@@ -700,4 +723,65 @@ mod tests {
700723
microvm_state.device_states
701724
)
702725
}
726+
727+
#[test]
728+
fn test_create_guest_memory() {
729+
let mem_state = GuestMemoryState {
730+
regions: vec![GuestMemoryRegionState {
731+
base_address: 0,
732+
size: 0x20000,
733+
offset: 0x10000,
734+
}],
735+
};
736+
737+
let (_, uffd_regions) =
738+
create_guest_memory(&mem_state, false, HugePageConfig::None).unwrap();
739+
740+
assert_eq!(uffd_regions.len(), 1);
741+
assert_eq!(uffd_regions[0].size, 0x20000);
742+
assert_eq!(uffd_regions[0].offset, 0x10000);
743+
assert_eq!(
744+
uffd_regions[0].page_size_kib,
745+
HugePageConfig::None.page_size_kib()
746+
);
747+
}
748+
749+
#[test]
750+
fn test_send_uffd_handshake() {
751+
let uffd_regions = vec![
752+
GuestRegionUffdMapping {
753+
base_host_virt_addr: 0,
754+
size: 0x100000,
755+
offset: 0,
756+
page_size_kib: HugePageConfig::None.page_size_kib(),
757+
},
758+
GuestRegionUffdMapping {
759+
base_host_virt_addr: 0x100000,
760+
size: 0x200000,
761+
offset: 0,
762+
page_size_kib: HugePageConfig::Hugetlbfs2M.page_size_kib(),
763+
},
764+
];
765+
766+
let uds_path = TempFile::new().unwrap();
767+
let uds_path = uds_path.as_path();
768+
std::fs::remove_file(uds_path).unwrap();
769+
770+
let listener = UnixListener::bind(uds_path).expect("Cannot bind to socket path");
771+
772+
send_uffd_handshake(uds_path, &uffd_regions, &std::io::stdin()).unwrap();
773+
774+
let (stream, _) = listener.accept().expect("Cannot listen on UDS socket");
775+
776+
let mut message_buf = vec![0u8; 1024];
777+
let (bytes_read, _) = stream
778+
.recv_with_fd(&mut message_buf[..])
779+
.expect("Cannot recv_with_fd");
780+
message_buf.resize(bytes_read, 0);
781+
782+
let deserialized: Vec<GuestRegionUffdMapping> =
783+
serde_json::from_slice(&message_buf).unwrap();
784+
785+
assert_eq!(uffd_regions, deserialized);
786+
}
703787
}

0 commit comments

Comments
 (0)