Skip to content

Commit 66a4287

Browse files
committed
Refactor SandboxMemoryManager to use SharedMemorySnapshotManager for managing memory snapshots and update related methods for improved state management
Signed-off-by: Simon Davies <[email protected]>
1 parent ad09ebb commit 66a4287

File tree

1 file changed

+100
-34
lines changed
  • src/hyperlight_host/src/mem

1 file changed

+100
-34
lines changed

src/hyperlight_host/src/mem/mgr.rs

Lines changed: 100 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use hyperlight_common::flatbuffer_wrappers::function_types::ReturnValue;
2424
use hyperlight_common::flatbuffer_wrappers::guest_error::GuestError;
2525
use hyperlight_common::flatbuffer_wrappers::guest_log_data::GuestLogData;
2626
use hyperlight_common::flatbuffer_wrappers::host_function_details::HostFunctionDetails;
27+
use hyperlight_common::mem::{PAGE_SIZE_USIZE, PAGES_IN_BLOCK};
2728
use tracing::{Span, instrument};
2829

2930
use super::exe::ExeInfo;
@@ -33,8 +34,9 @@ use super::memory_region::{DEFAULT_GUEST_BLOB_MEM_FLAGS, MemoryRegion, MemoryReg
3334
use super::ptr::{GuestPtr, RawPtr};
3435
use super::ptr_offset::Offset;
3536
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;
3840
use crate::sandbox::SandboxConfiguration;
3941
use crate::sandbox::uninitialized::GuestBlob;
4042
use crate::{Result, log_then_return, new_error};
@@ -73,9 +75,8 @@ pub(crate) struct SandboxMemoryManager<S> {
7375
pub(crate) load_addr: RawPtr,
7476
/// Offset for the execution entrypoint from `load_addr`
7577
pub(crate) entrypoint_offset: Offset,
76-
/// A vector of memory snapshots that can be used to save and restore the state of the memory
77-
/// This is used by the Rust Sandbox implementation (rather than the mem_snapshot field above which only exists to support current C API)
78-
snapshots: Arc<Mutex<Vec<SharedMemorySnapshot>>>,
78+
/// Shared memory snapshots that can be used to save and restore the state of the memory
79+
snapshot_manager: Arc<Mutex<Option<SharedMemorySnapshotManager>>>,
7980
}
8081

8182
impl<S> SandboxMemoryManager<S>
@@ -95,7 +96,7 @@ where
9596
shared_mem,
9697
load_addr,
9798
entrypoint_offset,
98-
snapshots: Arc::new(Mutex::new(Vec::new())),
99+
snapshot_manager: Arc::new(Mutex::new(None)),
99100
}
100101
}
101102

@@ -262,48 +263,108 @@ where
262263
}
263264
}
264265

