mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2025-02-09 20:04:20 +00:00
Reland "swap: add userfaultfd wrapper"
origin: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/3895235 userfaultfd enables applications to handle page faults on designated memory area. vmm-swap feature uses userfaultfd to catch page fault event and swap in the guest memory from the swap file. design document: go/tanooki-phase1-dd BUG=b:215093219 TEST=cargo build --features=swap Change-Id: I36ecfe9be988a4bc451f8edaf2ab48e25c6600f4 Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/4016142 Commit-Queue: Shin Kawamura <kawasin@google.com> Reviewed-by: David Stevens <stevensd@chromium.org> Reviewed-by: Dennis Kempin <denniskempin@google.com>
This commit is contained in:
parent
ba3e2fa852
commit
67390a05fb
6 changed files with 293 additions and 1 deletions
129
Cargo.lock
generated
129
Cargo.lock
generated
|
@ -230,6 +230,25 @@ dependencies = [
|
|||
"syn 1.0.103",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.60.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "062dddbc1ba4aca46de6338e2bf87771414c335f7b2f2036e8f3e9befebf88e6"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"lazy_static",
|
||||
"lazycell",
|
||||
"peeking_take_while",
|
||||
"proc-macro2",
|
||||
"quote 1.0.21",
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit_field"
|
||||
version = "0.1.0"
|
||||
|
@ -312,6 +331,15 @@ dependencies = [
|
|||
"jobserver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cexpr"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
|
||||
dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
|
@ -332,6 +360,17 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clang-sys"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3"
|
||||
dependencies = [
|
||||
"glob",
|
||||
"libc",
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.34.0"
|
||||
|
@ -532,6 +571,7 @@ dependencies = [
|
|||
"serde_json",
|
||||
"serde_keyvalue",
|
||||
"static_assertions",
|
||||
"swap",
|
||||
"sync",
|
||||
"tempfile",
|
||||
"terminal_size",
|
||||
|
@ -606,7 +646,7 @@ version = "3.2.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b37feaa84e6861e00a1f5e5aa8da3ee56d605c9992d33e082786754828e20865"
|
||||
dependencies = [
|
||||
"nix",
|
||||
"nix 0.24.2",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
|
@ -975,6 +1015,12 @@ dependencies = [
|
|||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||
|
||||
[[package]]
|
||||
name = "gpu_display"
|
||||
version = "0.1.0"
|
||||
|
@ -1172,6 +1218,12 @@ version = "1.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "lazycell"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.126"
|
||||
|
@ -1206,6 +1258,16 @@ dependencies = [
|
|||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libslirp-sys"
|
||||
version = "4.2.1"
|
||||
|
@ -1406,6 +1468,19 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.23.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"memoffset 0.6.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.24.2"
|
||||
|
@ -1497,6 +1572,12 @@ dependencies = [
|
|||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "peeking_take_while"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.9"
|
||||
|
@ -1718,6 +1799,12 @@ dependencies = [
|
|||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rutabaga_gfx"
|
||||
version = "0.1.0"
|
||||
|
@ -1837,6 +1924,12 @@ dependencies = [
|
|||
"syn 1.0.103",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.7"
|
||||
|
@ -1864,6 +1957,15 @@ version = "0.8.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "swap"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"base",
|
||||
"libc",
|
||||
"userfaultfd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "0.11.11"
|
||||
|
@ -2062,6 +2164,31 @@ dependencies = [
|
|||
"usb_sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "userfaultfd"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fee2cdd3f8bdd0b98d7aa9ace35e7214a71888229d60c1cd1cd71b7c09c089d0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"nix 0.23.1",
|
||||
"thiserror",
|
||||
"userfaultfd-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "userfaultfd-sys"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2cbcf2717fa856a7226499babbbccb07353ea2fc2b27defd38bd13b1227cc78"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "0.8.2"
|
||||
|
|
|
@ -90,6 +90,7 @@ members = [
|
|||
"resources",
|
||||
"rutabaga_gfx",
|
||||
"serde_keyvalue",
|
||||
"swap",
|
||||
"system_api",
|
||||
"third_party/vmm_vhost",
|
||||
"tpm2-sys",
|
||||
|
@ -296,6 +297,7 @@ all-aarch64 = [
|
|||
"panic-memfd",
|
||||
"power-monitor-powerd",
|
||||
"slirp",
|
||||
"swap",
|
||||
"tpm",
|
||||
"vaapi",
|
||||
"video-decoder",
|
||||
|
@ -395,6 +397,9 @@ aarch64 = { path = "aarch64" }
|
|||
minijail = "*" # provided by ebuild
|
||||
p9 = "*"
|
||||
|
||||
[target.'cfg(target_os="linux")'.dependencies]
|
||||
swap = { path = "swap", optional = true }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
anti_tamper = { path = "anti_tamper" }
|
||||
cros_async = { path = "cros_async" }
|
||||
|
|
10
swap/Cargo.toml
Normal file
10
swap/Cargo.toml
Normal file
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "swap"
|
||||
version = "0.1.0"
|
||||
authors = ["The Chromium OS Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
base = { path = "../base" }
|
||||
libc = "*"
|
||||
userfaultfd = "0.5.0"
|
11
swap/src/lib.rs
Normal file
11
swap/src/lib.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
// 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.
|
||||
|
||||
//! crate for the vmm-swap feature.
|
||||
|
||||
// TODO(kawasin): warn dead_code again after swap feature is done.
|
||||
#![allow(dead_code)]
|
||||
#![deny(missing_docs)]
|
||||
|
||||
mod userfaultfd;
|
138
swap/src/userfaultfd.rs
Normal file
138
swap/src/userfaultfd.rs
Normal file
|
@ -0,0 +1,138 @@
|
|||
// 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.
|
||||
|
||||
//! Provides wrapper of userfaultfd crate for vmm-swap feature.
|
||||
|
||||
#![deny(missing_docs)]
|
||||
|
||||
use std::convert::From;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
use base::AsRawDescriptor;
|
||||
use base::RawDescriptor;
|
||||
pub use userfaultfd::Error as UffdError;
|
||||
pub use userfaultfd::Event as UffdEvent;
|
||||
use userfaultfd::FeatureFlags;
|
||||
use userfaultfd::IoctlFlags;
|
||||
use userfaultfd::Result;
|
||||
use userfaultfd::Uffd;
|
||||
use userfaultfd::UffdBuilder;
|
||||
|
||||
/// Wrapper for [`userfaultfd::Uffd`] to be used in the vmm-swap feature.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The userfaultfd operations (`UFFDIO_COPY` and `UFFDIO_ZEROPAGE`) looks unsafe since it fills a
|
||||
/// memory content directly. But they actually are not unsafe operation but `UFFDIO_REGISTER` should
|
||||
/// be the unsafe operation for Rust memory safety.
|
||||
///
|
||||
/// According to [the Rust document](https://doc.rust-lang.org/nomicon/uninitialized.html),
|
||||
///
|
||||
/// > All runtime-allocated memory in a Rust program begins its life as uninitialized.
|
||||
///
|
||||
/// The userfaultfd operations actually does not change/overwrite the existing memory contents but
|
||||
/// they just setup the "uninitialized" pages. If the page was already initialized, the userfaultfd
|
||||
/// operations fail and return EEXIST error (which is not documented unfortunately). So they
|
||||
/// originally does not affect the Rust memory safety.
|
||||
///
|
||||
/// The "uninitialized" page in this context has 2 patterns:
|
||||
///
|
||||
/// 1. pages which is never touched or,
|
||||
/// 2. pages which is never touched after MADV_REMOVE
|
||||
///
|
||||
/// Filling the (1) pages with any contents should not affect the Rust memory safety.
|
||||
///
|
||||
/// Filling the (2) pages potentially may break the memory used by Rust. But the safety should be
|
||||
/// examined at `MADV_REMOVE` and `UFFDIO_REGISTER` timing.
|
||||
pub struct Userfaultfd {
|
||||
uffd: Uffd,
|
||||
}
|
||||
|
||||
impl Userfaultfd {
|
||||
/// Creates a new userfaultfd.
|
||||
pub fn new() -> Result<Self> {
|
||||
let uffd = UffdBuilder::new()
|
||||
.close_on_exec(true)
|
||||
.non_blocking(true)
|
||||
.user_mode_only(false)
|
||||
.require_features(
|
||||
FeatureFlags::EVENT_FORK | FeatureFlags::MISSING_SHMEM | FeatureFlags::EVENT_REMOVE,
|
||||
)
|
||||
.create()?;
|
||||
Ok(Self { uffd })
|
||||
}
|
||||
|
||||
/// Register a range of memory to the userfaultfd.
|
||||
///
|
||||
/// After this registration, any page faults on the range will be caught by the userfaultfd.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `addr` - the starting address of the range of memory.
|
||||
/// * `len` - the length in bytes of the range of memory.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// [addr, addr+len) must lie within a [MemoryMapping](base::MemoryMapping), and that mapping
|
||||
/// must live for the lifespan of the userfaultfd kernel object (which may be distinct from the
|
||||
/// `Userfaultfd` rust object in this process).
|
||||
pub unsafe fn register(&self, addr: usize, len: usize) -> Result<IoctlFlags> {
|
||||
self.uffd.register(addr as *mut libc::c_void, len)
|
||||
}
|
||||
|
||||
/// Initialize page(s) and fill it with zero.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `addr` - the starting address of the page(s) to be initialzed with zero.
|
||||
/// * `len` - the length in bytes of the page(s).
|
||||
/// * `wake` - whether or not to unblock the faulting thread.
|
||||
pub fn zero(&self, addr: usize, len: usize, wake: bool) -> Result<usize> {
|
||||
// safe because zeroing untouched pages does not break the Rust memory safety since "All
|
||||
// runtime-allocated memory in a Rust program begins its life as uninitialized."
|
||||
// https://doc.rust-lang.org/nomicon/uninitialized.html
|
||||
unsafe { self.uffd.zeropage(addr as *mut libc::c_void, len, wake) }
|
||||
}
|
||||
|
||||
/// Copy the `data` to the page(s) starting from `addr`.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `addr` - the starting address of the page(s) to be initialzed with data.
|
||||
/// * `len` - the length in bytes of the page(s).
|
||||
/// * `data` - the starting address of the content.
|
||||
/// * `wake` - whether or not to unblock the faulting thread.
|
||||
pub fn copy(&self, addr: usize, len: usize, data: *const u8, wake: bool) -> Result<usize> {
|
||||
// safe because filling untouched pages with data does not break the Rust memory safety
|
||||
// since "All runtime-allocated memory in a Rust program begins its life as uninitialized."
|
||||
// https://doc.rust-lang.org/nomicon/uninitialized.html
|
||||
unsafe {
|
||||
self.uffd.copy(
|
||||
data as *const libc::c_void,
|
||||
addr as *mut libc::c_void,
|
||||
len,
|
||||
wake,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Read an event from the userfaultfd.
|
||||
///
|
||||
/// Return `None` immediately if no events is ready to read.
|
||||
pub fn read_event(&self) -> Result<Option<UffdEvent>> {
|
||||
self.uffd.read_event()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Uffd> for Userfaultfd {
|
||||
fn from(uffd: Uffd) -> Self {
|
||||
Self { uffd }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawDescriptor for Userfaultfd {
|
||||
fn as_raw_descriptor(&self) -> RawDescriptor {
|
||||
self.uffd.as_raw_fd()
|
||||
}
|
||||
}
|
|
@ -71,6 +71,7 @@ WIN64_DISABLED_CRATES = [
|
|||
"p9",
|
||||
"qcow_utils",
|
||||
"rutabaga_gralloc",
|
||||
"swap",
|
||||
"system_api_stub",
|
||||
"tpm2-sys",
|
||||
"tpm2",
|
||||
|
|
Loading…
Reference in a new issue