mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2024-11-24 20:48:55 +00:00
bd8d3cc856
Opeing a file on the monitor process is blocking the upcoming minijail migration. Open the swap file on the main process and pass the file descriptor to the monitor process. Using a single swap file and mmap different ranges of it for different regions is efficient. BUG=b:269372016 TEST=cargo test -p swap Change-Id: I4be150419d263ebe76ad00dae2cafce6e8660cbd Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/4253294 Reviewed-by: Daniel Verkamp <dverkamp@chromium.org> Commit-Queue: Shin Kawamura <kawasin@google.com> Reviewed-by: David Stevens <stevensd@chromium.org>
866 lines
28 KiB
Rust
866 lines
28 KiB
Rust
// Copyright 2022 The ChromiumOS Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
//! Integration tests for [PageHandler]. these are more than unit tests since [PageHandler] rely on
|
|
//! the userfaultfd(2) kernel feature.
|
|
|
|
#![cfg(unix)]
|
|
|
|
mod common;
|
|
|
|
use std::array;
|
|
use std::ops::Range;
|
|
use std::thread;
|
|
use std::time;
|
|
|
|
use base::pagesize;
|
|
use base::MappedRegion;
|
|
use base::MemoryMappingBuilder;
|
|
use base::SharedMemory;
|
|
use common::*;
|
|
use swap::page_handler::Error;
|
|
use swap::page_handler::PageHandler;
|
|
use swap::userfaultfd::register_regions;
|
|
use swap::userfaultfd::unregister_regions;
|
|
use swap::worker::Worker;
|
|
|
|
const HUGEPAGE_SIZE: usize = 2 * 1024 * 1024; // 2MB
|
|
|
|
#[test]
|
|
fn create_success() {
|
|
let worker = Worker::new(2, 2);
|
|
let file = tempfile::tempfile().unwrap();
|
|
let shm = create_shared_memory("shm", 6 * pagesize());
|
|
let base_addr = shm.base_addr();
|
|
|
|
let result = PageHandler::create(
|
|
&file,
|
|
&[
|
|
base_addr..(base_addr + 3 * pagesize()),
|
|
(base_addr + 3 * pagesize())..(base_addr + 6 * pagesize()),
|
|
],
|
|
worker.channel.clone(),
|
|
);
|
|
|
|
assert!(result.is_ok());
|
|
worker.close();
|
|
}
|
|
|
|
#[test]
|
|
fn create_partially_overlap() {
|
|
let worker = Worker::new(2, 2);
|
|
let file = tempfile::tempfile().unwrap();
|
|
let shm = create_shared_memory("shm", 3 * pagesize());
|
|
let base_addr = shm.base_addr();
|
|
|
|
for range in [
|
|
// the same address range
|
|
base_addr..(base_addr + 3 * pagesize()),
|
|
// left of the existing region overlaps
|
|
(base_addr - pagesize())..(base_addr + pagesize()),
|
|
// new region is inside
|
|
(base_addr + pagesize())..(base_addr + 2 * pagesize()),
|
|
// right of the existing region overlaps
|
|
(base_addr + 2 * pagesize())..(base_addr + 4 * pagesize()),
|
|
// new region covers whole the existing region
|
|
(base_addr - pagesize())..(base_addr + 4 * pagesize()),
|
|
] {
|
|
let result = PageHandler::create(
|
|
&file,
|
|
&[base_addr..(base_addr + 3 * pagesize()), range],
|
|
worker.channel.clone(),
|
|
);
|
|
assert_eq!(result.is_err(), true);
|
|
match result {
|
|
Err(Error::RegionOverlap(_, _)) => {}
|
|
_ => {
|
|
unreachable!("not overlap")
|
|
}
|
|
}
|
|
}
|
|
worker.close();
|
|
}
|
|
|
|
#[test]
|
|
fn create_invalid_range() {
|
|
let worker = Worker::new(2, 2);
|
|
let file = tempfile::tempfile().unwrap();
|
|
let shm = create_shared_memory("shm", 6 * pagesize());
|
|
let base_addr = shm.base_addr();
|
|
|
|
let result = PageHandler::create(
|
|
&file,
|
|
&[base_addr..(base_addr - pagesize())],
|
|
worker.channel.clone(),
|
|
);
|
|
|
|
assert!(result.is_err());
|
|
worker.close();
|
|
}
|
|
|
|
fn wait_thread_with_timeout<T>(join_handle: thread::JoinHandle<T>, timeout_millis: u64) -> T {
|
|
for _ in 0..timeout_millis {
|
|
if join_handle.is_finished() {
|
|
return join_handle.join().unwrap();
|
|
}
|
|
thread::sleep(time::Duration::from_millis(1));
|
|
}
|
|
panic!("thread join timeout");
|
|
}
|
|
|
|
#[test]
|
|
fn handle_page_fault_zero_success() {
|
|
let worker = Worker::new(2, 2);
|
|
let file = tempfile::tempfile().unwrap();
|
|
let uffd = create_uffd_for_test();
|
|
let shm = create_shared_memory("shm", 3 * pagesize());
|
|
let base_addr = shm.base_addr();
|
|
let regions = [base_addr..(base_addr + 3 * pagesize())];
|
|
let mut page_handler = PageHandler::create(&file, ®ions, worker.channel.clone()).unwrap();
|
|
unsafe { register_regions(®ions, array::from_ref(&uffd)) }.unwrap();
|
|
|
|
page_handler.handle_page_fault(&uffd, base_addr).unwrap();
|
|
page_handler
|
|
.handle_page_fault(&uffd, base_addr + pagesize() + 1)
|
|
.unwrap();
|
|
page_handler
|
|
.handle_page_fault(&uffd, base_addr + 3 * pagesize() - 1)
|
|
.unwrap();
|
|
|
|
// read values on another thread to avoid blocking forever
|
|
let join_handle = thread::spawn(move || {
|
|
let mut result = Vec::new();
|
|
for i in 0..(3 * pagesize()) {
|
|
let ptr = shm.mmap.as_ptr() as usize + i;
|
|
unsafe {
|
|
result.push(*(ptr as *mut u8));
|
|
}
|
|
}
|
|
result
|
|
});
|
|
|
|
let result = wait_thread_with_timeout(join_handle, 100);
|
|
|
|
assert_eq!(result, vec![0; 3 * pagesize()]);
|
|
worker.close();
|
|
}
|
|
|
|
#[test]
|
|
fn handle_page_fault_invalid_address() {
|
|
let worker = Worker::new(2, 2);
|
|
let file = tempfile::tempfile().unwrap();
|
|
let uffd = create_uffd_for_test();
|
|
let shm = create_shared_memory("shm", 3 * pagesize());
|
|
let base_addr = shm.base_addr();
|
|
let regions = [base_addr..(base_addr + 3 * pagesize())];
|
|
let mut page_handler = PageHandler::create(&file, ®ions, worker.channel.clone()).unwrap();
|
|
unsafe { register_regions(®ions, array::from_ref(&uffd)) }.unwrap();
|
|
|
|
assert_eq!(
|
|
page_handler
|
|
.handle_page_fault(&uffd, base_addr - 1)
|
|
.is_err(),
|
|
true
|
|
);
|
|
assert_eq!(
|
|
page_handler
|
|
.handle_page_fault(&uffd, base_addr + 3 * pagesize())
|
|
.is_err(),
|
|
true
|
|
);
|
|
worker.close();
|
|
}
|
|
|
|
#[test]
|
|
fn handle_page_fault_duplicated_page_fault() {
|
|
let worker = Worker::new(2, 2);
|
|
let file = tempfile::tempfile().unwrap();
|
|
let uffd = create_uffd_for_test();
|
|
let shm = create_shared_memory("shm", 3 * pagesize());
|
|
let base_addr = shm.base_addr();
|
|
let regions = [base_addr..(base_addr + 3 * pagesize())];
|
|
let mut page_handler = PageHandler::create(&file, ®ions, worker.channel.clone()).unwrap();
|
|
unsafe { register_regions(®ions, array::from_ref(&uffd)) }.unwrap();
|
|
|
|
assert_eq!(
|
|
page_handler.handle_page_fault(&uffd, base_addr).is_ok(),
|
|
true
|
|
);
|
|
assert_eq!(
|
|
page_handler.handle_page_fault(&uffd, base_addr + 1).is_ok(),
|
|
true
|
|
);
|
|
worker.close();
|
|
}
|
|
|
|
#[test]
|
|
fn handle_page_remove_success() {
|
|
let worker = Worker::new(2, 2);
|
|
let file = tempfile::tempfile().unwrap();
|
|
let uffd = create_uffd_for_test();
|
|
let shm = create_shared_memory("shm", 3 * pagesize());
|
|
let base_addr = shm.base_addr();
|
|
let regions = [base_addr..(base_addr + 3 * pagesize())];
|
|
let mut page_handler = PageHandler::create(&file, ®ions, worker.channel.clone()).unwrap();
|
|
unsafe { register_regions(®ions, array::from_ref(&uffd)) }.unwrap();
|
|
|
|
// fill the first page with zero
|
|
page_handler.handle_page_fault(&uffd, base_addr).unwrap();
|
|
// write value on another thread to avoid blocking forever
|
|
let join_handle = thread::spawn(move || {
|
|
let ptr = base_addr as *mut u8;
|
|
unsafe {
|
|
*ptr = 1;
|
|
}
|
|
});
|
|
wait_thread_with_timeout(join_handle, 100);
|
|
let second_page_addr = base_addr + pagesize();
|
|
page_handler
|
|
.handle_page_remove(base_addr, second_page_addr)
|
|
.unwrap();
|
|
unsafe {
|
|
libc::madvise(
|
|
base_addr as *mut libc::c_void,
|
|
pagesize(),
|
|
libc::MADV_REMOVE,
|
|
);
|
|
}
|
|
// fill the first page with zero again
|
|
page_handler.handle_page_fault(&uffd, base_addr).unwrap();
|
|
// read value on another thread to avoid blocking forever
|
|
let join_handle = thread::spawn(move || {
|
|
let ptr = base_addr as *mut u8;
|
|
unsafe { *ptr }
|
|
});
|
|
|
|
assert_eq!(wait_thread_with_timeout(join_handle, 100), 0);
|
|
worker.close();
|
|
}
|
|
|
|
#[test]
|
|
fn handle_page_remove_invalid_address() {
|
|
let worker = Worker::new(2, 2);
|
|
let file = tempfile::tempfile().unwrap();
|
|
let uffd = create_uffd_for_test();
|
|
let shm = create_shared_memory("shm", 3 * pagesize());
|
|
let base_addr = shm.base_addr();
|
|
let regions = [base_addr..(base_addr + 3 * pagesize())];
|
|
let mut page_handler = PageHandler::create(&file, ®ions, worker.channel.clone()).unwrap();
|
|
unsafe { register_regions(®ions, array::from_ref(&uffd)) }.unwrap();
|
|
|
|
page_handler.handle_page_fault(&uffd, base_addr).unwrap();
|
|
page_handler
|
|
.handle_page_fault(&uffd, base_addr + pagesize())
|
|
.unwrap();
|
|
page_handler
|
|
.handle_page_fault(&uffd, base_addr + 2 * pagesize())
|
|
.unwrap();
|
|
assert_eq!(
|
|
page_handler
|
|
.handle_page_remove(base_addr - 1, base_addr + 3 * pagesize())
|
|
.is_err(),
|
|
true
|
|
);
|
|
assert_eq!(
|
|
page_handler
|
|
.handle_page_remove(base_addr, base_addr + 3 * pagesize() + 1)
|
|
.is_err(),
|
|
true
|
|
);
|
|
// remove for whole region should succeed.
|
|
assert_eq!(
|
|
page_handler
|
|
.handle_page_remove(base_addr, base_addr + 3 * pagesize())
|
|
.is_ok(),
|
|
true
|
|
);
|
|
worker.close();
|
|
}
|
|
|
|
#[test]
|
|
fn move_to_staging_data_written_before_enabling() {
|
|
let worker = Worker::new(2, 2);
|
|
let uffd = create_uffd_for_test();
|
|
let file = tempfile::tempfile().unwrap();
|
|
let shm = SharedMemory::new("shm", 6 * pagesize() as u64).unwrap();
|
|
let mmap1 = MemoryMappingBuilder::new(3 * pagesize())
|
|
.from_shared_memory(&shm)
|
|
.build()
|
|
.unwrap();
|
|
let mmap2 = MemoryMappingBuilder::new(3 * pagesize())
|
|
.from_shared_memory(&shm)
|
|
.offset(3 * pagesize() as u64)
|
|
.build()
|
|
.unwrap();
|
|
let base_addr1 = mmap1.as_ptr() as usize;
|
|
let base_addr2 = mmap2.as_ptr() as usize;
|
|
|
|
let regions = [
|
|
base_addr1..(base_addr1 + 3 * pagesize()),
|
|
base_addr2..(base_addr2 + 3 * pagesize()),
|
|
];
|
|
let mut page_handler = PageHandler::create(&file, ®ions, worker.channel.clone()).unwrap();
|
|
// write data before registering to userfaultfd
|
|
unsafe {
|
|
for i in base_addr1 + pagesize()..base_addr1 + 2 * pagesize() {
|
|
*(i as *mut u8) = 1;
|
|
}
|
|
for i in base_addr2 + pagesize()..base_addr2 + 2 * pagesize() {
|
|
*(i as *mut u8) = 2;
|
|
}
|
|
for i in base_addr2 + 2 * pagesize()..base_addr2 + 3 * pagesize() {
|
|
*(i as *mut u8) = 3;
|
|
}
|
|
}
|
|
unsafe { register_regions(®ions, array::from_ref(&uffd)) }.unwrap();
|
|
|
|
unsafe {
|
|
page_handler.move_to_staging(base_addr1, &shm, 0).unwrap();
|
|
page_handler
|
|
.move_to_staging(base_addr2, &shm, 3 * pagesize() as u64)
|
|
.unwrap();
|
|
}
|
|
worker.channel.wait_complete();
|
|
// page faults on all pages.
|
|
for i in 0..3 {
|
|
page_handler
|
|
.handle_page_fault(&uffd, base_addr1 + i * pagesize())
|
|
.unwrap();
|
|
page_handler
|
|
.handle_page_fault(&uffd, base_addr2 + i * pagesize())
|
|
.unwrap();
|
|
}
|
|
|
|
// read values on another thread to avoid blocking forever
|
|
let join_handle = thread::spawn(move || {
|
|
let mut result = Vec::new();
|
|
for i in 0..3 {
|
|
for j in 0..pagesize() {
|
|
let ptr = (base_addr1 + i * pagesize() + j) as *mut u8;
|
|
unsafe {
|
|
result.push(*ptr);
|
|
}
|
|
}
|
|
}
|
|
for i in 0..3 {
|
|
for j in 0..pagesize() {
|
|
let ptr = (base_addr2 + i * pagesize() + j) as *mut u8;
|
|
unsafe {
|
|
result.push(*ptr);
|
|
}
|
|
}
|
|
}
|
|
result
|
|
});
|
|
let result = wait_thread_with_timeout(join_handle, 100);
|
|
let values: Vec<u8> = vec![0, 1, 0, 0, 2, 3];
|
|
for (i, v) in values.iter().enumerate() {
|
|
for j in 0..pagesize() {
|
|
assert_eq!(&result[i * pagesize() + j], v);
|
|
}
|
|
}
|
|
worker.close();
|
|
}
|
|
|
|
fn page_idx_range(start_addr: usize, end_addr: usize) -> Range<usize> {
|
|
(start_addr / pagesize())..(end_addr / pagesize())
|
|
}
|
|
|
|
fn page_idx_to_addr(page_idx: usize) -> usize {
|
|
page_idx * pagesize()
|
|
}
|
|
|
|
#[test]
|
|
fn move_to_staging_hugepage_chunks() {
|
|
let worker = Worker::new(2, 2);
|
|
let uffd = create_uffd_for_test();
|
|
let file = tempfile::tempfile().unwrap();
|
|
let shm = SharedMemory::new("shm", 10 * HUGEPAGE_SIZE as u64).unwrap();
|
|
let mmap1 = MemoryMappingBuilder::new(5 * HUGEPAGE_SIZE)
|
|
.from_shared_memory(&shm)
|
|
.build()
|
|
.unwrap();
|
|
let mmap2 = MemoryMappingBuilder::new(5 * HUGEPAGE_SIZE)
|
|
.from_shared_memory(&shm)
|
|
.offset(5 * HUGEPAGE_SIZE as u64)
|
|
.build()
|
|
.unwrap();
|
|
let base_addr1 = mmap1.as_ptr() as usize;
|
|
let base_addr2 = mmap2.as_ptr() as usize;
|
|
|
|
let regions = [
|
|
base_addr1..(base_addr1 + 5 * HUGEPAGE_SIZE),
|
|
base_addr2..(base_addr2 + 5 * HUGEPAGE_SIZE),
|
|
];
|
|
let mut page_handler = PageHandler::create(&file, ®ions, worker.channel.clone()).unwrap();
|
|
// write data before registering to userfaultfd
|
|
unsafe {
|
|
for i in page_idx_range(base_addr1 + pagesize(), base_addr1 + 3 * pagesize()) {
|
|
*(page_idx_to_addr(i) as *mut u8) = 1;
|
|
}
|
|
for i in page_idx_range(
|
|
base_addr1 + HUGEPAGE_SIZE - pagesize(),
|
|
base_addr1 + HUGEPAGE_SIZE + pagesize(),
|
|
) {
|
|
*(page_idx_to_addr(i) as *mut u8) = 2;
|
|
}
|
|
for i in page_idx_range(
|
|
base_addr1 + 2 * HUGEPAGE_SIZE + pagesize(),
|
|
base_addr1 + 3 * HUGEPAGE_SIZE + pagesize(),
|
|
) {
|
|
*(page_idx_to_addr(i) as *mut u8) = 3;
|
|
}
|
|
for i in page_idx_range(base_addr2 + HUGEPAGE_SIZE, base_addr2 + 2 * HUGEPAGE_SIZE) {
|
|
*(page_idx_to_addr(i) as *mut u8) = 4;
|
|
}
|
|
for i in page_idx_range(
|
|
base_addr2 + 2 * HUGEPAGE_SIZE + pagesize(),
|
|
base_addr2 + 5 * HUGEPAGE_SIZE - pagesize(),
|
|
) {
|
|
*(page_idx_to_addr(i) as *mut u8) = 5;
|
|
}
|
|
}
|
|
unsafe { register_regions(®ions, array::from_ref(&uffd)) }.unwrap();
|
|
|
|
unsafe {
|
|
page_handler.move_to_staging(base_addr1, &shm, 0).unwrap();
|
|
page_handler
|
|
.move_to_staging(base_addr2, &shm, 5 * HUGEPAGE_SIZE as u64)
|
|
.unwrap();
|
|
}
|
|
worker.channel.wait_complete();
|
|
// page faults on all pages.
|
|
for i in 0..5 * HUGEPAGE_SIZE / pagesize() {
|
|
page_handler
|
|
.handle_page_fault(&uffd, base_addr1 + i * pagesize())
|
|
.unwrap();
|
|
page_handler
|
|
.handle_page_fault(&uffd, base_addr2 + i * pagesize())
|
|
.unwrap();
|
|
}
|
|
|
|
// read values on another thread to avoid blocking forever
|
|
let join_handle = thread::spawn(move || {
|
|
let mut result = Vec::new();
|
|
for i in page_idx_range(base_addr1, base_addr1 + 5 * HUGEPAGE_SIZE) {
|
|
let ptr = (page_idx_to_addr(i)) as *mut u8;
|
|
unsafe {
|
|
result.push(*ptr);
|
|
}
|
|
}
|
|
for i in page_idx_range(base_addr2, base_addr2 + 5 * HUGEPAGE_SIZE) {
|
|
let ptr = (page_idx_to_addr(i)) as *mut u8;
|
|
unsafe {
|
|
result.push(*ptr);
|
|
}
|
|
}
|
|
result
|
|
});
|
|
let result = wait_thread_with_timeout(join_handle, 100);
|
|
assert_eq!(result[0], 0);
|
|
assert_eq!(result[1], 1);
|
|
assert_eq!(result[2], 1);
|
|
for i in page_idx_range(3 * pagesize(), HUGEPAGE_SIZE - pagesize()) {
|
|
assert_eq!(result[i], 0);
|
|
}
|
|
for i in page_idx_range(HUGEPAGE_SIZE - pagesize(), HUGEPAGE_SIZE + pagesize()) {
|
|
assert_eq!(result[i], 2);
|
|
}
|
|
for i in page_idx_range(HUGEPAGE_SIZE + pagesize(), 2 * HUGEPAGE_SIZE + pagesize()) {
|
|
assert_eq!(result[i], 0);
|
|
}
|
|
for i in page_idx_range(
|
|
2 * HUGEPAGE_SIZE + pagesize(),
|
|
3 * HUGEPAGE_SIZE + pagesize(),
|
|
) {
|
|
assert_eq!(result[i], 3);
|
|
}
|
|
for i in page_idx_range(3 * HUGEPAGE_SIZE + pagesize(), 6 * HUGEPAGE_SIZE) {
|
|
assert_eq!(result[i], 0);
|
|
}
|
|
for i in page_idx_range(6 * HUGEPAGE_SIZE, 7 * HUGEPAGE_SIZE) {
|
|
assert_eq!(result[i], 4);
|
|
}
|
|
for i in page_idx_range(7 * HUGEPAGE_SIZE, 7 * HUGEPAGE_SIZE + pagesize()) {
|
|
assert_eq!(result[i], 0);
|
|
}
|
|
for i in page_idx_range(
|
|
7 * HUGEPAGE_SIZE + pagesize(),
|
|
10 * HUGEPAGE_SIZE - pagesize(),
|
|
) {
|
|
assert_eq!(result[i], 5);
|
|
}
|
|
for i in page_idx_range(10 * HUGEPAGE_SIZE - pagesize(), 10 * HUGEPAGE_SIZE) {
|
|
assert_eq!(result[i], 0);
|
|
}
|
|
worker.close();
|
|
}
|
|
|
|
#[test]
|
|
fn move_to_staging_invalid_base_addr() {
|
|
let worker = Worker::new(2, 2);
|
|
let uffd = create_uffd_for_test();
|
|
let file = tempfile::tempfile().unwrap();
|
|
let shm = create_shared_memory("shm1", 3 * pagesize());
|
|
let base_addr = shm.base_addr();
|
|
let regions = [base_addr..(base_addr + 3 * pagesize())];
|
|
let mut page_handler = PageHandler::create(&file, ®ions, worker.channel.clone()).unwrap();
|
|
unsafe { register_regions(®ions, array::from_ref(&uffd)) }.unwrap();
|
|
|
|
// the base_addr is within the region
|
|
assert_eq!(
|
|
unsafe { page_handler.move_to_staging(base_addr + pagesize(), &shm.shm, 0,) }.is_err(),
|
|
true
|
|
);
|
|
// the base_addr is outside of the region
|
|
assert_eq!(
|
|
unsafe { page_handler.move_to_staging(base_addr - pagesize(), &shm.shm, 0,) }.is_err(),
|
|
true
|
|
);
|
|
worker.close();
|
|
}
|
|
|
|
fn swap_out_all(page_handler: &mut PageHandler) {
|
|
while page_handler.swap_out(1024 * 1024).unwrap() != 0 {}
|
|
}
|
|
|
|
#[test]
|
|
fn swap_out_success() {
|
|
let worker = Worker::new(2, 2);
|
|
let uffd = create_uffd_for_test();
|
|
let file = tempfile::tempfile().unwrap();
|
|
let shm = SharedMemory::new("shm", 6 * pagesize() as u64).unwrap();
|
|
let mmap1 = MemoryMappingBuilder::new(3 * pagesize())
|
|
.from_shared_memory(&shm)
|
|
.build()
|
|
.unwrap();
|
|
let mmap2 = MemoryMappingBuilder::new(3 * pagesize())
|
|
.from_shared_memory(&shm)
|
|
.offset(3 * pagesize() as u64)
|
|
.build()
|
|
.unwrap();
|
|
let base_addr1 = mmap1.as_ptr() as usize;
|
|
let base_addr2 = mmap2.as_ptr() as usize;
|
|
let regions = [
|
|
base_addr1..(base_addr1 + 3 * pagesize()),
|
|
base_addr2..(base_addr2 + 3 * pagesize()),
|
|
];
|
|
let mut page_handler = PageHandler::create(&file, ®ions, worker.channel.clone()).unwrap();
|
|
// write data before registering to userfaultfd
|
|
unsafe {
|
|
for i in base_addr1 + pagesize()..base_addr1 + 2 * pagesize() {
|
|
*(i as *mut u8) = 1;
|
|
}
|
|
for i in base_addr2 + pagesize()..base_addr2 + 2 * pagesize() {
|
|
*(i as *mut u8) = 2;
|
|
}
|
|
}
|
|
unsafe { register_regions(®ions, array::from_ref(&uffd)) }.unwrap();
|
|
|
|
unsafe {
|
|
page_handler.move_to_staging(base_addr1, &shm, 0).unwrap();
|
|
page_handler
|
|
.move_to_staging(base_addr2, &shm, 3 * pagesize() as u64)
|
|
.unwrap();
|
|
}
|
|
worker.channel.wait_complete();
|
|
swap_out_all(&mut page_handler);
|
|
// page faults on all pages. page 0 and page 2 will be swapped in from the file. page 1 will
|
|
// be filled with zero.
|
|
for i in 0..3 {
|
|
page_handler
|
|
.handle_page_fault(&uffd, base_addr1 + i * pagesize())
|
|
.unwrap();
|
|
page_handler
|
|
.handle_page_fault(&uffd, base_addr2 + i * pagesize())
|
|
.unwrap();
|
|
}
|
|
|
|
// read values on another thread to avoid blocking forever
|
|
let join_handle = thread::spawn(move || {
|
|
let mut result = Vec::new();
|
|
for i in 0..3 {
|
|
for j in 0..pagesize() {
|
|
let ptr = (base_addr1 + i * pagesize() + j) as *mut u8;
|
|
unsafe {
|
|
result.push(*ptr);
|
|
}
|
|
}
|
|
}
|
|
for i in 0..3 {
|
|
for j in 0..pagesize() {
|
|
let ptr = (base_addr2 + i * pagesize() + j) as *mut u8;
|
|
unsafe {
|
|
result.push(*ptr);
|
|
}
|
|
}
|
|
}
|
|
result
|
|
});
|
|
let result = wait_thread_with_timeout(join_handle, 100);
|
|
let values: Vec<u8> = vec![0, 1, 0, 0, 2, 0];
|
|
for (i, v) in values.iter().enumerate() {
|
|
for j in 0..pagesize() {
|
|
assert_eq!(&result[i * pagesize() + j], v);
|
|
}
|
|
}
|
|
worker.close();
|
|
}
|
|
|
|
#[test]
|
|
fn swap_out_handled_page() {
|
|
let worker = Worker::new(2, 2);
|
|
let uffd = create_uffd_for_test();
|
|
let file = tempfile::tempfile().unwrap();
|
|
let shm = SharedMemory::new("shm", 6 * pagesize() as u64).unwrap();
|
|
let mmap1 = MemoryMappingBuilder::new(3 * pagesize())
|
|
.from_shared_memory(&shm)
|
|
.build()
|
|
.unwrap();
|
|
let base_addr1 = mmap1.as_ptr() as usize;
|
|
|
|
let regions = [base_addr1..(base_addr1 + 3 * pagesize())];
|
|
let mut page_handler = PageHandler::create(&file, ®ions, worker.channel.clone()).unwrap();
|
|
// write data before registering to userfaultfd
|
|
unsafe {
|
|
for i in base_addr1 + pagesize()..base_addr1 + 2 * pagesize() {
|
|
*(i as *mut u8) = 1;
|
|
}
|
|
}
|
|
unsafe { register_regions(®ions, array::from_ref(&uffd)) }.unwrap();
|
|
|
|
unsafe {
|
|
page_handler.move_to_staging(base_addr1, &shm, 0).unwrap();
|
|
}
|
|
worker.channel.wait_complete();
|
|
// page in before swap_out()
|
|
page_handler
|
|
.handle_page_fault(&uffd, base_addr1 + pagesize())
|
|
.unwrap();
|
|
swap_out_all(&mut page_handler);
|
|
|
|
// read values on another thread to avoid blocking forever
|
|
let join_handle = thread::spawn(move || {
|
|
let mut result = Vec::new();
|
|
for i in 0..pagesize() {
|
|
let ptr = (base_addr1 + pagesize() + i) as *mut u8;
|
|
unsafe {
|
|
result.push(*ptr);
|
|
}
|
|
}
|
|
result
|
|
});
|
|
// reading the page is not blocked.s
|
|
let result = wait_thread_with_timeout(join_handle, 100);
|
|
for v in result {
|
|
assert_eq!(v, 1);
|
|
}
|
|
worker.close();
|
|
}
|
|
|
|
#[test]
|
|
fn swap_out_twice() {
|
|
let worker = Worker::new(2, 2);
|
|
let uffd = create_uffd_for_test();
|
|
let file = tempfile::tempfile().unwrap();
|
|
let shm = SharedMemory::new("shm", 6 * pagesize() as u64).unwrap();
|
|
let mmap1 = MemoryMappingBuilder::new(3 * pagesize())
|
|
.from_shared_memory(&shm)
|
|
.build()
|
|
.unwrap();
|
|
let mmap2 = MemoryMappingBuilder::new(3 * pagesize())
|
|
.from_shared_memory(&shm)
|
|
.offset(3 * pagesize() as u64)
|
|
.build()
|
|
.unwrap();
|
|
let base_addr1 = mmap1.as_ptr() as usize;
|
|
let base_addr2 = mmap2.as_ptr() as usize;
|
|
let regions = [
|
|
base_addr1..(base_addr1 + 3 * pagesize()),
|
|
base_addr2..(base_addr2 + 3 * pagesize()),
|
|
];
|
|
let mut page_handler = PageHandler::create(&file, ®ions, worker.channel.clone()).unwrap();
|
|
unsafe {
|
|
for i in 0..pagesize() {
|
|
*((base_addr1 + i) as *mut u8) = 1;
|
|
*((base_addr1 + 2 * pagesize() + i) as *mut u8) = 2;
|
|
*((base_addr2 + i) as *mut u8) = 3;
|
|
*((base_addr2 + 2 * pagesize() + i) as *mut u8) = 4;
|
|
}
|
|
}
|
|
unsafe { register_regions(®ions, array::from_ref(&uffd)) }.unwrap();
|
|
|
|
unsafe {
|
|
page_handler.move_to_staging(base_addr1, &shm, 0).unwrap();
|
|
page_handler
|
|
.move_to_staging(base_addr2, &shm, 3 * pagesize() as u64)
|
|
.unwrap();
|
|
}
|
|
worker.channel.wait_complete();
|
|
swap_out_all(&mut page_handler);
|
|
// page faults on all pages in mmap1.
|
|
for i in 0..3 {
|
|
page_handler
|
|
.handle_page_fault(&uffd, (base_addr1) + i * pagesize())
|
|
.unwrap();
|
|
}
|
|
// write values on another thread to avoid blocking forever
|
|
let join_handle = thread::spawn(move || {
|
|
for i in 0..pagesize() {
|
|
let ptr = (base_addr1 + pagesize() + i) as *mut u8;
|
|
unsafe {
|
|
*ptr = 5;
|
|
}
|
|
}
|
|
for i in 0..pagesize() {
|
|
let ptr = (base_addr1 + 2 * pagesize() + i) as *mut u8;
|
|
unsafe {
|
|
*ptr = 6;
|
|
}
|
|
}
|
|
});
|
|
wait_thread_with_timeout(join_handle, 100);
|
|
unsafe {
|
|
page_handler.move_to_staging(base_addr1, &shm, 0).unwrap();
|
|
page_handler
|
|
.move_to_staging(base_addr2, &shm, 3 * pagesize() as u64)
|
|
.unwrap();
|
|
}
|
|
worker.channel.wait_complete();
|
|
swap_out_all(&mut page_handler);
|
|
|
|
// page faults on all pages.
|
|
for i in 0..3 {
|
|
page_handler
|
|
.handle_page_fault(&uffd, base_addr1 + i * pagesize())
|
|
.unwrap();
|
|
page_handler
|
|
.handle_page_fault(&uffd, base_addr2 + i * pagesize())
|
|
.unwrap();
|
|
}
|
|
// read values on another thread to avoid blocking forever
|
|
let join_handle = thread::spawn(move || {
|
|
let mut result = Vec::new();
|
|
for i in 0..3 {
|
|
for j in 0..pagesize() {
|
|
let ptr = (base_addr1 + i * pagesize() + j) as *mut u8;
|
|
unsafe {
|
|
result.push(*ptr);
|
|
}
|
|
}
|
|
}
|
|
for i in 0..3 {
|
|
for j in 0..pagesize() {
|
|
let ptr = (base_addr2 + i * pagesize() + j) as *mut u8;
|
|
unsafe {
|
|
result.push(*ptr);
|
|
}
|
|
}
|
|
}
|
|
result
|
|
});
|
|
let result = wait_thread_with_timeout(join_handle, 100);
|
|
let values: Vec<u8> = vec![1, 5, 6, 3, 0, 4];
|
|
for (i, v) in values.iter().enumerate() {
|
|
for j in 0..pagesize() {
|
|
assert_eq!(&result[i * pagesize() + j], v);
|
|
}
|
|
}
|
|
worker.close();
|
|
}
|
|
|
|
#[test]
|
|
fn swap_in_success() {
|
|
let worker = Worker::new(2, 2);
|
|
let uffd = create_uffd_for_test();
|
|
let file = tempfile::tempfile().unwrap();
|
|
let shm = SharedMemory::new("shm", 6 * pagesize() as u64).unwrap();
|
|
let mmap1 = MemoryMappingBuilder::new(3 * pagesize())
|
|
.from_shared_memory(&shm)
|
|
.build()
|
|
.unwrap();
|
|
let mmap2 = MemoryMappingBuilder::new(3 * pagesize())
|
|
.from_shared_memory(&shm)
|
|
.offset(3 * pagesize() as u64)
|
|
.build()
|
|
.unwrap();
|
|
let base_addr1 = mmap1.as_ptr() as usize;
|
|
let base_addr2 = mmap2.as_ptr() as usize;
|
|
let regions = [
|
|
base_addr1..(base_addr1 + 3 * pagesize()),
|
|
base_addr2..(base_addr2 + 3 * pagesize()),
|
|
];
|
|
let mut page_handler = PageHandler::create(&file, ®ions, worker.channel.clone()).unwrap();
|
|
unsafe {
|
|
for i in base_addr1 + pagesize()..base_addr1 + 2 * pagesize() {
|
|
*(i as *mut u8) = 1;
|
|
}
|
|
for i in base_addr2 + pagesize()..base_addr2 + 2 * pagesize() {
|
|
*(i as *mut u8) = 2;
|
|
}
|
|
for i in base_addr2 + 2 * pagesize()..base_addr2 + 3 * pagesize() {
|
|
*(i as *mut u8) = 3;
|
|
}
|
|
}
|
|
unsafe { register_regions(®ions, array::from_ref(&uffd)) }.unwrap();
|
|
|
|
unsafe {
|
|
page_handler.move_to_staging(base_addr1, &shm, 0).unwrap();
|
|
page_handler
|
|
.move_to_staging(base_addr2, &shm, 3 * pagesize() as u64)
|
|
.unwrap();
|
|
}
|
|
worker.channel.wait_complete();
|
|
swap_out_all(&mut page_handler);
|
|
page_handler
|
|
.handle_page_fault(&uffd, base_addr1 + pagesize())
|
|
.unwrap();
|
|
page_handler
|
|
.handle_page_fault(&uffd, base_addr2 + pagesize())
|
|
.unwrap();
|
|
unsafe {
|
|
for i in base_addr2 + pagesize()..base_addr2 + 2 * pagesize() {
|
|
*(i as *mut u8) = 4;
|
|
}
|
|
}
|
|
// move to staging memory.
|
|
unsafe {
|
|
page_handler
|
|
.move_to_staging(base_addr2, &shm, 3 * pagesize() as u64)
|
|
.unwrap();
|
|
}
|
|
worker.channel.wait_complete();
|
|
while page_handler.swap_in(&uffd, 1024 * 1024).unwrap() != 0 {}
|
|
unregister_regions(®ions, array::from_ref(&uffd)).unwrap();
|
|
|
|
// read values on another thread to avoid blocking forever
|
|
let join_handle = thread::spawn(move || {
|
|
let mut result = Vec::new();
|
|
for i in 0..3 {
|
|
for j in 0..pagesize() {
|
|
let ptr = (base_addr1 + i * pagesize() + j) as *mut u8;
|
|
unsafe {
|
|
result.push(*ptr);
|
|
}
|
|
}
|
|
}
|
|
for i in 0..3 {
|
|
for j in 0..pagesize() {
|
|
let ptr = (base_addr2 + i * pagesize() + j) as *mut u8;
|
|
unsafe {
|
|
result.push(*ptr);
|
|
}
|
|
}
|
|
}
|
|
result
|
|
});
|
|
let result = wait_thread_with_timeout(join_handle, 100);
|
|
let values: Vec<u8> = vec![0, 1, 0, 0, 4, 3];
|
|
for (i, v) in values.iter().enumerate() {
|
|
for j in 0..pagesize() {
|
|
assert_eq!(&result[i * pagesize() + j], v);
|
|
}
|
|
}
|
|
worker.close();
|
|
}
|