Skip to content

Commit 7f2fbb2

Browse files
committed
feat(virtio-mem): add to VmmConfig and PUT API
Allow to configure the virtio-mem device from the VmmConfig and the PUT API to /hotplug/memory. Signed-off-by: Riccardo Mancini <[email protected]>
1 parent 9b8931e commit 7f2fbb2

File tree

13 files changed

+374
-3
lines changed

13 files changed

+374
-3
lines changed

src/firecracker/src/api_server/parsed_request.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ use super::request::net::{parse_patch_net, parse_put_net};
2727
use super::request::snapshot::{parse_patch_vm_state, parse_put_snapshot};
2828
use super::request::version::parse_get_version;
2929
use super::request::vsock::parse_put_vsock;
30+
use crate::api_server::request::hotplug::parse_hotplug;
3031

3132
#[derive(Debug)]
3233
pub(crate) enum RequestAction {
@@ -109,6 +110,7 @@ impl TryFrom<&Request> for ParsedRequest {
109110
}
110111
(Method::Patch, "vm", Some(body)) => parse_patch_vm_state(body),
111112
(Method::Patch, _, None) => method_to_error(Method::Patch),
113+
(method, "hotplug", body) => parse_hotplug(method, path_tokens.next(), body),
112114
(method, unknown_uri, _) => Err(RequestError::InvalidPathMethod(
113115
unknown_uri.to_string(),
114116
method,
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use micro_http::Body;
5+
use vmm::rpc_interface::VmmAction;
6+
use vmm::vmm_config::memory_hotplug::MemoryHotplugConfig;
7+
8+
use crate::api_server::parsed_request::{ParsedRequest, RequestError};
9+
10+
pub(crate) fn parse_put_memory_hotplug(body: &Body) -> Result<ParsedRequest, RequestError> {
11+
Ok(ParsedRequest::new_sync(VmmAction::SetMemoryHotplugDevice(
12+
serde_json::from_slice::<MemoryHotplugConfig>(body.raw())?,
13+
)))
14+
}
15+
16+
#[cfg(test)]
17+
mod tests {
18+
use vmm::devices::virtio::mem::{VIRTIO_MEM_DEFAULT_BLOCK_SIZE, VIRTIO_MEM_DEFAULT_SLOT_SIZE};
19+
use vmm::utils::bytes_to_mib;
20+
21+
use super::*;
22+
use crate::api_server::parsed_request::tests::vmm_action_from_request;
23+
24+
#[test]
25+
fn test_parse_put_memory_hotplug_request() {
26+
parse_put_memory_hotplug(&Body::new("invalid_payload")).unwrap_err();
27+
28+
// PUT with invalid fields.
29+
let body = r#"{
30+
"total_size_mib": "bar"
31+
}"#;
32+
parse_put_memory_hotplug(&Body::new(body)).unwrap_err();
33+
34+
// PUT with valid input fields with defaults.
35+
let body = r#"{
36+
"total_size_mib": 2048
37+
}"#;
38+
let expected_config = MemoryHotplugConfig {
39+
total_size_mib: 2048,
40+
block_size_mib: bytes_to_mib(VIRTIO_MEM_DEFAULT_BLOCK_SIZE),
41+
slot_size_mib: bytes_to_mib(VIRTIO_MEM_DEFAULT_SLOT_SIZE),
42+
};
43+
assert_eq!(
44+
vmm_action_from_request(parse_put_memory_hotplug(&Body::new(body)).unwrap()),
45+
VmmAction::SetMemoryHotplugDevice(expected_config)
46+
);
47+
48+
// PUT with valid input fields.
49+
let body = r#"{
50+
"total_size_mib": 2048,
51+
"block_size_mib": 64,
52+
"slot_size_mib": 64
53+
}"#;
54+
let expected_config = MemoryHotplugConfig {
55+
total_size_mib: 2048,
56+
block_size_mib: 64,
57+
slot_size_mib: 64,
58+
};
59+
assert_eq!(
60+
vmm_action_from_request(parse_put_memory_hotplug(&Body::new(body)).unwrap()),
61+
VmmAction::SetMemoryHotplugDevice(expected_config)
62+
);
63+
}
64+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
mod memory;
5+
6+
use micro_http::{Body, Method};
7+
8+
use crate::api_server::parsed_request::{ParsedRequest, RequestError};
9+
use crate::api_server::request::hotplug::memory::parse_put_memory_hotplug;
10+
11+
pub(crate) fn parse_hotplug(
12+
method: Method,
13+
token: Option<&str>,
14+
body: Option<&Body>,
15+
) -> Result<ParsedRequest, RequestError> {
16+
let token =
17+
token.ok_or_else(|| RequestError::InvalidPathMethod("hotplug".to_string(), method))?;
18+
match (method, token, body) {
19+
(Method::Put, "memory", Some(body)) => parse_put_memory_hotplug(body),
20+
(method, unknown_uri, _) => Err(RequestError::InvalidPathMethod(
21+
unknown_uri.to_string(),
22+
method,
23+
)),
24+
}
25+
}

src/firecracker/src/api_server/request/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ pub mod boot_source;
77
pub mod cpu_configuration;
88
pub mod drive;
99
pub mod entropy;
10+
pub mod hotplug;
1011
pub mod instance_info;
1112
pub mod logger;
1213
pub mod machine_configuration;

src/vmm/src/device_manager/pci_mngr.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,7 @@ mod tests {
670670
let _restored_dev_manager =
671671
PciDevices::restore(restore_args, &device_manager_state.pci_state).unwrap();
672672

673+
// TODO(virtio-mem): add memory-hotplug device when snapshot-restore is implemented
673674
let expected_vm_resources = format!(
674675
r#"{{
675676
"balloon": {{
@@ -728,7 +729,8 @@ mod tests {
728729
}},
729730
"entropy": {{
730731
"rate_limiter": null
731-
}}
732+
}},
733+
"memory-hotplug": null
732734
}}"#,
733735
_block_files.last().unwrap().as_path().to_str().unwrap(),
734736
tmp_sock_file.as_path().to_str().unwrap()

