@@ -24,6 +24,7 @@ use hyperlight_common::flatbuffer_wrappers::function_types::ReturnValue;
24
24
use hyperlight_common:: flatbuffer_wrappers:: guest_error:: GuestError ;
25
25
use hyperlight_common:: flatbuffer_wrappers:: guest_log_data:: GuestLogData ;
26
26
use hyperlight_common:: flatbuffer_wrappers:: host_function_details:: HostFunctionDetails ;
27
+ use hyperlight_common:: mem:: PAGES_IN_BLOCK ;
27
28
use tracing:: { Span , instrument} ;
28
29
29
30
use super :: exe:: ExeInfo ;
@@ -33,8 +34,9 @@ use super::memory_region::{DEFAULT_GUEST_BLOB_MEM_FLAGS, MemoryRegion, MemoryReg
33
34
use super :: ptr:: { GuestPtr , RawPtr } ;
34
35
use super :: ptr_offset:: Offset ;
35
36
use 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 ;
38
40
use crate :: sandbox:: SandboxConfiguration ;
39
41
use crate :: sandbox:: uninitialized:: GuestBlob ;
40
42
use crate :: { Result , log_then_return, new_error} ;
@@ -75,9 +77,8 @@ pub(crate) struct SandboxMemoryManager<S> {
75
77
pub ( crate ) entrypoint_offset : Offset ,
76
78
/// How many memory regions were mapped after sandbox creation
77
79
pub ( crate ) mapped_rgns : u64 ,
78
- /// A vector of memory snapshots that can be used to save and restore the state of the memory
79
- /// This is used by the Rust Sandbox implementation (rather than the mem_snapshot field above which only exists to support current C API)
80
- snapshots : Arc < Mutex < Vec < SharedMemorySnapshot > > > ,
80
+ /// Shared memory snapshots that can be used to save and restore the state of the memory
81
+ snapshot_manager : Arc < Mutex < Option < SharedMemorySnapshotManager > > > ,
81
82
}
82
83
83
84
impl < S > SandboxMemoryManager < S >
98
99
load_addr,
99
100
entrypoint_offset,
100
101
mapped_rgns : 0 ,
101
- snapshots : Arc :: new ( Mutex :: new ( Vec :: new ( ) ) ) ,
102
+ snapshot_manager : Arc :: new ( Mutex :: new ( None ) ) ,
102
103
}
103
104
}
104
105
@@ -265,54 +266,100 @@ where
265
266
}
266
267
}
267
268
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
269
+ /// this function will create an initial snapshot and then create the SnapshotManager
270
+ pub ( crate ) fn create_initial_snapshot (
271
+ & mut self ,
272
+ vm_dirty_bitmap : & [ u64 ] ,
273
+ host_dirty_page_idx : & [ usize ] ,
274
+ layout : & SandboxMemoryLayout ,
275
+ ) -> Result < ( ) > {
276
+ let mut existing_snapshot_manager = self
277
+ . snapshot_manager
273
278
. try_lock ( )
274
- . map_err ( |e| new_error ! ( "Error locking at {}:{}: {}" , file!( ) , line!( ) , e) ) ?
275
- . push ( snapshot) ;
279
+ . map_err ( |e| new_error ! ( "Error locking at {}:{}: {}" , file!( ) , line!( ) , e) ) ?;
280
+
281
+ if existing_snapshot_manager. is_some ( ) {
282
+ log_then_return ! ( "Snapshot manager already initialized, not creating a new one" ) ;
283
+ }
284
+
285
+ // covert vec of page indices to bitmap
286
+ let mut res = new_page_bitmap ( self . shared_mem . raw_mem_size ( ) , false ) ?;
287
+ for page_idx in host_dirty_page_idx {
288
+ let block_idx = page_idx / PAGES_IN_BLOCK ;
289
+ let bit_idx = page_idx % PAGES_IN_BLOCK ;
290
+ res[ block_idx] |= 1 << bit_idx;
291
+ }
292
+
293
+ // merge the host dirty page map into the dirty bitmap
294
+ let merged = bitmap_union ( & res, vm_dirty_bitmap) ;
295
+
296
+ let snapshot_manager = SharedMemorySnapshotManager :: new (
297
+ & mut self . shared_mem ,
298
+ & merged,
299
+ layout,
300
+ self . mapped_rgns ,
301
+ ) ?;
302
+ existing_snapshot_manager. replace ( snapshot_manager) ;
276
303
Ok ( ( ) )
277
304
}
278
305
279
306
/// this function restores a memory snapshot from the last snapshot in the list but does not pop the snapshot
280
307
/// off the stack
281
308
/// It should be used when you want to restore the state of the memory to a previous state but still want to
282
309
/// 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
310
+ pub ( crate ) fn restore_state_from_last_snapshot ( & mut self , dirty_bitmap : & [ u64 ] ) -> Result < u64 > {
311
+ let mut snapshot_manager = self
312
+ . snapshot_manager
290
313
. try_lock ( )
291
314
. 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 ) ;
315
+
316
+ match snapshot_manager. as_mut ( ) {
317
+ None => {
318
+ log_then_return ! ( "Snapshot manager not initialized" ) ;
319
+ }
320
+ Some ( snapshot_manager) => {
321
+ snapshot_manager. restore_from_snapshot ( & mut self . shared_mem , dirty_bitmap)
322
+ }
295
323
}
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 )
301
324
}
302
325
303
326
/// this function pops the last snapshot off the stack and restores the memory to the previous state
304
327
/// 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
305
328
/// 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
329
+ pub ( crate ) fn pop_and_restore_state_from_snapshot (
330
+ & mut self ,
331
+ dirty_bitmap : & [ u64 ] ,
332
+ ) -> Result < u64 > {
333
+ let mut snapshot_manager = self
334
+ . snapshot_manager
309
335
. 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 ) ;
336
+ . map_err ( |e| new_error ! ( "Error locking at {}:{}: {}" , file!( ) , line!( ) , e) ) ?;
337
+
338
+ match snapshot_manager. as_mut ( ) {
339
+ None => {
340
+ log_then_return ! ( "Snapshot manager not initialized" ) ;
341
+ }
342
+ Some ( snapshot_manager) => snapshot_manager
343
+ . pop_and_restore_state_from_snapshot ( & mut self . shared_mem , dirty_bitmap) ,
344
+ }
345
+ }
346
+
347
+ pub ( crate ) fn push_state ( & mut self , dirty_bitmap : & [ u64 ] ) -> Result < ( ) > {
348
+ let mut snapshot_manager = self
349
+ . snapshot_manager
350
+ . try_lock ( )
351
+ . map_err ( |e| new_error ! ( "Error locking at {}:{}: {}" , file!( ) , line!( ) , e) ) ?;
352
+
353
+ match snapshot_manager. as_mut ( ) {
354
+ None => {
355
+ log_then_return ! ( "Snapshot manager not initialized" ) ;
356
+ }
357
+ Some ( snapshot_manager) => snapshot_manager. create_new_snapshot (
358
+ & mut self . shared_mem ,
359
+ dirty_bitmap,
360
+ self . mapped_rgns ,
361
+ ) ,
314
362
}
315
- self . restore_state_from_last_snapshot ( )
316
363
}
317
364
318
365
/// Sets `addr` to the correct offset in the memory referenced by
@@ -347,7 +394,7 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
347
394
cfg : SandboxConfiguration ,
348
395
exe_info : & mut ExeInfo ,
349
396
guest_blob : Option < & GuestBlob > ,
350
- ) -> Result < Self > {
397
+ ) -> Result < ( Self , DirtyPageTracker ) > {
351
398
let guest_blob_size = guest_blob. map ( |b| b. data . len ( ) ) . unwrap_or ( 0 ) ;
352
399
let guest_blob_mem_flags = guest_blob. map ( |b| b. permissions ) ;
353
400
@@ -360,6 +407,7 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
360
407
guest_blob_mem_flags,
361
408
) ?;
362
409
let mut shared_mem = ExclusiveSharedMemory :: new ( layout. get_memory_size ( ) ?) ?;
410
+ let tracker = shared_mem. start_tracking_dirty_pages ( ) ?;
363
411
364
412
let load_addr: RawPtr = RawPtr :: try_from ( layout. get_guest_code_address ( ) ) ?;
365
413
@@ -378,7 +426,10 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
378
426
& mut shared_mem. as_mut_slice ( ) [ layout. get_guest_code_offset ( ) ..] ,
379
427
) ?;
380
428
381
- Ok ( Self :: new ( layout, shared_mem, load_addr, entrypoint_offset) )
429
+ Ok ( (
430
+ Self :: new ( layout, shared_mem, load_addr, entrypoint_offset) ,
431
+ tracker,
432
+ ) )
382
433
}
383
434
384
435
/// Writes host function details to memory
@@ -440,15 +491,15 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
440
491
load_addr : self . load_addr . clone ( ) ,
441
492
entrypoint_offset : self . entrypoint_offset ,
442
493
mapped_rgns : 0 ,
443
- snapshots : Arc :: new ( Mutex :: new ( Vec :: new ( ) ) ) ,
494
+ snapshot_manager : Arc :: new ( Mutex :: new ( None ) ) ,
444
495
} ,
445
496
SandboxMemoryManager {
446
497
shared_mem : gshm,
447
498
layout : self . layout ,
448
499
load_addr : self . load_addr . clone ( ) ,
449
500
entrypoint_offset : self . entrypoint_offset ,
450
501
mapped_rgns : 0 ,
451
- snapshots : Arc :: new ( Mutex :: new ( Vec :: new ( ) ) ) ,
502
+ snapshot_manager : Arc :: new ( Mutex :: new ( None ) ) ,
452
503
} ,
453
504
)
454
505
}
0 commit comments