From 53085c7ccc1f111d2d35ea41bfa85e20f05e66dc Mon Sep 17 00:00:00 2001 From: Sebastien Boeuf Date: Fri, 24 May 2019 12:21:23 -0700 Subject: [PATCH] memory: Allow memory to be backed by a file In the context of vhost-user, we need the guest RAM to be backed by a file in order to be accessed by an external process. This patch adds the new flag "file=" to the "--memory" option so that we can specify from the command line if the memory needs to be backed, and by which specific file. Signed-off-by: Sebastien Boeuf --- src/main.rs | 19 +++++++++------- vmm/src/config.rs | 58 +++++++++++++++++++++++++++++++++++------------ vmm/src/vm.rs | 33 +++++++++++++++------------ 3 files changed, 73 insertions(+), 37 deletions(-) diff --git a/src/main.rs b/src/main.rs index 815f1feb3..c4b5a78e5 100755 --- a/src/main.rs +++ b/src/main.rs @@ -26,7 +26,10 @@ fn main() { .arg( Arg::with_name("memory") .long("memory") - .help("Amount of RAM (in MiB)") + .help( + "Memory parameters \"size=,\ + file=\"", + ) .default_value(config::DEFAULT_MEMORY), ) .arg( @@ -118,7 +121,7 @@ fn main() { "Cloud Hypervisor Guest\n\tvCPUs: {}\n\tMemory: {} MB\ \n\tKernel: {:?}\n\tKernel cmdline: {}\n\tDisk(s): {:?}", u8::from(&vm_config.cpus), - u64::from(&vm_config.memory), + vm_config.memory.size, vm_config.kernel.path, vm_config.cmdline.args.as_str(), vm_config.disks, @@ -242,7 +245,7 @@ mod tests { let (disks, fw_path) = prepare_files(); let mut child = Command::new("target/debug/cloud-hypervisor") .args(&["--cpus", "1"]) - .args(&["--memory", "512"]) + .args(&["--memory", "size=512"]) .args(&["--kernel", fw_path.as_str()]) .args(&["--disk", disks[0], disks[1]]) .args(&["--net", "tap=,mac=,ip=192.168.2.1,mask=255.255.255.0"]) @@ -270,7 +273,7 @@ mod tests { let (disks, fw_path) = prepare_files(); let mut child = Command::new("target/debug/cloud-hypervisor") .args(&["--cpus", "2"]) - .args(&["--memory", "512"]) + .args(&["--memory", "size=512"]) .args(&["--kernel", fw_path.as_str()]) .args(&["--disk", disks[0], disks[1]]) .args(&["--net", "tap=,mac=,ip=192.168.2.1,mask=255.255.255.0"]) @@ -295,7 +298,7 @@ mod tests { let (disks, fw_path) = prepare_files(); let mut child = Command::new("target/debug/cloud-hypervisor") .args(&["--cpus", "1"]) - .args(&["--memory", "5120"]) + .args(&["--memory", "size=5120"]) .args(&["--kernel", fw_path.as_str()]) .args(&["--disk", disks[0], disks[1]]) .args(&["--net", "tap=,mac=,ip=192.168.2.1,mask=255.255.255.0"]) @@ -320,7 +323,7 @@ mod tests { let (disks, fw_path) = prepare_files(); let mut child = Command::new("target/debug/cloud-hypervisor") .args(&["--cpus", "1"]) - .args(&["--memory", "512"]) + .args(&["--memory", "size=512"]) .args(&["--kernel", fw_path.as_str()]) .args(&["--disk", disks[0], disks[1]]) .args(&["--net", "tap=,mac=,ip=192.168.2.1,mask=255.255.255.0"]) @@ -358,7 +361,7 @@ mod tests { let mut child = Command::new("target/debug/cloud-hypervisor") .args(&["--cpus", "1"]) - .args(&["--memory", "512"]) + .args(&["--memory", "size=512"]) .args(&["--kernel", kernel_path.to_str().unwrap()]) .args(&["--disk", disks[0], disks[1]]) .args(&["--net", "tap=,mac=,ip=192.168.2.1,mask=255.255.255.0"]) @@ -400,7 +403,7 @@ mod tests { let mut child = Command::new("target/debug/cloud-hypervisor") .args(&["--cpus", "1"]) - .args(&["--memory", "512"]) + .args(&["--memory", "size=512"]) .args(&["--kernel", kernel_path.to_str().unwrap()]) .args(&["--disk", disks[0], disks[1]]) .args(&["--net", "tap=,mac=,ip=192.168.2.1,mask=255.255.255.0"]) diff --git a/vmm/src/config.rs b/vmm/src/config.rs index c956f5b00..d7a817970 100644 --- a/vmm/src/config.rs +++ b/vmm/src/config.rs @@ -13,7 +13,7 @@ use std::result; use vm_memory::GuestAddress; pub const DEFAULT_VCPUS: &str = "1"; -pub const DEFAULT_MEMORY: &str = "512"; +pub const DEFAULT_MEMORY: &str = "size=512"; pub const DEFAULT_RNG_SOURCE: &str = "/dev/urandom"; const CMDLINE_OFFSET: GuestAddress = GuestAddress(0x20000); @@ -22,8 +22,10 @@ const CMDLINE_OFFSET: GuestAddress = GuestAddress(0x20000); pub enum Error<'a> { /// Failed parsing cpus parameters. ParseCpusParams(std::num::ParseIntError), - /// Failed parsing memory parameters. - ParseMemoryParams(std::num::ParseIntError), + /// Failed parsing memory size parameter. + ParseMemorySizeParam(std::num::ParseIntError), + /// Failed parsing memory file parameter. + ParseMemoryFileParam, /// Failed parsing kernel parameters. ParseKernelParams, /// Failed parsing kernel command line parameters. @@ -76,19 +78,45 @@ impl From<&CpusConfig> for u8 { } } -pub struct MemoryConfig(pub u64); - -impl MemoryConfig { - pub fn parse(memory: &str) -> Result { - Ok(MemoryConfig( - memory.parse::().map_err(Error::ParseMemoryParams)?, - )) - } +pub struct MemoryConfig<'a> { + pub size: u64, + pub file: Option<&'a Path>, } -impl From<&MemoryConfig> for u64 { - fn from(val: &MemoryConfig) -> Self { - val.0 +impl<'a> MemoryConfig<'a> { + pub fn parse(memory: &'a str) -> Result { + // Split the parameters based on the comma delimiter + let params_list: Vec<&str> = memory.split(',').collect(); + + let mut size_str: &str = ""; + let mut file_str: &str = ""; + let mut backed = false; + + for param in params_list.iter() { + if param.starts_with("size=") { + size_str = ¶m[5..]; + } else if param.starts_with("file=") { + backed = true; + file_str = ¶m[5..]; + } + } + + let file = if backed { + if file_str.is_empty() { + return Err(Error::ParseMemoryFileParam); + } + + Some(Path::new(file_str)) + } else { + None + }; + + Ok(MemoryConfig { + size: size_str + .parse::() + .map_err(Error::ParseMemorySizeParam)?, + file, + }) } } @@ -268,7 +296,7 @@ impl<'a> FsConfig<'a> { pub struct VmConfig<'a> { pub cpus: CpusConfig, - pub memory: MemoryConfig, + pub memory: MemoryConfig<'a>, pub kernel: KernelConfig<'a>, pub cmdline: CmdlineConfig, pub disks: Vec>, diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index b37cb4476..dafa2c3dc 100755 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -843,24 +843,29 @@ impl<'a> Vm<'a> { let fd = Arc::new(fd); // Init guest memory - let arch_mem_regions = arch::arch_memory_regions(u64::from(&config.memory) << 20); + let arch_mem_regions = arch::arch_memory_regions(config.memory.size << 20); - let mut mem_regions = Vec::<(GuestAddress, usize, Option)>::new(); - for region in arch_mem_regions.iter() { - let file = OpenOptions::new() - .read(true) - .write(true) - .custom_flags(O_TMPFILE) - .open("/dev/shm") - .map_err(Error::SharedFileCreate)?; + let guest_memory = match config.memory.file { + Some(file) => { + let mut mem_regions = Vec::<(GuestAddress, usize, Option)>::new(); + for region in arch_mem_regions.iter() { + let file = OpenOptions::new() + .read(true) + .write(true) + .custom_flags(O_TMPFILE) + .open(file) + .map_err(Error::SharedFileCreate)?; - file.set_len(region.1 as u64) - .map_err(Error::SharedFileSetLen)?; + file.set_len(region.1 as u64) + .map_err(Error::SharedFileSetLen)?; - mem_regions.push((region.0, region.1, Some(FileOffset::new(file, 0)))); - } + mem_regions.push((region.0, region.1, Some(FileOffset::new(file, 0)))); + } - let guest_memory = GuestMemoryMmap::with_files(&mem_regions).map_err(Error::GuestMemory)?; + GuestMemoryMmap::with_files(&mem_regions).map_err(Error::GuestMemory)? + } + None => GuestMemoryMmap::new(&arch_mem_regions).map_err(Error::GuestMemory)?, + }; guest_memory .with_regions(|index, region| {