Skip to content

Commit 6da550e

Browse files
Merge branch 'abk/get_memory_region-bug' into 'master'
RUN-656: Fix `PageMap::get_memory_region` bug This fixes a small bug in `PageMap::get_memory_region` where the reported region of zeros at the end of the `PageMap` would extend into regions actually coming from the page delta at indices below the requested `page_index`. This bug has no effect on production code because the method is only used for prefetching canister memory and we only ever prefetch indices greater than the faulting page (so the false pages at lower indices were ignored anyway). But it needs to be fixed for prefetching to work in both directions. See merge request dfinity-lab/public/ic!12674
2 parents a2c8c6c + 790eab9 commit 6da550e

File tree

3 files changed

+92
-4
lines changed

3 files changed

+92
-4
lines changed

rs/replicated_state/src/page_map.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ impl std::fmt::Display for PersistenceError {
250250

251251
/// A wrapper around the raw file descriptor to be used for memory mapping the
252252
/// file into the Wasm heap while executing a canister.
253-
#[derive(Serialize, Deserialize, Clone, Debug)]
253+
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
254254
pub struct FileDescriptor {
255255
pub fd: RawFd,
256256
}
@@ -266,6 +266,7 @@ pub type FileOffset = off_t;
266266
/// - The page maps to the checkpoint file.
267267
/// - The page is in the page delta of the current `PageMap`. In this case the
268268
/// range is a singleton and its contents need to be copied out.
269+
#[derive(Debug, PartialEq)]
269270
pub enum MemoryRegion<'a> {
270271
Zeros(Range<PageIndex>),
271272
BackedByFile(Range<PageIndex>, FileDescriptor),

rs/replicated_state/src/page_map/checkpoint.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ impl Mapping {
130130
let num_pages = (self.mmap.len() / PAGE_SIZE) as u64;
131131
if page_index.get() >= num_pages {
132132
MemoryRegion::Zeros(Range {
133-
start: PageIndex::new(num_pages),
133+
start: PageIndex::new(std::cmp::max(num_pages, page_range.start.get())),
134134
end: page_range.end,
135135
})
136136
} else {

rs/replicated_state/src/page_map/tests.rs

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ use super::{
44
Buffer, FileDescriptor, PageAllocator, PageAllocatorRegistry, PageDelta, PageIndex, PageMap,
55
PageMapSerialization,
66
};
7-
use crate::page_map::TestPageAllocatorFileDescriptorImpl;
7+
use crate::page_map::{MemoryRegion, TestPageAllocatorFileDescriptorImpl};
88
use ic_sys::PAGE_SIZE;
99
use ic_types::{Height, MAX_STABLE_MEMORY_IN_BYTES};
1010
use nix::unistd::dup;
11-
use std::fs::OpenOptions;
1211
use std::sync::Arc;
12+
use std::{fs::OpenOptions, ops::Range};
1313

1414
fn assert_equal_page_maps(page_map1: &PageMap, page_map2: &PageMap) {
1515
assert_eq!(page_map1.num_host_pages(), page_map2.num_host_pages());
@@ -405,3 +405,90 @@ fn calc_dirty_pages_matches_actual_change() {
405405
)
406406
.unwrap()
407407
}
408+
409+
#[test]
410+
fn zeros_region_after_delta() {
411+
let mut page_map = PageMap::new_for_testing();
412+
let tmp = tempfile::Builder::new()
413+
.prefix("checkpoints")
414+
.tempdir()
415+
.unwrap();
416+
let heap_file = tmp.path().join("heap");
417+
let pages = &[(PageIndex::new(1), &[1u8; PAGE_SIZE])];
418+
page_map.update(pages);
419+
420+
page_map.persist_delta(&heap_file).unwrap();
421+
422+
let mut page_map = PageMap::open(
423+
&heap_file,
424+
Height::new(0),
425+
Arc::new(TestPageAllocatorFileDescriptorImpl::new()),
426+
)
427+
.unwrap();
428+
429+
let zero_range = page_map.get_memory_region(PageIndex::new(5));
430+
assert_eq!(
431+
MemoryRegion::Zeros(Range {
432+
start: PageIndex::new(2),
433+
end: PageIndex::new(u64::MAX)
434+
}),
435+
zero_range
436+
);
437+
438+
let pages = &[(PageIndex::new(3), &[1u8; PAGE_SIZE])];
439+
page_map.update(pages);
440+
441+
let zero_range = page_map.get_memory_region(PageIndex::new(5));
442+
assert_eq!(
443+
MemoryRegion::Zeros(Range {
444+
start: PageIndex::new(4),
445+
end: PageIndex::new(u64::MAX)
446+
}),
447+
zero_range
448+
);
449+
}
450+
451+
#[test]
452+
fn zeros_region_within_delta() {
453+
let mut page_map = PageMap::new_for_testing();
454+
let tmp = tempfile::Builder::new()
455+
.prefix("checkpoints")
456+
.tempdir()
457+
.unwrap();
458+
let heap_file = tmp.path().join("heap");
459+
let pages = &[(PageIndex::new(1), &[1u8; PAGE_SIZE])];
460+
page_map.update(pages);
461+
462+
page_map.persist_delta(&heap_file).unwrap();
463+
464+
let mut page_map = PageMap::open(
465+
&heap_file,
466+
Height::new(0),
467+
Arc::new(TestPageAllocatorFileDescriptorImpl::new()),
468+
)
469+
.unwrap();
470+
471+
let zero_range = page_map.get_memory_region(PageIndex::new(5));
472+
assert_eq!(
473+
MemoryRegion::Zeros(Range {
474+
start: PageIndex::new(2),
475+
end: PageIndex::new(u64::MAX)
476+
}),
477+
zero_range
478+
);
479+
480+
let pages = &[
481+
(PageIndex::new(3), &[1u8; PAGE_SIZE]),
482+
(PageIndex::new(10), &[1u8; PAGE_SIZE]),
483+
];
484+
page_map.update(pages);
485+
486+
let zero_range = page_map.get_memory_region(PageIndex::new(5));
487+
assert_eq!(
488+
MemoryRegion::Zeros(Range {
489+
start: PageIndex::new(4),
490+
end: PageIndex::new(10)
491+
}),
492+
zero_range
493+
);
494+
}

0 commit comments

Comments
 (0)