vmm: Add 'hotplugged_size' to memory parameters

Add the new option 'hotplugged_size' to both --memory-zone and --memory
parameters so that we can let the user specify a certain amount of
memory being plugged at boot.

This is also part of making sure we can store the virtio-mem size over a
reboot of the VM.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
Sebastien Boeuf 2020-09-14 20:23:45 +02:00
parent 33a1e37c35
commit 4e1b78e1ff
5 changed files with 130 additions and 5 deletions

View File

@ -17,6 +17,7 @@ struct MemoryConfig {
hugepages: bool, hugepages: bool,
hotplug_method: HotplugMethod, hotplug_method: HotplugMethod,
hotplug_size: Option<u64>, hotplug_size: Option<u64>,
hotplugged_size: Option<u64>,
balloon: bool, balloon: bool,
balloon_size: u64, balloon_size: u64,
zones: Option<Vec<MemoryZoneConfig>>, zones: Option<Vec<MemoryZoneConfig>>,
@ -24,7 +25,7 @@ struct MemoryConfig {
``` ```
``` ```
--memory <memory> Memory parameters "size=<guest_memory_size>,mergeable=on|off,shared=on|off,hugepages=on|off,hotplug_method=acpi|virtio-mem,hotplug_size=<hotpluggable_memory_size>,balloon=on|off" --memory <memory> Memory parameters "size=<guest_memory_size>,mergeable=on|off,shared=on|off,hugepages=on|off,hotplug_method=acpi|virtio-mem,hotplug_size=<hotpluggable_memory_size>,hotplugged_size=<hotplugged_memory_size>,balloon=on|off"
``` ```
### `size` ### `size`
@ -120,6 +121,27 @@ _Example_
--memory size=1G,hotplug_size=1G --memory size=1G,hotplug_size=1G
``` ```
### `hotplugged_size`
Amount of memory that will be dynamically added to the VM at boot. This option
allows for starting a VM with a certain amount of memory that can be reduced
during runtime.
This is only valid when the `hotplug_method` is `virtio-mem` as it does not
make sense for the `acpi` use case. When using ACPI, the memory can't be
resized after it has been extended.
This option is only valid when `hotplug_size` is specified, and its value can't
exceed the value of `hotplug_size`.
Value is an unsigned integer of 64 bits. A value of 0 is invalid.
_Example_
```
--memory size=1G,hotplug_method=virtio-mem,hotplug_size=1G,hotplugged_size=512M
```
### `balloon` ### `balloon`
Specifies if the `virtio-balloon` device must be activated. This creates a Specifies if the `virtio-balloon` device must be activated. This creates a
@ -149,11 +171,12 @@ struct MemoryZoneConfig {
hugepages: bool, hugepages: bool,
host_numa_node: Option<u32>, host_numa_node: Option<u32>,
hotplug_size: Option<u64>, hotplug_size: Option<u64>,
hotplugged_size: Option<u64>,
} }
``` ```
``` ```
--memory-zone <memory-zone> User defined memory zone parameters "size=<guest_memory_region_size>,file=<backing_file>,shared=on|off,hugepages=on|off,host_numa_node=<node_id>,id=<zone_identifier>,hotplug_size=<hotpluggable_memory_size>" --memory-zone <memory-zone> User defined memory zone parameters "size=<guest_memory_region_size>,file=<backing_file>,shared=on|off,hugepages=on|off,host_numa_node=<node_id>,id=<zone_identifier>,hotplug_size=<hotpluggable_memory_size>,hotplugged_size=<hotplugged_memory_size>"
``` ```
This parameter expects one or more occurences, allowing for a list of memory This parameter expects one or more occurences, allowing for a list of memory
@ -303,6 +326,28 @@ _Example_
--memory-zone id=mem0,size=1G,hotplug_size=1G --memory-zone id=mem0,size=1G,hotplug_size=1G
``` ```
### `hotplugged_size`
Amount of memory that will be dynamically added to a memory zone at VM's boot.
This option allows for starting a VM with a certain amount of memory that can
be reduced during runtime.
This is only valid when the `hotplug_method` is `virtio-mem` as it does not
make sense for the `acpi` use case. When using ACPI, the memory can't be
resized after it has been extended.
This option is only valid when `hotplug_size` is specified, and its value can't
exceed the value of `hotplug_size`.
Value is an unsigned integer of 64 bits. A value of 0 is invalid.
_Example_
```
--memory size=0,hotplug_method=virtio-mem
--memory-zone id=mem0,size=1G,hotplug_size=1G,hotplugged_size=512M
```
## NUMA settings ## NUMA settings
`NumaConfig` or what is known as `--numa` from the CLI perspective has been `NumaConfig` or what is known as `--numa` from the CLI perspective has been

View File

@ -109,7 +109,8 @@ fn create_app<'a, 'b>(
"Memory parameters \ "Memory parameters \
\"size=<guest_memory_size>,mergeable=on|off,shared=on|off,hugepages=on|off,\ \"size=<guest_memory_size>,mergeable=on|off,shared=on|off,hugepages=on|off,\
hotplug_method=acpi|virtio-mem,\ hotplug_method=acpi|virtio-mem,\
hotplug_size=<hotpluggable_memory_size>\"", hotplug_size=<hotpluggable_memory_size>,\
hotplugged_size=<hotplugged_memory_size>\"",
) )
.default_value(&default_memory) .default_value(&default_memory)
.group("vm-config"), .group("vm-config"),
@ -121,7 +122,8 @@ fn create_app<'a, 'b>(
"User defined memory zone parameters \ "User defined memory zone parameters \
\"size=<guest_memory_region_size>,file=<backing_file>,\ \"size=<guest_memory_region_size>,file=<backing_file>,\
shared=on|off,hugepages=on|off,host_numa_node=<node_id>,\ shared=on|off,hugepages=on|off,host_numa_node=<node_id>,\
id=<zone_identifier>,hotplug_size=<hotpluggable_memory_size>\"", id=<zone_identifier>,hotplug_size=<hotpluggable_memory_size>,\
hotplugged_size=<hotplugged_memory_size>\"",
) )
.takes_value(true) .takes_value(true)
.min_values(1) .min_values(1)
@ -539,6 +541,7 @@ mod unit_tests {
mergeable: false, mergeable: false,
hotplug_method: HotplugMethod::Acpi, hotplug_method: HotplugMethod::Acpi,
hotplug_size: None, hotplug_size: None,
hotplugged_size: None,
shared: false, shared: false,
hugepages: false, hugepages: false,
balloon: false, balloon: false,

View File

@ -496,6 +496,9 @@ components:
hotplug_size: hotplug_size:
type: integer type: integer
format: int64 format: int64
hotplugged_size:
type: integer
format: int64
MemoryConfig: MemoryConfig:
required: required:
@ -509,6 +512,9 @@ components:
hotplug_size: hotplug_size:
type: integer type: integer
format: int64 format: int64
hotplugged_size:
type: integer
format: int64
mergeable: mergeable:
type: boolean type: boolean
default: false default: false

View File

@ -381,6 +381,8 @@ pub struct MemoryZoneConfig {
pub host_numa_node: Option<u32>, pub host_numa_node: Option<u32>,
#[serde(default)] #[serde(default)]
pub hotplug_size: Option<u64>, pub hotplug_size: Option<u64>,
#[serde(default)]
pub hotplugged_size: Option<u64>,
} }
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
@ -393,6 +395,8 @@ pub struct MemoryConfig {
#[serde(default)] #[serde(default)]
pub hotplug_size: Option<u64>, pub hotplug_size: Option<u64>,
#[serde(default)] #[serde(default)]
pub hotplugged_size: Option<u64>,
#[serde(default)]
pub shared: bool, pub shared: bool,
#[serde(default)] #[serde(default)]
pub hugepages: bool, pub hugepages: bool,
@ -413,6 +417,7 @@ impl MemoryConfig {
.add("mergeable") .add("mergeable")
.add("hotplug_method") .add("hotplug_method")
.add("hotplug_size") .add("hotplug_size")
.add("hotplugged_size")
.add("shared") .add("shared")
.add("hugepages") .add("hugepages")
.add("balloon"); .add("balloon");
@ -436,6 +441,10 @@ impl MemoryConfig {
.convert::<ByteSized>("hotplug_size") .convert::<ByteSized>("hotplug_size")
.map_err(Error::ParseMemory)? .map_err(Error::ParseMemory)?
.map(|v| v.0); .map(|v| v.0);
let hotplugged_size = parser
.convert::<ByteSized>("hotplugged_size")
.map_err(Error::ParseMemory)?
.map(|v| v.0);
let shared = parser let shared = parser
.convert::<Toggle>("shared") .convert::<Toggle>("shared")
.map_err(Error::ParseMemory)? .map_err(Error::ParseMemory)?
@ -463,7 +472,8 @@ impl MemoryConfig {
.add("shared") .add("shared")
.add("hugepages") .add("hugepages")
.add("host_numa_node") .add("host_numa_node")
.add("hotplug_size"); .add("hotplug_size")
.add("hotplugged_size");
parser.parse(memory_zone).map_err(Error::ParseMemoryZone)?; parser.parse(memory_zone).map_err(Error::ParseMemoryZone)?;
let id = parser.get("id").ok_or(Error::ParseMemoryZoneIdMissing)?; let id = parser.get("id").ok_or(Error::ParseMemoryZoneIdMissing)?;
@ -490,6 +500,10 @@ impl MemoryConfig {
.convert::<ByteSized>("hotplug_size") .convert::<ByteSized>("hotplug_size")
.map_err(Error::ParseMemoryZone)? .map_err(Error::ParseMemoryZone)?
.map(|v| v.0); .map(|v| v.0);
let hotplugged_size = parser
.convert::<ByteSized>("hotplugged_size")
.map_err(Error::ParseMemoryZone)?
.map(|v| v.0);
zones.push(MemoryZoneConfig { zones.push(MemoryZoneConfig {
id, id,
@ -499,6 +513,7 @@ impl MemoryConfig {
hugepages, hugepages,
host_numa_node, host_numa_node,
hotplug_size, hotplug_size,
hotplugged_size,
}); });
} }
Some(zones) Some(zones)
@ -511,6 +526,7 @@ impl MemoryConfig {
mergeable, mergeable,
hotplug_method, hotplug_method,
hotplug_size, hotplug_size,
hotplugged_size,
shared, shared,
hugepages, hugepages,
balloon, balloon,
@ -527,6 +543,7 @@ impl Default for MemoryConfig {
mergeable: false, mergeable: false,
hotplug_method: HotplugMethod::Acpi, hotplug_method: HotplugMethod::Acpi,
hotplug_size: None, hotplug_size: None,
hotplugged_size: None,
shared: false, shared: false,
hugepages: false, hugepages: false,
balloon: false, balloon: false,
@ -2118,6 +2135,7 @@ mod tests {
mergeable: false, mergeable: false,
hotplug_method: HotplugMethod::Acpi, hotplug_method: HotplugMethod::Acpi,
hotplug_size: None, hotplug_size: None,
hotplugged_size: None,
shared: false, shared: false,
hugepages: false, hugepages: false,
balloon: false, balloon: false,

View File

@ -440,6 +440,32 @@ impl MemoryManager {
return Err(Error::InvalidMemoryParameters); return Err(Error::InvalidMemoryParameters);
} }
if let Some(hotplugged_size) = config.hotplugged_size {
if let Some(hotplug_size) = config.hotplug_size {
if hotplugged_size > hotplug_size {
error!(
"'hotplugged_size' {} can't be bigger than \
'hotplug_size' {}",
hotplugged_size, hotplug_size,
);
return Err(Error::InvalidMemoryParameters);
}
} else {
error!(
"Invalid to define 'hotplugged_size' when there is\
no 'hotplug_size'"
);
return Err(Error::InvalidMemoryParameters);
}
if config.hotplug_method == HotplugMethod::Acpi {
error!(
"Invalid to define 'hotplugged_size' with hotplug \
method 'acpi'"
);
return Err(Error::InvalidMemoryParameters);
}
}
// Create a single zone from the global memory config. This lets // Create a single zone from the global memory config. This lets
// us reuse the codepath for user defined memory zones. // us reuse the codepath for user defined memory zones.
let zones = vec![MemoryZoneConfig { let zones = vec![MemoryZoneConfig {
@ -450,6 +476,7 @@ impl MemoryManager {
hugepages: config.hugepages, hugepages: config.hugepages,
host_numa_node: None, host_numa_node: None,
hotplug_size: config.hotplug_size, hotplug_size: config.hotplug_size,
hotplugged_size: config.hotplugged_size,
}]; }];
(config.size, zones) (config.size, zones)
@ -485,6 +512,32 @@ impl MemoryManager {
error!("Invalid to set ACPI hotplug method for memory zones"); error!("Invalid to set ACPI hotplug method for memory zones");
return Err(Error::InvalidHotplugMethodWithMemoryZones); return Err(Error::InvalidHotplugMethodWithMemoryZones);
} }
if let Some(hotplugged_size) = zone.hotplugged_size {
if let Some(hotplug_size) = zone.hotplug_size {
if hotplugged_size > hotplug_size {
error!(
"'hotplugged_size' {} can't be bigger than \
'hotplug_size' {}",
hotplugged_size, hotplug_size,
);
return Err(Error::InvalidMemoryParameters);
}
} else {
error!(
"Invalid to define 'hotplugged_size' when there is\
no 'hotplug_size' for a memory zone"
);
return Err(Error::InvalidMemoryParameters);
}
if config.hotplug_method == HotplugMethod::Acpi {
error!(
"Invalid to define 'hotplugged_size' with hotplug \
method 'acpi'"
);
return Err(Error::InvalidMemoryParameters);
}
}
} }
(total_ram_size, zones) (total_ram_size, zones)