main, vmm: Let the user define distincts memory zones

Introducing a new CLI option --memory-zone letting the user specify
custom memory zones. When this option is present, the --memory size
must be explicitly set to 0.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
Sebastien Boeuf 2020-08-18 15:19:49 +02:00 committed by Samuel Ortiz
parent d25ec66bb6
commit be475ddc22
3 changed files with 123 additions and 11 deletions

View File

@ -114,6 +114,18 @@ fn create_app<'a, 'b>(
.default_value(&default_memory)
.group("vm-config"),
)
.arg(
Arg::with_name("memory-zone")
.long("memory-zone")
.help(
"User defined memory zone parameters \
\"size=<guest_memory_region_size>,file=<backing_file>,\
mergeable=on|off,shared=on|off,hugepages=on|off\"",
)
.takes_value(true)
.min_values(1)
.group("vm-config"),
)
.arg(
Arg::with_name("kernel")
.long("kernel")
@ -522,6 +534,7 @@ mod unit_tests {
hugepages: false,
balloon: false,
balloon_size: 0,
zones: None,
},
kernel: Some(KernelConfig {
path: PathBuf::from("/path/to/kernel"),

View File

@ -448,6 +448,27 @@ components:
topology:
$ref: '#/components/schemas/CpuTopology'
MemoryZoneConfig:
required:
- size
type: object
properties:
size:
type: integer
format: int64
default: 512 MB
file:
type: string
mergeable:
type: boolean
default: false
shared:
type: boolean
default: false
hugepages:
type: boolean
default: false
MemoryConfig:
required:
- size
@ -477,6 +498,10 @@ components:
balloon:
type: boolean
default: false
zones:
type: array
items:
$ref: '#/components/schemas/MemoryZoneConfig'
KernelConfig:
required:

View File

@ -42,6 +42,8 @@ pub enum Error {
ParseCpus(OptionParserError),
/// Error parsing memory options
ParseMemory(OptionParserError),
/// Error parsing memory zone options
ParseMemoryZone(OptionParserError),
/// Error parsing disk options
ParseDisk(OptionParserError),
/// Error parsing network options
@ -147,6 +149,7 @@ impl fmt::Display for Error {
ParseVsockCidMissing => write!(f, "Error parsing --vsock: cid missing"),
ParseVsockSockMissing => write!(f, "Error parsing --vsock: socket missing"),
ParseMemory(o) => write!(f, "Error parsing --memory: {}", o),
ParseMemoryZone(o) => write!(f, "Error parsing --memory-zone: {}", o),
ParseNetwork(o) => write!(f, "Error parsing --net: {}", o),
ParseDisk(o) => write!(f, "Error parsing --disk: {}", o),
ParseRNG(o) => write!(f, "Error parsing --rng: {}", o),
@ -166,6 +169,7 @@ pub type Result<T> = result::Result<T, Error>;
pub struct VmParams<'a> {
pub cpus: &'a str,
pub memory: &'a str,
pub memory_zones: Option<Vec<&'a str>>,
pub kernel: Option<&'a str>,
pub initramfs: Option<&'a str>,
pub cmdline: Option<&'a str>,
@ -187,6 +191,7 @@ impl<'a> VmParams<'a> {
// These .unwrap()s cannot fail as there is a default value defined
let cpus = args.value_of("cpus").unwrap();
let memory = args.value_of("memory").unwrap();
let memory_zones: Option<Vec<&str>> = args.values_of("memory-zone").map(|x| x.collect());
let rng = args.value_of("rng").unwrap();
let serial = args.value_of("serial").unwrap();
@ -207,6 +212,7 @@ impl<'a> VmParams<'a> {
VmParams {
cpus,
memory,
memory_zones,
kernel,
initramfs,
cmdline,
@ -337,6 +343,19 @@ impl Default for CpusConfig {
}
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct MemoryZoneConfig {
pub size: u64,
#[serde(default)]
pub file: Option<PathBuf>,
#[serde(default)]
pub mergeable: bool,
#[serde(default)]
pub shared: bool,
#[serde(default)]
pub hugepages: bool,
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct MemoryConfig {
pub size: u64,
@ -356,10 +375,12 @@ pub struct MemoryConfig {
pub balloon: bool,
#[serde(default)]
pub balloon_size: u64,
#[serde(default)]
pub zones: Option<Vec<MemoryZoneConfig>>,
}
impl MemoryConfig {
pub fn parse(memory: &str) -> Result<Self> {
pub fn parse(memory: &str, memory_zones: Option<Vec<&str>>) -> Result<Self> {
let mut parser = OptionParser::new();
parser
.add("size")
@ -407,6 +428,53 @@ impl MemoryConfig {
.unwrap_or(Toggle(false))
.0;
let zones: Option<Vec<MemoryZoneConfig>> = if let Some(memory_zones) = &memory_zones {
let mut zones = Vec::new();
for memory_zone in memory_zones.iter() {
let mut parser = OptionParser::new();
parser
.add("size")
.add("file")
.add("mergeable")
.add("shared")
.add("hugepages");
parser.parse(memory_zone).map_err(Error::ParseMemoryZone)?;
let size = parser
.convert::<ByteSized>("size")
.map_err(Error::ParseMemoryZone)?
.unwrap_or(ByteSized(DEFAULT_MEMORY_MB << 20))
.0;
let file = parser.get("file").map(PathBuf::from);
let mergeable = parser
.convert::<Toggle>("mergeable")
.map_err(Error::ParseMemoryZone)?
.unwrap_or(Toggle(false))
.0;
let shared = parser
.convert::<Toggle>("shared")
.map_err(Error::ParseMemoryZone)?
.unwrap_or(Toggle(false))
.0;
let hugepages = parser
.convert::<Toggle>("hugepages")
.map_err(Error::ParseMemoryZone)?
.unwrap_or(Toggle(false))
.0;
zones.push(MemoryZoneConfig {
size,
file,
mergeable,
shared,
hugepages,
});
}
Some(zones)
} else {
None
};
Ok(MemoryConfig {
size,
file,
@ -417,6 +485,7 @@ impl MemoryConfig {
hugepages,
balloon,
balloon_size: 0,
zones,
})
}
}
@ -433,6 +502,7 @@ impl Default for MemoryConfig {
hugepages: false,
balloon: false,
balloon_size: 0,
zones: None,
}
}
}
@ -1388,7 +1458,7 @@ impl VmConfig {
let config = VmConfig {
cpus: CpusConfig::parse(vm_params.cpus)?,
memory: MemoryConfig::parse(vm_params.memory)?,
memory: MemoryConfig::parse(vm_params.memory, vm_params.memory_zones)?,
kernel,
initramfs,
cmdline: CmdlineConfig::parse(vm_params.cmdline)?,
@ -1481,11 +1551,14 @@ mod tests {
#[test]
fn test_mem_parsing() -> Result<()> {
assert_eq!(MemoryConfig::parse("")?, MemoryConfig::default());
assert_eq!(MemoryConfig::parse("", None)?, MemoryConfig::default());
// Default string
assert_eq!(MemoryConfig::parse("size=512M")?, MemoryConfig::default());
assert_eq!(
MemoryConfig::parse("size=512M,file=/some/file")?,
MemoryConfig::parse("size=512M", None)?,
MemoryConfig::default()
);
assert_eq!(
MemoryConfig::parse("size=512M,file=/some/file", None)?,
MemoryConfig {
size: 512 << 20,
file: Some(PathBuf::from("/some/file")),
@ -1493,7 +1566,7 @@ mod tests {
}
);
assert_eq!(
MemoryConfig::parse("size=512M,mergeable=on")?,
MemoryConfig::parse("size=512M,mergeable=on", None)?,
MemoryConfig {
size: 512 << 20,
mergeable: true,
@ -1501,14 +1574,14 @@ mod tests {
}
);
assert_eq!(
MemoryConfig::parse("mergeable=on")?,
MemoryConfig::parse("mergeable=on", None)?,
MemoryConfig {
mergeable: true,
..Default::default()
}
);
assert_eq!(
MemoryConfig::parse("size=1G,mergeable=off")?,
MemoryConfig::parse("size=1G,mergeable=off", None)?,
MemoryConfig {
size: 1 << 30,
mergeable: false,
@ -1516,20 +1589,20 @@ mod tests {
}
);
assert_eq!(
MemoryConfig::parse("hotplug_method=acpi")?,
MemoryConfig::parse("hotplug_method=acpi", None)?,
MemoryConfig {
..Default::default()
}
);
assert_eq!(
MemoryConfig::parse("hotplug_method=acpi,hotplug_size=512M")?,
MemoryConfig::parse("hotplug_method=acpi,hotplug_size=512M", None)?,
MemoryConfig {
hotplug_size: Some(512 << 20),
..Default::default()
}
);
assert_eq!(
MemoryConfig::parse("hotplug_method=virtio-mem,hotplug_size=512M")?,
MemoryConfig::parse("hotplug_method=virtio-mem,hotplug_size=512M", None)?,
MemoryConfig {
hotplug_size: Some(512 << 20),
hotplug_method: HotplugMethod::VirtioMem,
@ -1951,6 +2024,7 @@ mod tests {
hugepages: false,
balloon: false,
balloon_size: 0,
zones: None,
},
kernel: Some(KernelConfig {
path: PathBuf::from("/path/to/kernel"),