Skip to content

Commit 329ed65

Browse files
simongdaviesludfjig
authored andcommitted
Update MultiUseSandbox to use dirty page tracking in restore and devolve methods
Signed-off-by: Simon Davies <[email protected]>
1 parent a07c4e0 commit 329ed65

File tree

4 files changed

+101
-50
lines changed

4 files changed

+101
-50
lines changed

src/hyperlight_host/src/mem/mgr.rs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,6 @@ pub(crate) struct SandboxMemoryManager<S> {
7777
pub(crate) entrypoint_offset: Offset,
7878
/// How many memory regions were mapped after sandbox creation
7979
pub(crate) mapped_rgns: u64,
80-
/// A vector of memory snapshots that can be used to save and restore the state of the memory
81-
/// This is used by the Rust Sandbox implementation (rather than the mem_snapshot field above which only exists to support current C API)
82-
snapshots: Arc<Mutex<Vec<SharedMemorySnapshot>>>,
8380
/// Shared memory snapshots that can be used to save and restore the state of the memory
8481
snapshot_manager: Arc<Mutex<Option<SharedMemorySnapshotManager>>>,
8582
}
@@ -102,7 +99,6 @@ where
10299
load_addr,
103100
entrypoint_offset,
104101
mapped_rgns: 0,
105-
snapshots: Arc::new(Mutex::new(Vec::new())),
106102
snapshot_manager: Arc::new(Mutex::new(None)),
107103
}
108104
}
@@ -311,8 +307,12 @@ where
311307
dirty_bitmap
312308
};
313309

314-
let snapshot_manager =
315-
SharedMemorySnapshotManager::new(&mut self.shared_mem, dirty_page_map, layout)?;
310+
let snapshot_manager = SharedMemorySnapshotManager::new(
311+
&mut self.shared_mem,
312+
dirty_page_map,
313+
layout,
314+
self.mapped_rgns,
315+
)?;
316316
existing_snapshot_manager.replace(snapshot_manager);
317317
Ok(())
318318
}
@@ -321,7 +321,7 @@ where
321321
/// off the stack
322322
/// It should be used when you want to restore the state of the memory to a previous state but still want to
323323
/// retain that state, for example after calling a function in the guest
324-
pub(crate) fn restore_state_from_last_snapshot(&mut self, dirty_bitmap: &[u64]) -> Result<()> {
324+
pub(crate) fn restore_state_from_last_snapshot(&mut self, dirty_bitmap: &[u64]) -> Result<u64> {
325325
let mut snapshot_manager = self
326326
.snapshot_manager
327327
.try_lock()
@@ -343,7 +343,7 @@ where
343343
pub(crate) fn pop_and_restore_state_from_snapshot(
344344
&mut self,
345345
dirty_bitmap: &[u64],
346-
) -> Result<()> {
346+
) -> Result<u64> {
347347
let mut snapshot_manager = self
348348
.snapshot_manager
349349
.try_lock()
@@ -368,9 +368,11 @@ where
368368
None => {
369369
log_then_return!("Snapshot manager not initialized");
370370
}
371-
Some(snapshot_manager) => {
372-
snapshot_manager.create_new_snapshot(&mut self.shared_mem, dirty_bitmap)
373-
}
371+
Some(snapshot_manager) => snapshot_manager.create_new_snapshot(
372+
&mut self.shared_mem,
373+
dirty_bitmap,
374+
self.mapped_rgns,
375+
),
374376
}
375377
}
376378

@@ -504,7 +506,6 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
504506
load_addr: self.load_addr.clone(),
505507
entrypoint_offset: self.entrypoint_offset,
506508
mapped_rgns: 0,
507-
snapshots: Arc::new(Mutex::new(Vec::new())),
508509
snapshot_manager: Arc::new(Mutex::new(None)),
509510
},
510511
SandboxMemoryManager {
@@ -513,7 +514,6 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
513514
load_addr: self.load_addr.clone(),
514515
entrypoint_offset: self.entrypoint_offset,
515516
mapped_rgns: 0,
516-
snapshots: Arc::new(Mutex::new(Vec::new())),
517517
snapshot_manager: Arc::new(Mutex::new(None)),
518518
},
519519
)

src/hyperlight_host/src/mem/page_snapshot.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ pub(super) struct PageSnapshot {
3636
page_index: HashMap<usize, usize>, // page_number -> buffer_offset_in_pages
3737
/// Contiguous buffer containing all the page data
3838
buffer: Vec<u8>,
39+
/// How many non-main-RAM regions were mapped when this snapshot was taken?
40+
mapped_rgns: u64,
3941
}
4042

