mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2025-02-10 20:19:07 +00:00
crosvm: implement lock-guest-memory feature.
This CL adds a command-line flag to crosm that locks the entire guest memory, per http://go/host-assisted-vm-swap. The change relies on existing balloon device code that punches holes within the guest memory region upon "inflate". BUG=b:236210703 TEST=manual startup, roblox+instagram and various chrome sessions Change-Id: I11e0e5b0b0cb6450f47124a6a8b6c4befd9de6ae Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3725731 Reviewed-by: Keiichi Watanabe <keiichiw@chromium.org> Reviewed-by: David Stevens <stevensd@chromium.org> Tested-by: kokoro <noreply+kokoro@google.com> Commit-Queue: Alexandre Marciano Gimenez <raging@google.com> Auto-Submit: Alexandre Marciano Gimenez <raging@google.com>
This commit is contained in:
parent
10574227dc
commit
ed071b6edd
5 changed files with 71 additions and 2 deletions
|
@ -12,6 +12,8 @@ use std::{
|
||||||
ptr::{copy_nonoverlapping, null_mut, read_unaligned, write_unaligned},
|
ptr::{copy_nonoverlapping, null_mut, read_unaligned, write_unaligned},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use log::warn;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
external_mapping::ExternalMapping, AsRawDescriptor, Descriptor,
|
external_mapping::ExternalMapping, AsRawDescriptor, Descriptor,
|
||||||
MemoryMapping as CrateMemoryMapping, MemoryMappingBuilder, RawDescriptor, SafeDescriptor,
|
MemoryMapping as CrateMemoryMapping, MemoryMappingBuilder, RawDescriptor, SafeDescriptor,
|
||||||
|
@ -703,6 +705,34 @@ impl MemoryMapping {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Disable host swap for this mapping.
|
||||||
|
pub fn lock_all(&self) -> Result<()> {
|
||||||
|
let ret = unsafe {
|
||||||
|
// Safe because MLOCK_ONFAULT only affects the swap behavior of the kernel, so it
|
||||||
|
// has no impact on rust semantics.
|
||||||
|
// TODO(raging): use the explicit declaration of mlock2, which was being merged
|
||||||
|
// as of when the call below was being worked on.
|
||||||
|
libc::syscall(
|
||||||
|
libc::SYS_mlock2,
|
||||||
|
(self.addr as usize) as *const libc::c_void,
|
||||||
|
self.size(),
|
||||||
|
libc::MLOCK_ONFAULT,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if ret < 0 {
|
||||||
|
let errno = super::Error::last();
|
||||||
|
warn!(
|
||||||
|
"failed to mlock at {:#x} with length {}: {}",
|
||||||
|
(self.addr as usize) as u64,
|
||||||
|
self.size(),
|
||||||
|
errno,
|
||||||
|
);
|
||||||
|
Err(Error::SystemCallFailed(errno))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check that offset+count is valid and return the sum.
|
// Check that offset+count is valid and return the sum.
|
||||||
fn range_end(&self, offset: usize, count: usize) -> Result<usize> {
|
fn range_end(&self, offset: usize, count: usize) -> Result<usize> {
|
||||||
let mem_end = offset.checked_add(count).ok_or(Error::InvalidAddress)?;
|
let mem_end = offset.checked_add(count).ok_or(Error::InvalidAddress)?;
|
||||||
|
@ -990,13 +1020,19 @@ impl CrateMemoryMapping {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Unix {
|
pub trait Unix {
|
||||||
|
/// Remove the specified range from the mapping.
|
||||||
fn remove_range(&self, mem_offset: usize, count: usize) -> Result<()>;
|
fn remove_range(&self, mem_offset: usize, count: usize) -> Result<()>;
|
||||||
|
/// Disable host swap for this mapping.
|
||||||
|
fn lock_all(&self) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Unix for CrateMemoryMapping {
|
impl Unix for CrateMemoryMapping {
|
||||||
fn remove_range(&self, mem_offset: usize, count: usize) -> Result<()> {
|
fn remove_range(&self, mem_offset: usize, count: usize) -> Result<()> {
|
||||||
self.mapping.remove_range(mem_offset, count)
|
self.mapping.remove_range(mem_offset, count)
|
||||||
}
|
}
|
||||||
|
fn lock_all(&self) -> Result<()> {
|
||||||
|
self.mapping.lock_all()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait MemoryMappingBuilderUnix<'a> {
|
pub trait MemoryMappingBuilderUnix<'a> {
|
||||||
|
|
|
@ -651,6 +651,9 @@ pub struct RunCommand {
|
||||||
#[argh(option, long = "kvm-device", arg_name = "PATH")]
|
#[argh(option, long = "kvm-device", arg_name = "PATH")]
|
||||||
/// path to the KVM device. (default /dev/kvm)
|
/// path to the KVM device. (default /dev/kvm)
|
||||||
pub kvm_device_path: Option<PathBuf>,
|
pub kvm_device_path: Option<PathBuf>,
|
||||||
|
#[argh(switch)]
|
||||||
|
/// disable host swap on guest VM pages.
|
||||||
|
pub lock_guest_memory: bool,
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
#[argh(option, arg_name = "MAC", long = "mac")]
|
#[argh(option, arg_name = "MAC", long = "mac")]
|
||||||
/// MAC address for VM
|
/// MAC address for VM
|
||||||
|
@ -1197,6 +1200,8 @@ impl TryFrom<RunCommand> for super::config::Config {
|
||||||
|
|
||||||
cfg.hugepages = cmd.hugepages;
|
cfg.hugepages = cmd.hugepages;
|
||||||
|
|
||||||
|
cfg.lock_guest_memory = cmd.lock_guest_memory;
|
||||||
|
|
||||||
#[cfg(feature = "audio")]
|
#[cfg(feature = "audio")]
|
||||||
{
|
{
|
||||||
cfg.ac97_parameters = cmd.ac97;
|
cfg.ac97_parameters = cmd.ac97;
|
||||||
|
|
|
@ -1720,6 +1720,7 @@ pub struct Config {
|
||||||
pub itmt: bool,
|
pub itmt: bool,
|
||||||
pub jail_config: Option<JailConfig>,
|
pub jail_config: Option<JailConfig>,
|
||||||
pub kvm_device_path: PathBuf,
|
pub kvm_device_path: PathBuf,
|
||||||
|
pub lock_guest_memory: bool,
|
||||||
pub mac_address: Option<net_util::MacAddress>,
|
pub mac_address: Option<net_util::MacAddress>,
|
||||||
pub memory: Option<u64>,
|
pub memory: Option<u64>,
|
||||||
pub memory_file: Option<PathBuf>,
|
pub memory_file: Option<PathBuf>,
|
||||||
|
@ -1856,6 +1857,7 @@ impl Default for Config {
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
kvm_device_path: PathBuf::from(KVM_PATH),
|
kvm_device_path: PathBuf::from(KVM_PATH),
|
||||||
|
lock_guest_memory: false,
|
||||||
mac_address: None,
|
mac_address: None,
|
||||||
memory: None,
|
memory: None,
|
||||||
memory_file: None,
|
memory_file: None,
|
||||||
|
@ -2137,6 +2139,10 @@ pub fn validate_config(cfg: &mut Config) -> std::result::Result<(), String> {
|
||||||
return Err("'balloon-control' requires enabled balloon".to_string());
|
return Err("'balloon-control' requires enabled balloon".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cfg.lock_guest_memory && cfg.jail_config.is_none() {
|
||||||
|
return Err("'lock-guest-memory' and 'disable-sandbox' are mutually exclusive".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
set_default_serial_parameters(
|
set_default_serial_parameters(
|
||||||
&mut cfg.serial_parameters,
|
&mut cfg.serial_parameters,
|
||||||
!cfg.vhost_user_console.is_empty(),
|
!cfg.vhost_user_console.is_empty(),
|
||||||
|
|
|
@ -1201,6 +1201,10 @@ pub fn run_config(cfg: Config) -> Result<ExitState> {
|
||||||
if components.hugepages {
|
if components.hugepages {
|
||||||
mem_policy |= MemoryPolicy::USE_HUGEPAGES;
|
mem_policy |= MemoryPolicy::USE_HUGEPAGES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cfg.lock_guest_memory {
|
||||||
|
mem_policy |= MemoryPolicy::LOCK_GUEST_MEMORY;
|
||||||
|
}
|
||||||
guest_mem.set_memory_policy(mem_policy);
|
guest_mem.set_memory_policy(mem_policy);
|
||||||
|
|
||||||
if cfg.kvm_device_path.exists() {
|
if cfg.kvm_device_path.exists() {
|
||||||
|
|
|
@ -11,6 +11,7 @@ use crate::{Error, GuestAddress, GuestMemory, Result};
|
||||||
bitflags! {
|
bitflags! {
|
||||||
pub struct MemoryPolicy: u32 {
|
pub struct MemoryPolicy: u32 {
|
||||||
const USE_HUGEPAGES = 1;
|
const USE_HUGEPAGES = 1;
|
||||||
|
const LOCK_GUEST_MEMORY = (1 << 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,14 +42,31 @@ impl GuestMemory {
|
||||||
|
|
||||||
/// Handles guest memory policy hints/advices.
|
/// Handles guest memory policy hints/advices.
|
||||||
pub fn set_memory_policy(&self, mem_policy: MemoryPolicy) {
|
pub fn set_memory_policy(&self, mem_policy: MemoryPolicy) {
|
||||||
if mem_policy.contains(MemoryPolicy::USE_HUGEPAGES) {
|
if mem_policy.is_empty() {
|
||||||
for (_, region) in self.regions.iter().enumerate() {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (_, region) in self.regions.iter().enumerate() {
|
||||||
|
if mem_policy.contains(MemoryPolicy::USE_HUGEPAGES) {
|
||||||
let ret = region.mapping.use_hugepages();
|
let ret = region.mapping.use_hugepages();
|
||||||
|
|
||||||
if let Err(err) = ret {
|
if let Err(err) = ret {
|
||||||
println!("Failed to enable HUGEPAGE for mapping {}", err);
|
println!("Failed to enable HUGEPAGE for mapping {}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if mem_policy.contains(MemoryPolicy::LOCK_GUEST_MEMORY) {
|
||||||
|
// This is done in coordination with remove_range() calls, which are
|
||||||
|
// performed by the virtio-balloon process (they must be performed by
|
||||||
|
// a different process from the one that issues the locks).
|
||||||
|
// We also prevent this from happening in single-process configurations,
|
||||||
|
// when we compute configuration flags.
|
||||||
|
let ret = region.mapping.lock_all();
|
||||||
|
|
||||||
|
if let Err(err) = ret {
|
||||||
|
println!("Failed to lock memory for mapping {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue