Skip to content

Commit ba8533a

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 5b8a5a0 commit ba8533a

File tree

15 files changed

+367
-3
lines changed

15 files changed

+367
-3
lines changed

src/firecracker/src/api_server/parsed_request.rs

Lines changed: 4 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::memory::parse_put_memory_hotplug;
3031
use crate::api_server::request::serial::parse_put_serial;
3132

3233
#[derive(Debug)]
@@ -101,6 +102,9 @@ impl TryFrom<&Request> for ParsedRequest {
101102
(Method::Put, "snapshot", Some(body)) => parse_put_snapshot(body, path_tokens.next()),
102103
(Method::Put, "vsock", Some(body)) => parse_put_vsock(body),
103104
(Method::Put, "entropy", Some(body)) => parse_put_entropy(body),
105+
(Method::Put, "hotplug", Some(body)) if path_tokens.next() == Some("memory") => {
106+
parse_put_memory_hotplug(body)
107+
}
104108
(Method::Put, _, None) => method_to_error(Method::Put),
105109
(Method::Patch, "balloon", Some(body)) => parse_patch_balloon(body, path_tokens.next()),
106110
(Method::Patch, "drives", Some(body)) => parse_patch_drive(body, path_tokens.next()),
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
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::logger::{IncMetric, METRICS};
6+
use vmm::rpc_interface::VmmAction;
7+
use vmm::vmm_config::memory_hotplug::MemoryHotplugConfig;
8+
9+
use crate::api_server::parsed_request::{ParsedRequest, RequestError};
10+
11+
pub(crate) fn parse_put_memory_hotplug(body: &Body) -> Result<ParsedRequest, RequestError> {
12+
METRICS.put_api_requests.hotplug_memory_count.inc();
13+
let config = serde_json::from_slice::<MemoryHotplugConfig>(body.raw()).inspect_err(|_| {
14+
METRICS.put_api_requests.hotplug_memory_fails.inc();
15+
})?;
16+
Ok(ParsedRequest::new_sync(VmmAction::SetMemoryHotplugDevice(
17+
config,
18+
)))
19+
}
20+
21+
#[cfg(test)]
22+
mod tests {
23+
use vmm::devices::virtio::mem::{VIRTIO_MEM_DEFAULT_BLOCK_SIZE, VIRTIO_MEM_DEFAULT_SLOT_SIZE};
24+
use vmm::utils::bytes_to_mib;
25+
26+
use super::*;
27+
use crate::api_server::parsed_request::tests::vmm_action_from_request;
28+
29+
#[test]
30+
fn test_parse_put_memory_hotplug_request() {
31+
parse_put_memory_hotplug(&Body::new("invalid_payload")).unwrap_err();
32+
33+
// PUT with invalid fields.
34+
let body = r#"{
35+
"total_size_mib": "bar"
36+
}"#;
37+
parse_put_memory_hotplug(&Body::new(body)).unwrap_err();
38+
39+
// PUT with valid input fields with defaults.
40+
let body = r#"{
41+
"total_size_mib": 2048
42+
}"#;
43+
let expected_config = MemoryHotplugConfig {
44+
total_size_mib: 2048,
45+
block_size_mib: bytes_to_mib(VIRTIO_MEM_DEFAULT_BLOCK_SIZE),
46+
slot_size_mib: bytes_to_mib(VIRTIO_MEM_DEFAULT_SLOT_SIZE),
47+
};
48+
assert_eq!(
49+
vmm_action_from_request(parse_put_memory_hotplug(&Body::new(body)).unwrap()),
50+
VmmAction::SetMemoryHotplugDevice(expected_config)
51+
);
52+
53+
// PUT with valid input fields.
54+
let body = r#"{
55+
"total_size_mib": 2048,
56+
"block_size_mib": 64,
57+
"slot_size_mib": 64
58+
}"#;
59+
let expected_config = MemoryHotplugConfig {
60+
total_size_mib: 2048,
61+
block_size_mib: 64,
62+
slot_size_mib: 64,
63+
};
64+
assert_eq!(
65+
vmm_action_from_request(parse_put_memory_hotplug(&Body::new(body)).unwrap()),
66+
VmmAction::SetMemoryHotplugDevice(expected_config)
67+
);
68+
}
69+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
pub mod memory;

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
@@ -666,6 +666,7 @@ mod tests {
666666
let _restored_dev_manager =
667667
PciDevices::restore(restore_args, &device_manager_state.pci_state).unwrap();
668668

669+
// TODO(virtio-mem): add memory-hotplug device when snapshot-restore is implemented
669670
let expected_vm_resources = format!(
670671
r#"{{
671672
"balloon": {{
@@ -724,7 +725,8 @@ mod tests {
724725
}},
725726
"entropy": {{
726727
"rate_limiter": null
727-
}}
728+
}},
729+
"memory-hotplug": null
728730
}}"#,
729731
_block_files.last().unwrap().as_path().to_str().unwrap(),
730732
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
@@ -687,6 +687,7 @@ mod tests {
687687
let _restored_dev_manager =
688688
MMIODeviceManager::restore(restore_args, &device_manager_state.mmio_state).unwrap();
689689

690+
// TODO(virtio-mem): add memory-hotplug device when snapshot-restore is implemented
690691
let expected_vm_resources = format!(
691692
r#"{{
692693
"balloon": {{
@@ -745,7 +746,8 @@ mod tests {
745746
}},
746747
"entropy": {{
747748
"rate_limiter": null
748-
}}
749+
}},
750+
"memory-hotplug": null
749751
}}"#,
750752
_block_files.last().unwrap().as_path().to_str().unwrap(),
751753
tmp_sock_file.as_path().to_str().unwrap()

