@@ -24,6 +24,7 @@ use hyperlight_common::flatbuffer_wrappers::function_types::ReturnValue;
2424use hyperlight_common:: flatbuffer_wrappers:: guest_error:: GuestError ;
2525use hyperlight_common:: flatbuffer_wrappers:: guest_log_data:: GuestLogData ;
2626use hyperlight_common:: flatbuffer_wrappers:: host_function_details:: HostFunctionDetails ;
27+ use hyperlight_common:: mem:: { PAGE_SIZE_USIZE , PAGES_IN_BLOCK } ;
2728use tracing:: { Span , instrument} ;
2829
2930use super :: exe:: ExeInfo ;
@@ -33,8 +34,9 @@ use super::memory_region::{DEFAULT_GUEST_BLOB_MEM_FLAGS, MemoryRegion, MemoryReg
3334use super :: ptr:: { GuestPtr , RawPtr } ;
3435use super :: ptr_offset:: Offset ;
3536use super :: shared_mem:: { ExclusiveSharedMemory , GuestSharedMemory , HostSharedMemory , SharedMemory } ;
36- use super :: shared_mem_snapshot:: SharedMemorySnapshot ;
37- use crate :: HyperlightError :: NoMemorySnapshot ;
37+ use super :: shared_memory_snapshot_manager:: SharedMemorySnapshotManager ;
38+ use crate :: mem:: bitmap:: { bitmap_union, new_page_bitmap} ;
39+ use crate :: mem:: dirty_page_tracking:: DirtyPageTracker ;
3840use crate :: sandbox:: SandboxConfiguration ;
3941use crate :: sandbox:: uninitialized:: GuestBlob ;
4042use crate :: { Result , log_then_return, new_error} ;
@@ -78,6 +80,8 @@ pub(crate) struct SandboxMemoryManager<S> {
7880 /// A vector of memory snapshots that can be used to save and restore the state of the memory
7981 /// This is used by the Rust Sandbox implementation (rather than the mem_snapshot field above which only exists to support current C API)
8082 snapshots : Arc < Mutex < Vec < SharedMemorySnapshot > > > ,
83+ /// Shared memory snapshots that can be used to save and restore the state of the memory
84+ snapshot_manager : Arc < Mutex < Option < SharedMemorySnapshotManager > > > ,
8185}
8286
8387impl < S > SandboxMemoryManager < S >
99103 entrypoint_offset,
100104 mapped_rgns : 0 ,
101105 snapshots : Arc :: new ( Mutex :: new ( Vec :: new ( ) ) ) ,
106+ snapshot_manager : Arc :: new ( Mutex :: new ( None ) ) ,
102107 }
103108 }
104109
@@ -265,54 +270,108 @@ where
265270 }
266271 }
267272
268- /// this function will create a memory snapshot and push it onto the stack of snapshots
269- /// It should be used when you want to save the state of the memory, for example, when evolving a sandbox to a new state
270- pub ( crate ) fn push_state ( & mut self ) -> Result < ( ) > {
271- let snapshot = SharedMemorySnapshot :: new ( & mut self . shared_mem , self . mapped_rgns ) ?;
272- self . snapshots
273+ /// this function will create an initial snapshot and then create the SnapshotManager
274+ pub ( crate ) fn create_initial_snapshot (
275+ & mut self ,
276+ dirty_bitmap : Option < & Vec < u64 > > ,
277+ layout : & SandboxMemoryLayout ,
278+ ) -> Result < ( ) > {
279+ let mut existing_snapshot_manager = self
280+ . snapshot_manager
273281 . try_lock ( )
274- . map_err ( |e| new_error ! ( "Error locking at {}:{}: {}" , file!( ) , line!( ) , e) ) ?
275- . push ( snapshot) ;
282+ . map_err ( |e| new_error ! ( "Error locking at {}:{}: {}" , file!( ) , line!( ) , e) ) ?;
283+
284+ if existing_snapshot_manager. is_some ( ) {
285+ log_then_return ! ( "Snapshot manager already initialized, not creating a new one" ) ;
286+ }
287+ let merged_page_map = if let Some ( host_dirty_page_map) = self
288+ . shared_mem
289+ . with_exclusivity ( |e| e. get_and_clear_host_dirty_page_map ( ) ) ??
290+ {
291+ // covert vec of page indices to vec of blocks
292+ // memory size is the size of the shared memory minus the 2 guard pages
293+ let mut res = new_page_bitmap ( self . shared_mem . mem_size ( ) - 2 * PAGE_SIZE_USIZE , false ) ?;
294+ for page_idx in host_dirty_page_map {
295+ let block_idx = page_idx / PAGES_IN_BLOCK ;
296+ let bit_idx = page_idx % PAGES_IN_BLOCK ;
297+ res[ block_idx] |= 1 << bit_idx;
298+ }
299+
300+ // merge the host dirty page map into the dirty bitmap
301+ let len = res. len ( ) ;
302+ let merged = bitmap_union ( & res, dirty_bitmap. unwrap_or ( & vec ! [ 0 ; len] ) ) ;
303+ Some ( merged)
304+ } else {
305+ None
306+ } ;
307+
308+ let dirty_page_map = if let Some ( ref dirty_page_map) = merged_page_map {
309+ Some ( dirty_page_map)
310+ } else {
311+ dirty_bitmap
312+ } ;
313+
314+ let snapshot_manager =
315+ SharedMemorySnapshotManager :: new ( & mut self . shared_mem , dirty_page_map, layout) ?;
316+ existing_snapshot_manager. replace ( snapshot_manager) ;
276317 Ok ( ( ) )
277318 }
278319
279320 /// this function restores a memory snapshot from the last snapshot in the list but does not pop the snapshot
280321 /// off the stack
281322 /// It should be used when you want to restore the state of the memory to a previous state but still want to
282323 /// retain that state, for example after calling a function in the guest
283- ///
284- /// Returns the number of memory regions mapped into the sandbox
285- /// that need to be unmapped in order for the restore to be
286- /// completed.
287- pub ( crate ) fn restore_state_from_last_snapshot ( & mut self ) -> Result < u64 > {
288- let mut snapshots = self
289- . snapshots
324+ pub ( crate ) fn restore_state_from_last_snapshot ( & mut self , dirty_bitmap : & [ u64 ] ) -> Result < ( ) > {
325+ let mut snapshot_manager = self
326+ . snapshot_manager
290327 . try_lock ( )
291328 . map_err ( |e| new_error ! ( "Error locking at {}:{}: {}" , file!( ) , line!( ) , e) ) ?;
292- let last = snapshots. last_mut ( ) ;
293- if last. is_none ( ) {
294- log_then_return ! ( NoMemorySnapshot ) ;
329+
330+ match snapshot_manager. as_mut ( ) {
331+ None => {
332+ log_then_return ! ( "Snapshot manager not initialized" ) ;
333+ }
334+ Some ( snapshot_manager) => {
335+ snapshot_manager. restore_from_snapshot ( & mut self . shared_mem , dirty_bitmap)
336+ }
295337 }
296- #[ allow( clippy:: unwrap_used) ] // We know that last is not None because we checked it above
297- let snapshot = last. unwrap ( ) ;
298- let old_rgns = self . mapped_rgns ;
299- self . mapped_rgns = snapshot. restore_from_snapshot ( & mut self . shared_mem ) ?;
300- Ok ( old_rgns - self . mapped_rgns )
301338 }
302339
303340 /// this function pops the last snapshot off the stack and restores the memory to the previous state
304341 /// It should be used when you want to restore the state of the memory to a previous state and do not need to retain that state
305342 /// for example when devolving a sandbox to a previous state.
306- pub ( crate ) fn pop_and_restore_state_from_snapshot ( & mut self ) -> Result < u64 > {
307- let last = self
308- . snapshots
343+ pub ( crate ) fn pop_and_restore_state_from_snapshot (
344+ & mut self ,
345+ dirty_bitmap : & [ u64 ] ,
346+ ) -> Result < ( ) > {
347+ let mut snapshot_manager = self
348+ . snapshot_manager
309349 . try_lock ( )
310- . map_err ( |e| new_error ! ( "Error locking at {}:{}: {}" , file!( ) , line!( ) , e) ) ?
311- . pop ( ) ;
312- if last. is_none ( ) {
313- log_then_return ! ( NoMemorySnapshot ) ;
350+ . map_err ( |e| new_error ! ( "Error locking at {}:{}: {}" , file!( ) , line!( ) , e) ) ?;
351+
352+ match snapshot_manager. as_mut ( ) {
353+ None => {
354+ log_then_return ! ( "Snapshot manager not initialized" ) ;
355+ }
356+ Some ( snapshot_manager) => snapshot_manager
357+ . pop_and_restore_state_from_snapshot ( & mut self . shared_mem , dirty_bitmap) ,
358+ }
359+ }
360+
361+ pub ( crate ) fn push_state ( & mut self , dirty_bitmap : Option < & Vec < u64 > > ) -> Result < ( ) > {
362+ let mut snapshot_manager = self
363+ . snapshot_manager
364+ . try_lock ( )
365+ . map_err ( |e| new_error ! ( "Error locking at {}:{}: {}" , file!( ) , line!( ) , e) ) ?;
366+
367+ match snapshot_manager. as_mut ( ) {
368+ None => {
369+ log_then_return ! ( "Snapshot manager not initialized" ) ;
370+ }
371+ Some ( snapshot_manager) => {
372+ snapshot_manager. create_new_snapshot ( & mut self . shared_mem , dirty_bitmap)
373+ }
314374 }
315- self . restore_state_from_last_snapshot ( )
316375 }
317376
318377 /// Sets `addr` to the correct offset in the memory referenced by
@@ -347,7 +406,7 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
347406 cfg : SandboxConfiguration ,
348407 exe_info : & mut ExeInfo ,
349408 guest_blob : Option < & GuestBlob > ,
350- ) -> Result < Self > {
409+ ) -> Result < ( Self , DirtyPageTracker ) > {
351410 let guest_blob_size = guest_blob. map ( |b| b. data . len ( ) ) . unwrap_or ( 0 ) ;
352411 let guest_blob_mem_flags = guest_blob. map ( |b| b. permissions ) ;
353412
@@ -360,6 +419,7 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
360419 guest_blob_mem_flags,
361420 ) ?;
362421 let mut shared_mem = ExclusiveSharedMemory :: new ( layout. get_memory_size ( ) ?) ?;
422+ let tracker = shared_mem. start_tracking_dirty_pages ( ) ?;
363423
364424 let load_addr: RawPtr = RawPtr :: try_from ( layout. get_guest_code_address ( ) ) ?;
365425
@@ -378,7 +438,10 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
378438 & mut shared_mem. as_mut_slice ( ) [ layout. get_guest_code_offset ( ) ..] ,
379439 ) ?;
380440
381- Ok ( Self :: new ( layout, shared_mem, load_addr, entrypoint_offset) )
441+ Ok ( (
442+ Self :: new ( layout, shared_mem, load_addr, entrypoint_offset) ,
443+ tracker,
444+ ) )
382445 }
383446
384447 /// Writes host function details to memory
@@ -414,6 +477,7 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
414477 host_function_call_buffer. as_slice ( ) ,
415478 self . layout . host_function_definitions_buffer_offset ,
416479 ) ?;
480+
417481 Ok ( ( ) )
418482 }
419483
@@ -441,6 +505,7 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
441505 entrypoint_offset : self . entrypoint_offset ,
442506 mapped_rgns : 0 ,
443507 snapshots : Arc :: new ( Mutex :: new ( Vec :: new ( ) ) ) ,
508+ snapshot_manager : Arc :: new ( Mutex :: new ( None ) ) ,
444509 } ,
445510 SandboxMemoryManager {
446511 shared_mem : gshm,
@@ -449,6 +514,7 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
449514 entrypoint_offset : self . entrypoint_offset ,
450515 mapped_rgns : 0 ,
451516 snapshots : Arc :: new ( Mutex :: new ( Vec :: new ( ) ) ) ,
517+ snapshot_manager : Arc :: new ( Mutex :: new ( None ) ) ,
452518 } ,
453519 )
454520 }
0 commit comments