src/vmm/src/device_manager/persist.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,7 @@ mod tests {
696696
let _restored_dev_manager =
697697
MMIODeviceManager::restore(restore_args, &device_manager_state.mmio_state).unwrap();
698698

699+
// TODO(virtio-mem): add memory-hotplug device when snapshot-restore is implemented
699700
let expected_vm_resources = format!(
700701
r#"{{
701702
"balloon": {{
@@ -754,7 +755,8 @@ mod tests {
754755
}},
755756
"entropy": {{
756757
"rate_limiter": null
757-
}}
758+
}},
759+
"memory-hotplug": null
758760
}}"#,
759761
_block_files.last().unwrap().as_path().to_str().unwrap(),
760762
tmp_sock_file.as_path().to_str().unwrap()

src/vmm/src/resources.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use crate::vmm_config::instance_info::InstanceInfo;
2525
use crate::vmm_config::machine_config::{
2626
HugePageConfig, MachineConfig, MachineConfigError, MachineConfigUpdate,
2727
};
28+
use crate::vmm_config::memory_hotplug::{MemoryHotplugConfig, MemoryHotplugConfigError};
2829
use crate::vmm_config::metrics::{MetricsConfig, MetricsConfigError, init_metrics};
2930
use crate::vmm_config::mmds::{MmdsConfig, MmdsConfigError};
3031
use crate::vmm_config::net::*;
@@ -61,6 +62,8 @@ pub enum ResourcesError {
6162
VsockDevice(#[from] VsockConfigError),
6263
/// Entropy device error: {0}
6364
EntropyDevice(#[from] EntropyDeviceError),
65+
/// Memory hotplug config error: {0}
66+
MemoryHotplugConfig(#[from] MemoryHotplugConfigError),
6467
}
6568

6669
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
@@ -86,6 +89,7 @@ pub struct VmmConfig {
8689
network_interfaces: Vec<NetworkInterfaceConfig>,
8790
vsock: Option<VsockDeviceConfig>,
8891
entropy: Option<EntropyDeviceConfig>,
92+
memory_hotplug: Option<MemoryHotplugConfig>,
8993
}
9094

9195
/// A data structure that encapsulates the device configurations
@@ -106,6 +110,8 @@ pub struct VmResources {
106110
pub net_builder: NetBuilder,
107111
/// The entropy device builder.
108112
pub entropy: EntropyDeviceBuilder,
113+
/// The memory hotplug configuration.
114+
pub memory_hotplug: Option<MemoryHotplugConfig>,
109115
/// The optional Mmds data store.
110116
// This is initialised on demand (if ever used), so that we don't allocate it unless it's
111117
// actually used.
@@ -193,6 +199,10 @@ impl VmResources {
193199
resources.build_entropy_device(entropy_device_config)?;
194200
}
195201

202+
if let Some(memory_hotplug_config) = vmm_config.memory_hotplug {
203+
resources.set_memory_hotplug_config(memory_hotplug_config)?;
204+
}
205+
196206
Ok(resources)
197207
}
198208

@@ -380,6 +390,16 @@ impl VmResources {
380390
self.entropy.insert(body)
381391
}
382392

393+
/// Sets the memory hotplug configuration.
394+
pub fn set_memory_hotplug_config(
395+
&mut self,
396+
config: MemoryHotplugConfig,
397+
) -> Result<(), MemoryHotplugConfigError> {
398+
config.validate()?;
399+
self.memory_hotplug = Some(config);
400+
Ok(())
401+
}
402+
383403
/// Setter for mmds config.
384404
pub fn set_mmds_config(
385405
&mut self,
@@ -506,6 +526,7 @@ impl From<&VmResources> for VmmConfig {
506526
network_interfaces: resources.net_builder.configs(),
507527
vsock: resources.vsock.config(),
508528
entropy: resources.entropy.config(),
529+
memory_hotplug: resources.memory_hotplug.clone(),
509530
}
510531
}
511532
}
@@ -617,6 +638,7 @@ mod tests {
617638
mmds_size_limit: HTTP_MAX_PAYLOAD_SIZE,
618639
entropy: Default::default(),
619640
pci_enabled: false,
641+
memory_hotplug: Default::default(),
620642
}
621643
}
622644

src/vmm/src/rpc_interface.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ use crate::vmm_config::drive::{BlockDeviceConfig, BlockDeviceUpdateConfig, Drive
2828
use crate::vmm_config::entropy::{EntropyDeviceConfig, EntropyDeviceError};
2929
use crate::vmm_config::instance_info::InstanceInfo;
3030
use crate::vmm_config::machine_config::{MachineConfig, MachineConfigError, MachineConfigUpdate};
31+
use crate::vmm_config::memory_hotplug::{MemoryHotplugConfig, MemoryHotplugConfigError};
3132
use crate::vmm_config::metrics::{MetricsConfig, MetricsConfigError};
3233
use crate::vmm_config::mmds::{MmdsConfig, MmdsConfigError};
3334
use crate::vmm_config::net::{
@@ -103,6 +104,9 @@ pub enum VmmAction {
103104
/// Set the entropy device using `EntropyDeviceConfig` as input. This action can only be called
104105
/// before the microVM has booted.
105106
SetEntropyDevice(EntropyDeviceConfig),
107+
/// Set the memory hotplug device using `MemoryHotplugConfig` as input. This action can only be
108+
/// called before the microVM has booted.
109+
SetMemoryHotplugDevice(MemoryHotplugConfig),
106110
/// Launch the microVM. This action can only be called before the microVM has booted.
107111
StartMicroVm,
108112
/// Send CTRL+ALT+DEL to the microVM, using the i8042 keyboard function. If an AT-keyboard
@@ -138,6 +142,8 @@ pub enum VmmActionError {
138142
DriveConfig(#[from] DriveError),
139143
/// Entropy device error: {0}
140144
EntropyDevice(#[from] EntropyDeviceError),
145+
/// Memory hotplug config error: {0}
146+
MemoryHotplugConfig(#[from] MemoryHotplugConfigError),
141147
/// Internal VMM error: {0}
142148
InternalVmm(#[from] VmmError),
143149
/// Load snapshot error: {0}
@@ -438,6 +444,7 @@ impl<'a> PrebootApiController<'a> {
438444
StartMicroVm => self.start_microvm(),
439445
UpdateMachineConfiguration(config) => self.update_machine_config(config),
440446
SetEntropyDevice(config) => self.set_entropy_device(config),
447+
SetMemoryHotplugDevice(config) => self.set_memory_hotplug_device(config),
441448
// Operations not allowed pre-boot.
442449
CreateSnapshot(_)
443450
| FlushMetrics
@@ -537,6 +544,16 @@ impl<'a> PrebootApiController<'a> {
537544
Ok(VmmData::Empty)
538545
}
539546

547+
fn set_memory_hotplug_device(
548+
&mut self,
549+
cfg: MemoryHotplugConfig,
550+
) -> Result<VmmData, VmmActionError> {
551+
self.boot_path = true;
552+
cfg.validate()?;
553+
self.vm_resources.set_memory_hotplug_config(cfg)?;
554+
Ok(VmmData::Empty)
555+
}
556+
540557
// On success, this command will end the pre-boot stage and this controller
541558
// will be replaced by a runtime controller.
542559
fn start_microvm(&mut self) -> Result<VmmData, VmmActionError> {
@@ -684,6 +701,7 @@ impl RuntimeApiController {
684701
| SetVsockDevice(_)
685702
| SetMmdsConfiguration(_)
686703
| SetEntropyDevice(_)
704+
| SetMemoryHotplugDevice(_)
687705
| StartMicroVm
688706
| UpdateMachineConfiguration(_) => Err(VmmActionError::OperationNotSupportedPostBoot),
689707
}
@@ -1262,5 +1280,8 @@ mod tests {
12621280
check_unsupported(runtime_request(VmmAction::SetEntropyDevice(
12631281
EntropyDeviceConfig::default(),
12641282
)));
1283+
check_unsupported(runtime_request(VmmAction::SetMemoryHotplugDevice(
1284+
MemoryHotplugConfig::default(),
1285+
)));
12651286
}
12661287
}

src/vmm/src/utils/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ pub const fn mib_to_bytes(mib: usize) -> usize {
5454
mib << MIB_TO_BYTES_SHIFT
5555
}
5656

57+
/// Converts Bytes to MiB, truncating any remainder
58+
pub const fn bytes_to_mib(bytes: usize) -> usize {
59+
bytes >> MIB_TO_BYTES_SHIFT
60+
}
61+
5762
/// Align address up to the aligment.
5863
pub const fn align_up(addr: u64, align: u64) -> u64 {
5964
debug_assert!(align != 0);

0 commit comments

Comments
 (0)