265-
/// this function will create a memory snapshot and push it onto the stack of snapshots
266-
/// It should be used when you want to save the state of the memory, for example, when evolving a sandbox to a new state
267-
pub(crate) fn push_state(&mut self) -> Result<()> {
268-
let snapshot = SharedMemorySnapshot::new(&mut self.shared_mem)?;
269-
self.snapshots
266+
/// this function will create an initial snapshot and then create the SnapshotManager
267+
pub(crate) fn create_initial_snapshot(
268+
&mut self,
269+
dirty_bitmap: Option<&Vec<u64>>,
270+
layout: &SandboxMemoryLayout,
271+
) -> Result<()> {
272+
let mut existing_snapshot_manager = self
273+
.snapshot_manager
270274
.try_lock()
271-
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?
272-
.push(snapshot);
275+
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?;
276+
277+
if existing_snapshot_manager.is_some() {
278+
log_then_return!("Snapshot manager already initialized, not creating a new one");
279+
}
280+
let merged_page_map = if let Some(host_dirty_page_map) = self
281+
.shared_mem
282+
.with_exclusivity(|e| e.get_and_clear_host_dirty_page_map())??
283+
{
284+
// covert vec of page indices to vec of blocks
285+
// memory size is the size of the shared memory minus the 2 guard pages
286+
let mut res = new_page_bitmap(self.shared_mem.mem_size() - 2 * PAGE_SIZE_USIZE, false)?;
287+
for page_idx in host_dirty_page_map {
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 len = res.len();
295+
let merged = bitmap_union(&res, dirty_bitmap.unwrap_or(&vec![0; len]));
296+
Some(merged)
297+
} else {
298+
None
299+
};
300+
301+
let dirty_page_map = if let Some(ref dirty_page_map) = merged_page_map {
302+
Some(dirty_page_map)
303+
} else {
304+
dirty_bitmap
305+
};
306+
307+
let snapshot_manager =
308+
SharedMemorySnapshotManager::new(&mut self.shared_mem, dirty_page_map, layout)?;
309+
existing_snapshot_manager.replace(snapshot_manager);
273310
Ok(())
274311
}
275312

276313
/// this function restores a memory snapshot from the last snapshot in the list but does not pop the snapshot
277314
/// off the stack
278315
/// It should be used when you want to restore the state of the memory to a previous state but still want to
279316
/// retain that state, for example after calling a function in the guest
280-
pub(crate) fn restore_state_from_last_snapshot(&mut self) -> Result<()> {
281-
let mut snapshots = self
282-
.snapshots
317+
pub(crate) fn restore_state_from_last_snapshot(&mut self, dirty_bitmap: &[u64]) -> Result<()> {
318+
let mut snapshot_manager = self
319+
.snapshot_manager
283320
.try_lock()
284321
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?;
285-
let last = snapshots.last_mut();
286-
if last.is_none() {
287-
log_then_return!(NoMemorySnapshot);
322+
323+
match snapshot_manager.as_mut() {
324+
None => {
325+
log_then_return!("Snapshot manager not initialized");
326+
}
327+
Some(snapshot_manager) => {
328+
snapshot_manager.restore_from_snapshot(&mut self.shared_mem, dirty_bitmap)
329+
}
288330
}
289-
#[allow(clippy::unwrap_used)] // We know that last is not None because we checked it above
290-
let snapshot = last.unwrap();
291-
snapshot.restore_from_snapshot(&mut self.shared_mem)
292331
}
293332

294333
/// this function pops the last snapshot off the stack and restores the memory to the previous state
295334
/// 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
296335
/// for example when devolving a sandbox to a previous state.
297-
pub(crate) fn pop_and_restore_state_from_snapshot(&mut self) -> Result<()> {
298-
let last = self
299-
.snapshots
336+
pub(crate) fn pop_and_restore_state_from_snapshot(
337+
&mut self,
338+
dirty_bitmap: &[u64],
339+
) -> Result<()> {
340+
let mut snapshot_manager = self
341+
.snapshot_manager
300342
.try_lock()
301-
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?
302-
.pop();
303-
if last.is_none() {
304-
log_then_return!(NoMemorySnapshot);
343+
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?;
344+
345+
match snapshot_manager.as_mut() {
346+
None => {
347+
log_then_return!("Snapshot manager not initialized");
348+
}
349+
Some(snapshot_manager) => snapshot_manager
350+
.pop_and_restore_state_from_snapshot(&mut self.shared_mem, dirty_bitmap),
351+
}
352+
}
353+
354+
pub(crate) fn push_state(&mut self, dirty_bitmap: Option<&Vec<u64>>) -> Result<()> {
355+
let mut snapshot_manager = self
356+
.snapshot_manager
357+
.try_lock()
358+
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?;
359+
360+
match snapshot_manager.as_mut() {
361+
None => {
362+
log_then_return!("Snapshot manager not initialized");
363+
}
364+
Some(snapshot_manager) => {
365+
snapshot_manager.create_new_snapshot(&mut self.shared_mem, dirty_bitmap)
366+
}
305367
}
306-
self.restore_state_from_last_snapshot()
307368
}
308369

309370
/// Sets `addr` to the correct offset in the memory referenced by
@@ -338,7 +399,7 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
338399
cfg: SandboxConfiguration,
339400
exe_info: &mut ExeInfo,
340401
guest_blob: Option<&GuestBlob>,
341-
) -> Result<Self> {
402+
) -> Result<(Self, DirtyPageTracker)> {
342403
let guest_blob_size = guest_blob.map(|b| b.data.len()).unwrap_or(0);
343404
let guest_blob_mem_flags = guest_blob.map(|b| b.permissions);
344405

@@ -351,6 +412,7 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
351412
guest_blob_mem_flags,
352413
)?;
353414
let mut shared_mem = ExclusiveSharedMemory::new(layout.get_memory_size()?)?;
415+
let tracker = shared_mem.start_tracking_dirty_pages()?;
354416

355417
let load_addr: RawPtr = RawPtr::try_from(layout.get_guest_code_address())?;
356418

@@ -369,7 +431,10 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
369431
&mut shared_mem.as_mut_slice()[layout.get_guest_code_offset()..],
370432
)?;
371433

372-
Ok(Self::new(layout, shared_mem, load_addr, entrypoint_offset))
434+
Ok((
435+
Self::new(layout, shared_mem, load_addr, entrypoint_offset),
436+
tracker,
437+
))
373438
}
374439

375440
/// Writes host function details to memory
@@ -405,6 +470,7 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
405470
host_function_call_buffer.as_slice(),
406471
self.layout.host_function_definitions_buffer_offset,
407472
)?;
473+
408474
Ok(())
409475
}
410476

@@ -430,14 +496,14 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
430496
layout: self.layout,
431497
load_addr: self.load_addr.clone(),
432498
entrypoint_offset: self.entrypoint_offset,
433-
snapshots: Arc::new(Mutex::new(Vec::new())),
499+
snapshot_manager: Arc::new(Mutex::new(None)),
434500
},
435501
SandboxMemoryManager {
436502
shared_mem: gshm,
437503
layout: self.layout,
438504
load_addr: self.load_addr.clone(),
439505
entrypoint_offset: self.entrypoint_offset,
440-
snapshots: Arc::new(Mutex::new(Vec::new())),
506+
snapshot_manager: Arc::new(Mutex::new(None)),
441507
},
442508
)
443509
}

0 commit comments

Comments
 (0)