Skip to content

Commit 1d43aec

Browse files
simongdaviesludfjig
authored andcommitted
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 ec75763 commit 1d43aec

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};
@@ -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

8387
impl<S> SandboxMemoryManager<S>
@@ -99,6 +103,7 @@ where
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

Comments
 (0)