src/vmm/src/logger/metrics.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,10 @@ pub struct PutRequestsMetrics {
419419
pub serial_count: SharedIncMetric,
420420
/// Number of failed PUTs to /serial
421421
pub serial_fails: SharedIncMetric,
422+
/// Number of PUTs to /hotplug/memory
423+
pub hotplug_memory_count: SharedIncMetric,
424+
/// Number of failed PUTs to /hotplug/memory
425+
pub hotplug_memory_fails: SharedIncMetric,
422426
}
423427
impl PutRequestsMetrics {
424428
/// Const default construction.
@@ -446,6 +450,8 @@ impl PutRequestsMetrics {
446450
vsock_fails: SharedIncMetric::new(),
447451
serial_count: SharedIncMetric::new(),
448452
serial_fails: SharedIncMetric::new(),
453+
hotplug_memory_count: SharedIncMetric::new(),
454+
hotplug_memory_fails: SharedIncMetric::new(),
449455
}
450456
}
451457
}

src/vmm/src/resources.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use crate::vmm_config::instance_info::InstanceInfo;
2626
use crate::vmm_config::machine_config::{
2727
HugePageConfig, MachineConfig, MachineConfigError, MachineConfigUpdate,
2828
};
29+
use crate::vmm_config::memory_hotplug::{MemoryHotplugConfig, MemoryHotplugConfigError};
2930
use crate::vmm_config::metrics::{MetricsConfig, MetricsConfigError, init_metrics};
3031
use crate::vmm_config::mmds::{MmdsConfig, MmdsConfigError};
3132
use crate::vmm_config::net::*;
@@ -63,6 +64,8 @@ pub enum ResourcesError {
6364
VsockDevice(#[from] VsockConfigError),
6465
/// Entropy device error: {0}
6566
EntropyDevice(#[from] EntropyDeviceError),
67+
/// Memory hotplug config error: {0}
68+
MemoryHotplugConfig(#[from] MemoryHotplugConfigError),
6669
}
6770

6871
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
@@ -90,6 +93,7 @@ pub struct VmmConfig {
9093
entropy: Option<EntropyDeviceConfig>,
9194
#[serde(skip)]
9295
serial_config: Option<SerialConfig>,
96+
memory_hotplug: Option<MemoryHotplugConfig>,
9397
}
9498

9599
/// A data structure that encapsulates the device configurations
@@ -110,6 +114,8 @@ pub struct VmResources {
110114
pub net_builder: NetBuilder,
111115
/// The entropy device builder.
112116
pub entropy: EntropyDeviceBuilder,
117+
/// The memory hotplug configuration.
118+
pub memory_hotplug: Option<MemoryHotplugConfig>,
113119
/// The optional Mmds data store.
114120
// This is initialised on demand (if ever used), so that we don't allocate it unless it's
115121
// actually used.
@@ -203,6 +209,10 @@ impl VmResources {
203209
resources.serial_out_path = serial_cfg.serial_out_path;
204210
}
205211

212+
if let Some(memory_hotplug_config) = vmm_config.memory_hotplug {
213+
resources.set_memory_hotplug_config(memory_hotplug_config)?;
214+
}
215+
206216
Ok(resources)
207217
}
208218

@@ -390,6 +400,16 @@ impl VmResources {
390400
self.entropy.insert(body)
391401
}
392402

403+
/// Sets the memory hotplug configuration.
404+
pub fn set_memory_hotplug_config(
405+
&mut self,
406+
config: MemoryHotplugConfig,
407+
) -> Result<(), MemoryHotplugConfigError> {
408+
config.validate()?;
409+
self.memory_hotplug = Some(config);
410+
Ok(())
411+
}
412+
393413
/// Setter for mmds config.
394414
pub fn set_mmds_config(
395415
&mut self,
@@ -526,6 +546,7 @@ impl From<&VmResources> for VmmConfig {
526546
entropy: resources.entropy.config(),
527547
// serial_config is marked serde(skip) so that it doesnt end up in snapshots.
528548
serial_config: None,
549+
memory_hotplug: resources.memory_hotplug.clone(),
529550
}
530551
}
531552
}
@@ -638,6 +659,7 @@ mod tests {
638659
entropy: Default::default(),
639660
pci_enabled: false,
640661
serial_out_path: None,
662+
memory_hotplug: Default::default(),
641663
}
642664
}
643665

src/vmm/src/rpc_interface.rs

Lines changed: 20 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::{
@@ -106,6 +107,9 @@ pub enum VmmAction {
106107
/// Set the entropy device using `EntropyDeviceConfig` as input. This action can only be called
107108
/// before the microVM has booted.
108109
SetEntropyDevice(EntropyDeviceConfig),
110+
/// Set the memory hotplug device using `MemoryHotplugConfig` as input. This action can only be
111+
/// called before the microVM has booted.
112+
SetMemoryHotplugDevice(MemoryHotplugConfig),
109113
/// Launch the microVM. This action can only be called before the microVM has booted.
110114
StartMicroVm,
111115
/// Send CTRL+ALT+DEL to the microVM, using the i8042 keyboard function. If an AT-keyboard
@@ -143,6 +147,8 @@ pub enum VmmActionError {
143147
DriveConfig(#[from] DriveError),
144148
/// Entropy device error: {0}
145149
EntropyDevice(#[from] EntropyDeviceError),
150+
/// Memory hotplug config error: {0}
151+
MemoryHotplugConfig(#[from] MemoryHotplugConfigError),
146152
/// Internal VMM error: {0}
147153
InternalVmm(#[from] VmmError),
148154
/// Load snapshot error: {0}
@@ -447,6 +453,7 @@ impl<'a> PrebootApiController<'a> {
447453
StartMicroVm => self.start_microvm(),
448454
UpdateMachineConfiguration(config) => self.update_machine_config(config),
449455
SetEntropyDevice(config) => self.set_entropy_device(config),
456+
SetMemoryHotplugDevice(config) => self.set_memory_hotplug_device(config),
450457
// Operations not allowed pre-boot.
451458
CreateSnapshot(_)
452459
| FlushMetrics
@@ -546,6 +553,15 @@ impl<'a> PrebootApiController<'a> {
546553
Ok(VmmData::Empty)
547554
}
548555

556+
fn set_memory_hotplug_device(
557+
&mut self,
558+
cfg: MemoryHotplugConfig,
559+
) -> Result<VmmData, VmmActionError> {
560+
self.boot_path = true;
561+
self.vm_resources.set_memory_hotplug_config(cfg)?;
562+
Ok(VmmData::Empty)
563+
}
564+
549565
// On success, this command will end the pre-boot stage and this controller
550566
// will be replaced by a runtime controller.
551567
fn start_microvm(&mut self) -> Result<VmmData, VmmActionError> {
@@ -694,6 +710,7 @@ impl RuntimeApiController {
694710
| SetVsockDevice(_)
695711
| SetMmdsConfiguration(_)
696712
| SetEntropyDevice(_)
713+
| SetMemoryHotplugDevice(_)
697714
| StartMicroVm
698715
| UpdateMachineConfiguration(_) => Err(VmmActionError::OperationNotSupportedPostBoot),
699716
}
@@ -1272,5 +1289,8 @@ mod tests {
12721289
check_unsupported(runtime_request(VmmAction::SetEntropyDevice(
12731290
EntropyDeviceConfig::default(),
12741291
)));
1292+
check_unsupported(runtime_request(VmmAction::SetMemoryHotplugDevice(
1293+
MemoryHotplugConfig::default(),
1294+
)));
12751295
}
12761296
}

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)