@@ -307,6 +307,7 @@ pub(crate) struct HypervLinuxDriver {
307307 vcpu_fd : VcpuFd ,
308308 entrypoint : u64 ,
309309 mem_regions : Vec < MemoryRegion > ,
310+ n_initial_regions : usize ,
310311 orig_rsp : GuestPtr ,
311312 interrupt_handle : Arc < LinuxInterruptHandle > ,
312313
@@ -417,10 +418,20 @@ impl HypervLinuxDriver {
417418 // the downside of doing this here is that the call to get_dirty_log will takes longer as the number of pages increase
418419 // but for larger sandboxes its easily cheaper than copying all the pages
419420
420- #[ cfg( mshv2) ]
421- vm_fd. get_dirty_log ( base_pfn, total_size, CLEAR_DIRTY_BIT_FLAG ) ?;
422- #[ cfg( mshv3) ]
423- vm_fd. get_dirty_log ( base_pfn, total_size, MSHV_GPAP_ACCESS_OP_CLEAR as u8 ) ?;
421+ // Clear dirty bits for each memory region separately since they may not be contiguous
422+ for region in & mem_regions {
423+ let mshv_region: mshv_user_mem_region = region. to_owned ( ) . into ( ) ;
424+ let region_size = region. guest_region . len ( ) ;
425+
426+ #[ cfg( mshv2) ]
427+ vm_fd. get_dirty_log ( mshv_region. guest_pfn , region_size, CLEAR_DIRTY_BIT_FLAG ) ?;
428+ #[ cfg( mshv3) ]
429+ vm_fd. get_dirty_log (
430+ mshv_region. guest_pfn ,
431+ region_size,
432+ MSHV_GPAP_ACCESS_OP_CLEAR as u8 ,
433+ ) ?;
434+ }
424435
425436 let interrupt_handle = Arc :: new ( LinuxInterruptHandle {
426437 running : AtomicU64 :: new ( 0 ) ,
@@ -452,6 +463,7 @@ impl HypervLinuxDriver {
452463 page_size : 0 ,
453464 vm_fd,
454465 vcpu_fd,
466+ n_initial_regions : mem_regions. len ( ) ,
455467 mem_regions,
456468 entrypoint : entrypoint_ptr. absolute ( ) ?,
457469 orig_rsp : rsp_ptr,
@@ -887,7 +899,8 @@ impl Hypervisor for HypervLinuxDriver {
887899 self . interrupt_handle . clone ( )
888900 }
889901
890- fn get_and_clear_dirty_pages ( & mut self ) -> Result < Vec < u64 > > {
902+ // TODO: Implement getting additional host-mapped dirty pages.
903+ fn get_and_clear_dirty_pages ( & mut self ) -> Result < ( Vec < u64 > , Option < Vec < Vec < u64 > > > ) > {
891904 let first_mshv_region: mshv_user_mem_region = self
892905 . mem_regions
893906 . first ( )
@@ -896,16 +909,38 @@ impl Hypervisor for HypervLinuxDriver {
896909 ) ) ?
897910 . to_owned ( )
898911 . into ( ) ;
899- let total_size = self . mem_regions . iter ( ) . map ( |r| r. guest_region . len ( ) ) . sum ( ) ;
900- let res = self . vm_fd . get_dirty_log (
912+
913+ let n_contiguous = self
914+ . mem_regions
915+ . windows ( 2 )
916+ . take_while ( |window| window[ 0 ] . guest_region . end == window[ 1 ] . guest_region . start )
917+ . count ( )
918+ + 1 ; // +1 because windows(2) gives us n-1 pairs for n regions
919+
920+ if n_contiguous != self . n_initial_regions {
921+ return Err ( new_error ! (
922+ "get_and_clear_dirty_pages: not all regions are contiguous, expected {} but got {}" ,
923+ self . n_initial_regions,
924+ n_contiguous
925+ ) ) ;
926+ }
927+
928+ let sandbox_total_size = self
929+ . mem_regions
930+ . iter ( )
931+ . take ( n_contiguous)
932+ . map ( |r| r. guest_region . len ( ) )
933+ . sum ( ) ;
934+
935+ let sandbox_dirty_pages = self . vm_fd . get_dirty_log (
901936 first_mshv_region. guest_pfn ,
902- total_size ,
937+ sandbox_total_size ,
903938 #[ cfg( mshv2) ]
904939 CLEAR_DIRTY_BIT_FLAG ,
905940 #[ cfg( mshv3) ]
906941 ( MSHV_GPAP_ACCESS_OP_CLEAR as u8 ) ,
907942 ) ?;
908- Ok ( res )
943+ Ok ( ( sandbox_dirty_pages , None ) )
909944 }
910945
911946 #[ cfg( crashdump) ]
@@ -1158,7 +1193,8 @@ mod tests {
11581193 return ;
11591194 }
11601195 const MEM_SIZE : usize = 0x3000 ;
1161- let gm = shared_mem_with_code ( CODE . as_slice ( ) , MEM_SIZE , 0 ) . unwrap ( ) ;
1196+ let mut gm = shared_mem_with_code ( CODE . as_slice ( ) , MEM_SIZE , 0 ) . unwrap ( ) ;
1197+ gm. stop_tracking_dirty_pages ( ) . unwrap ( ) ;
11621198 let rsp_ptr = GuestPtr :: try_from ( 0 ) . unwrap ( ) ;
11631199 let pml4_ptr = GuestPtr :: try_from ( 0 ) . unwrap ( ) ;
11641200 let entrypoint_ptr = GuestPtr :: try_from ( 0 ) . unwrap ( ) ;
0 commit comments