4143
impl PageSnapshot {
@@ -44,11 +46,16 @@ impl PageSnapshot {
4446
Self {
4547
page_index: HashMap::new(),
4648
buffer: Vec::new(),
49+
mapped_rgns: 0,
4750
}
4851
}
4952

5053
/// Create a snapshot from a list of page numbers with pre-allocated buffer
51-
pub(super) fn with_pages_and_buffer(page_numbers: Vec<usize>, buffer: Vec<u8>) -> Self {
54+
pub(super) fn with_pages_and_buffer(
55+
page_numbers: Vec<usize>,
56+
buffer: Vec<u8>,
57+
mapped_rgns: u64,
58+
) -> Self {
5259
let page_count = page_numbers.len();
5360
let mut page_index = HashMap::with_capacity(page_count);
5461

@@ -57,7 +64,11 @@ impl PageSnapshot {
5764
page_index.insert(page_num, buffer_offset);
5865
}
5966

60-
Self { page_index, buffer }
67+
Self {
68+
page_index,
69+
buffer,
70+
mapped_rgns,
71+
}
6172
}
6273

6374
/// Get page data by page number, returns None if page is not in snapshot
@@ -78,4 +89,9 @@ impl PageSnapshot {
7889
pub(super) fn max_page(&self) -> Option<usize> {
7990
self.page_index.keys().max().copied()
8091
}
92+
93+
/// Get the number of mapped regions when this snapshot was taken
94+
pub(super) fn mapped_rgns(&self) -> u64 {
95+
self.mapped_rgns
96+
}
8197
}

src/hyperlight_host/src/mem/shared_memory_snapshot_manager.rs

Lines changed: 61 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,12 @@ impl SharedMemorySnapshotManager {
4848
shared_mem: &mut S,
4949
dirty_page_map: Option<&Vec<u64>>,
5050
layout: &SandboxMemoryLayout,
51+
mapped_rgns: u64,
5152
) -> Result<Self> {
5253
// Build a snapshot of memory from the dirty_page_map
5354

54-
let diff = Self::build_snapshot_from_dirty_page_map(shared_mem, dirty_page_map)?;
55+
let diff =
56+
Self::build_snapshot_from_dirty_page_map(shared_mem, dirty_page_map, mapped_rgns)?;
5557

5658
// Get the input output buffer details from the layout so that they can be reset to their initial state
5759
let input_data_size_offset = layout.get_input_data_size_offset();
@@ -86,6 +88,7 @@ impl SharedMemorySnapshotManager {
8688
fn build_snapshot_from_dirty_page_map<S: SharedMemory>(
8789
shared_mem: &mut S,
8890
dirty_page_map: Option<&Vec<u64>>,
91+
mapped_rgns: u64,
8992
) -> Result<PageSnapshot> {
9093
// If there is no dirty page map, return an empty snapshot
9194
if dirty_page_map.is_none() {
@@ -146,7 +149,7 @@ impl SharedMemorySnapshotManager {
146149
}
147150

148151
// Create the snapshot with the pre-allocated buffer
149-
let snapshot = PageSnapshot::with_pages_and_buffer(dirty_pages, buffer);
152+
let snapshot = PageSnapshot::with_pages_and_buffer(dirty_pages, buffer, mapped_rgns);
150153

151154
Ok(snapshot)
152155
}
@@ -155,8 +158,10 @@ impl SharedMemorySnapshotManager {
155158
&mut self,
156159
shared_mem: &mut S,
157160
dirty_page_map: Option<&Vec<u64>>,
161+
mapped_rgns: u64,
158162
) -> Result<()> {
159-
let snapshot = Self::build_snapshot_from_dirty_page_map(shared_mem, dirty_page_map)?;
163+
let snapshot =
164+
Self::build_snapshot_from_dirty_page_map(shared_mem, dirty_page_map, mapped_rgns)?;
160165
self.snapshots.push(snapshot);
161166
Ok(())
162167
}
@@ -168,7 +173,7 @@ impl SharedMemorySnapshotManager {
168173
&mut self,
169174
shared_mem: &mut S,
170175
dirty_bitmap: &[u64],
171-
) -> Result<()> {
176+
) -> Result<u64> {
172177
// check the each index in the dirty bitmap and restore only the corresponding pages from the snapshots vector
173178
// starting at the last snapshot look for the page in each snapshot if it exists and restore it
174179
// if it does not exist set the page to zero
@@ -232,14 +237,16 @@ impl SharedMemorySnapshotManager {
232237
self.output_data_buffer_offset,
233238
SandboxMemoryLayout::STACK_POINTER_SIZE_BYTES,
234239
)
235-
})?
240+
})??;
241+
242+
Ok(self.snapshots.last().unwrap().mapped_rgns())
236243
}
237244

238245
pub(super) fn pop_and_restore_state_from_snapshot<S: SharedMemory>(
239246
&mut self,
240247
shared_mem: &mut S,
241248
dirty_bitmap: &[u64],
242-
) -> Result<()> {
249+
) -> Result<u64> {
243250
// Check that there is a snapshot to restore from
244251
if self.snapshots.is_empty() {
245252
return Err(crate::HyperlightError::NoMemorySnapshot);
@@ -256,9 +263,7 @@ impl SharedMemorySnapshotManager {
256263
}
257264

258265
// restore the state from the last snapshot
259-
self.restore_from_snapshot(shared_mem, &merged_bitmap)?;
260-
261-
Ok(())
266+
self.restore_from_snapshot(shared_mem, &merged_bitmap)
262267
}
263268

264269
fn get_bitmap_from_snapshot(&self, snapshot_index: usize) -> Vec<u64> {
@@ -390,9 +395,13 @@ mod tests {
390395
}
391396

392397
// Create snapshot
393-
let mut snapshot_manager =
394-
super::SharedMemorySnapshotManager::new(&mut shared_mem, Some(&dirty_pages), &layout)
395-
.unwrap();
398+
let mut snapshot_manager = super::SharedMemorySnapshotManager::new(
399+
&mut shared_mem,
400+
Some(&dirty_pages),
401+
&layout,
402+
0,
403+
)
404+
.unwrap();
396405

397406
// Modify memory
398407
let modified_data = vec![0xBB; PAGE_SIZE_USIZE];
@@ -468,9 +477,13 @@ mod tests {
468477
}
469478

470479
// Create initial snapshot (State 1)
471-
let mut snapshot_manager =
472-
super::SharedMemorySnapshotManager::new(&mut shared_mem, Some(&dirty_pages), &layout)
473-
.unwrap();
480+
let mut snapshot_manager = super::SharedMemorySnapshotManager::new(
481+
&mut shared_mem,
482+
Some(&dirty_pages),
483+
&layout,
484+
0,
485+
)
486+
.unwrap();
474487

475488
// State 2: Modify and create second snapshot
476489
let tracker2 = shared_mem.start_tracking_dirty_pages().unwrap();
@@ -494,7 +507,7 @@ mod tests {
494507
}
495508

496509
snapshot_manager
497-
.create_new_snapshot(&mut shared_mem, Some(&dirty_pages2))
510+
.create_new_snapshot(&mut shared_mem, Some(&dirty_pages2), 0)
498511
.unwrap();
499512

500513
// State 3: Modify again
@@ -594,9 +607,13 @@ mod tests {
594607
}
595608

596609
// Create snapshot
597-
let mut snapshot_manager =
598-
super::SharedMemorySnapshotManager::new(&mut shared_mem, Some(&dirty_pages), &layout)
599-
.unwrap();
610+
let mut snapshot_manager = super::SharedMemorySnapshotManager::new(
611+
&mut shared_mem,
612+
Some(&dirty_pages),
613+
&layout,
614+
0,
615+
)
616+
.unwrap();
600617

601618
// Modify first and third pages
602619
let modified_data = [vec![0x11; PAGE_SIZE_USIZE], vec![0x22; PAGE_SIZE_USIZE]];
@@ -680,9 +697,13 @@ mod tests {
680697
}
681698
}
682699

683-
let mut snapshot_manager =
684-
super::SharedMemorySnapshotManager::new(&mut shared_mem, Some(&dirty_pages), &layout)
685-
.unwrap();
700+
let mut snapshot_manager = super::SharedMemorySnapshotManager::new(
701+
&mut shared_mem,
702+
Some(&dirty_pages),
703+
&layout,
704+
0,
705+
)
706+
.unwrap();
686707

687708
// Cycle 2: Modify and snapshot
688709
let tracker2 = shared_mem.start_tracking_dirty_pages().unwrap();
@@ -713,7 +734,7 @@ mod tests {
713734
}
714735

715736
snapshot_manager
716-
.create_new_snapshot(&mut shared_mem, Some(&dirty_pages2))
737+
.create_new_snapshot(&mut shared_mem, Some(&dirty_pages2), 0)
717738
.unwrap();
718739

719740
// Cycle 3: Modify again
@@ -814,6 +835,7 @@ mod tests {
814835
&mut shared_mem,
815836
Some(&dirty_pages_snapshot),
816837
&layout,
838+
0,
817839
)
818840
.unwrap();
819841

@@ -966,9 +988,13 @@ mod tests {
966988
}
967989

968990
// Create initial checkpoint
969-
let mut snapshot_manager =
970-
super::SharedMemorySnapshotManager::new(&mut shared_mem, Some(&dirty_pages), &layout)
971-
.unwrap();
991+
let mut snapshot_manager = super::SharedMemorySnapshotManager::new(
992+
&mut shared_mem,
993+
Some(&dirty_pages),
994+
&layout,
995+
0,
996+
)
997+
.unwrap();
972998

973999
// Simulate function call 1: modify pages 0 and 2
9741000
let tracker1 = shared_mem.start_tracking_dirty_pages().unwrap();
@@ -998,7 +1024,7 @@ mod tests {
9981024

9991025
// Checkpoint after function 1
10001026
snapshot_manager
1001-
.create_new_snapshot(&mut shared_mem, Some(&dirty_pages1))
1027+
.create_new_snapshot(&mut shared_mem, Some(&dirty_pages1), 0)
10021028
.unwrap();
10031029

10041030
// Simulate function call 2: modify pages 1 and 3
@@ -1029,7 +1055,7 @@ mod tests {
10291055

10301056
// Checkpoint after function 2
10311057
snapshot_manager
1032-
.create_new_snapshot(&mut shared_mem, Some(&dirty_pages2))
1058+
.create_new_snapshot(&mut shared_mem, Some(&dirty_pages2), 0)
10331059
.unwrap();
10341060

10351061
// Simulate function call 3: modify all pages
@@ -1228,9 +1254,13 @@ mod tests {
12281254
}
12291255

12301256
// Create snapshot
1231-
let mut snapshot_manager =
1232-
super::SharedMemorySnapshotManager::new(&mut shared_mem, Some(&dirty_pages), &layout)
1233-
.unwrap();
1257+
let mut snapshot_manager = super::SharedMemorySnapshotManager::new(
1258+
&mut shared_mem,
1259+
Some(&dirty_pages),
1260+
&layout,
1261+
0,
1262+
)
1263+
.unwrap();
12341264

12351265
// Modify only the dirty pages
12361266
let modified_patterns = [

src/hyperlight_host/src/sandbox/initialized_multi_use.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -270,8 +270,9 @@ impl MultiUseSandbox {
270270
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
271271
pub(crate) fn restore_state(&mut self) -> Result<()> {
272272
let mem_mgr = self.mem_mgr.unwrap_mgr_mut();
273-
let rgns_to_unmap = mem_mgr.restore_state_from_last_snapshot()?;
274-
unsafe { self.vm.unmap_regions(rgns_to_unmap)? };
273+
let dirty_pages = self.vm.get_and_clear_dirty_pages()?;
274+
let rgns_to_umap = mem_mgr.restore_state_from_last_snapshot(&dirty_pages)?;
275+
unsafe { self.vm.unmap_regions(rgns_to_umap)? };
275276
Ok(())
276277
}
277278

@@ -354,10 +355,11 @@ impl DevolvableSandbox<MultiUseSandbox, MultiUseSandbox, Noop<MultiUseSandbox, M
354355
/// The devolve can be used to return the MultiUseSandbox to the state before the code was loaded. Thus avoiding initialisation overhead
355356
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
356357
fn devolve(mut self, _tsn: Noop<MultiUseSandbox, MultiUseSandbox>) -> Result<MultiUseSandbox> {
358+
let dirty_pages = self.vm.get_and_clear_dirty_pages()?;
357359
let rgns_to_unmap = self
358360
.mem_mgr
359361
.unwrap_mgr_mut()
360-
.pop_and_restore_state_from_snapshot()?;
362+
.pop_and_restore_state_from_snapshot(&dirty_pages)?;
361363
unsafe { self.vm.unmap_regions(rgns_to_unmap)? };
362364
Ok(self)
363365
}
@@ -389,7 +391,10 @@ where
389391
let mut ctx = self.new_call_context();
390392
transition_func.call(&mut ctx)?;
391393
let mut sbox = ctx.finish_no_reset();
392-
sbox.mem_mgr.unwrap_mgr_mut().push_state()?;
394+
let vm_dirty_pages = sbox.vm.get_and_clear_dirty_pages()?;
395+
sbox.mem_mgr
396+
.unwrap_mgr_mut()
397+
.push_state(Some(&vm_dirty_pages))?;
393398
Ok(sbox)
394399
}
395400
}

0 commit comments

Comments
 (0)