Skip to content

Commit e000cf8

Browse files
committed
feat(virtio-mem): add GET API
Add support for GET /hotplug/memory that returns the current status of the virtio-mem device. This API can only be called after boot. Signed-off-by: Riccardo Mancini <[email protected]>
1 parent c19345e commit e000cf8

File tree

9 files changed

+100
-4
lines changed

9 files changed

+100
-4
lines changed

src/firecracker/src/api_server/parsed_request.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ 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;
30+
use crate::api_server::request::hotplug::memory::{
31+
parse_get_memory_hotplug, parse_put_memory_hotplug,
32+
};
3133
use crate::api_server::request::serial::parse_put_serial;
3234

3335
#[derive(Debug)]
@@ -85,6 +87,9 @@ impl TryFrom<&Request> for ParsedRequest {
8587
}
8688
(Method::Get, "machine-config", None) => parse_get_machine_config(),
8789
(Method::Get, "mmds", None) => parse_get_mmds(),
90+
(Method::Get, "hotplug", None) if path_tokens.next() == Some("memory") => {
91+
parse_get_memory_hotplug()
92+
}
8893
(Method::Get, _, Some(_)) => method_to_error(Method::Get),
8994
(Method::Put, "actions", Some(body)) => parse_put_actions(body),
9095
(Method::Put, "balloon", Some(body)) => parse_put_balloon(body),
@@ -177,6 +182,7 @@ impl ParsedRequest {
177182
Self::success_response_with_data(balloon_config)
178183
}
179184
VmmData::BalloonStats(stats) => Self::success_response_with_data(stats),
185+
VmmData::VirtioMemStatus(data) => Self::success_response_with_data(data),
180186
VmmData::InstanceInformation(info) => Self::success_response_with_data(info),
181187
VmmData::VmmVersion(version) => Self::success_response_with_data(
182188
&serde_json::json!({ "firecracker_version": version.as_str() }),
@@ -561,6 +567,9 @@ pub mod tests {
561567
VmmData::BalloonStats(stats) => {
562568
http_response(&serde_json::to_string(stats).unwrap(), 200)
563569
}
570+
VmmData::VirtioMemStatus(data) => {
571+
http_response(&serde_json::to_string(data).unwrap(), 200)
572+
}
564573
VmmData::Empty => http_response("", 204),
565574
VmmData::FullVmConfig(cfg) => {
566575
http_response(&serde_json::to_string(cfg).unwrap(), 200)

src/firecracker/src/api_server/request/hotplug/memory.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ pub(crate) fn parse_put_memory_hotplug(body: &Body) -> Result<ParsedRequest, Req
1818
)))
1919
}
2020

21+
pub(crate) fn parse_get_memory_hotplug() -> Result<ParsedRequest, RequestError> {
22+
METRICS.get_api_requests.hotplug_memory_count.inc();
23+
Ok(ParsedRequest::new_sync(VmmAction::GetMemoryHotplugStatus))
24+
}
25+
2126
#[cfg(test)]
2227
mod tests {
2328
use vmm::devices::virtio::mem::{VIRTIO_MEM_DEFAULT_BLOCK_SIZE, VIRTIO_MEM_DEFAULT_SLOT_SIZE};
@@ -66,4 +71,12 @@ mod tests {
6671
VmmAction::SetMemoryHotplugDevice(expected_config)
6772
);
6873
}
74+
75+
#[test]
76+
fn test_parse_parse_get_memory_hotplug_request() {
77+
assert_eq!(
78+
vmm_action_from_request(parse_get_memory_hotplug().unwrap()),
79+
VmmAction::GetMemoryHotplugStatus
80+
);
81+
}
6982
}

src/vmm/src/devices/virtio/mem/device.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,22 @@ pub struct VirtioMem {
6161
vm: Arc<Vm>,
6262
}
6363

64+
/// Memory hotplug device status information.
65+
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
66+
#[serde(deny_unknown_fields)]
67+
pub struct VirtioMemStatus {
68+
/// Block size in MiB.
69+
pub block_size_mib: usize,
70+
/// Total memory size in MiB that can be hotplugged.
71+
pub total_size_mib: usize,
72+
/// Size of the KVM slots in MiB.
73+
pub slot_size_mib: usize,
74+
/// Currently plugged memory size in MiB.
75+
pub plugged_size_mib: usize,
76+
/// Requested memory size in MiB.
77+
pub requested_size_mib: usize,
78+
}
79+
6480
impl VirtioMem {
6581
pub fn new(
6682
vm: Arc<Vm>,
@@ -132,6 +148,16 @@ impl VirtioMem {
132148
bytes_to_mib(self.config.requested_size.try_into().unwrap())
133149
}
134150

151+
pub fn status(&self) -> VirtioMemStatus {
152+
VirtioMemStatus {
153+
block_size_mib: self.block_size_mib(),
154+
total_size_mib: self.total_size_mib(),
155+
slot_size_mib: self.slot_size_mib(),
156+
plugged_size_mib: self.plugged_size_mib(),
157+
requested_size_mib: self.requested_size_mib(),
158+
}
159+
}
160+
135161
fn signal_used_queue(&self) -> Result<(), VirtioMemError> {
136162
self.interrupt_trigger()
137163
.trigger(VirtioInterruptType::Queue(MEM_QUEUE.try_into().unwrap()))
@@ -387,4 +413,20 @@ mod tests {
387413
mem.set_acked_features(456);
388414
assert_eq!(mem.acked_features(), 456);
389415
}
416+
417+
#[test]
418+
fn test_status() {
419+
let mut mem = default_virtio_mem();
420+
let status = mem.status();
421+
assert_eq!(
422+
status,
423+
VirtioMemStatus {
424+
block_size_mib: 2,
425+
total_size_mib: 1024,
426+
slot_size_mib: 128,
427+
plugged_size_mib: 0,
428+
requested_size_mib: 0,
429+
}
430+
);
431+
}
390432
}

src/vmm/src/devices/virtio/mem/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ mod event_handler;
66

77
use vm_memory::GuestAddress;
88

9-
pub use self::device::{VirtioMem, VirtioMemError};
9+
pub use self::device::{VirtioMem, VirtioMemError, VirtioMemStatus};
1010
use crate::arch::FIRST_ADDR_PAST_64BITS_MMIO;
1111

1212
pub(crate) const MEM_NUM_QUEUES: usize = 1;

src/vmm/src/lib.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ use vstate::vcpu::{self, StartThreadedError, VcpuSendEventError};
136136
use crate::cpu_config::templates::CpuConfiguration;
137137
use crate::devices::virtio::balloon::{BALLOON_DEV_ID, Balloon, BalloonConfig, BalloonStats};
138138
use crate::devices::virtio::block::device::Block;
139-
use crate::devices::virtio::mem::VirtioMemError;
139+
use crate::devices::virtio::mem::{VIRTIO_MEM_DEV_ID, VirtioMem, VirtioMemError, VirtioMemStatus};
140140
use crate::devices::virtio::net::Net;
141141
use crate::logger::{METRICS, MetricsError, error, info, warn};
142142
use crate::persist::{MicrovmState, MicrovmStateError, VmInfo};
@@ -594,6 +594,13 @@ impl Vmm {
594594
.map_err(VmmError::FindDeviceError)
595595
}
596596

597+
/// Returns the current state of the memory hotplug device.
598+
pub fn memory_hotplug_status(&self) -> Result<VirtioMemStatus, VmmError> {
599+
self.device_manager
600+
.with_virtio_device_with_id(VIRTIO_MEM_DEV_ID, |dev: &mut VirtioMem| dev.status())
601+
.map_err(VmmError::FindDeviceError)
602+
}
603+
597604
/// Signals Vmm to stop and exit.
598605
pub fn stop(&mut self, exit_code: FcExitCode) {
599606
// To avoid cycles, all teardown paths take the following route:

src/vmm/src/logger/metrics.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,8 @@ pub struct GetRequestsMetrics {
365365
pub mmds_count: SharedIncMetric,
366366
/// Number of GETs for getting the VMM version.
367367
pub vmm_version_count: SharedIncMetric,
368+
/// Number of GETs for getting hotpluggable memory status.
369+
pub hotplug_memory_count: SharedIncMetric,
368370
}
369371
impl GetRequestsMetrics {
370372
/// Const default construction.
@@ -374,6 +376,7 @@ impl GetRequestsMetrics {
374376
machine_cfg_count: SharedIncMetric::new(),
375377
mmds_count: SharedIncMetric::new(),
376378
vmm_version_count: SharedIncMetric::new(),
379+
hotplug_memory_count: SharedIncMetric::new(),
377380
}
378381
}
379382
}

src/vmm/src/rpc_interface.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use super::{Vmm, VmmError};
1414
use crate::EventManager;
1515
use crate::builder::StartMicrovmError;
1616
use crate::cpu_config::templates::{CustomCpuTemplate, GuestConfigError};
17+
use crate::devices::virtio::mem::VirtioMemStatus;
1718
use crate::logger::{LoggerConfig, info, warn, *};
1819
use crate::mmds::data_store::{self, Mmds};
1920
use crate::persist::{CreateSnapshotError, RestoreFromSnapshotError, VmInfo};
@@ -107,6 +108,8 @@ pub enum VmmAction {
107108
/// Set the entropy device using `EntropyDeviceConfig` as input. This action can only be called
108109
/// before the microVM has booted.
109110
SetEntropyDevice(EntropyDeviceConfig),
111+
/// Get the memory hotplug device configuration and status.
112+
GetMemoryHotplugStatus,
110113
/// Set the memory hotplug device using `MemoryHotplugConfig` as input. This action can only be
111114
/// called before the microVM has booted.
112115
SetMemoryHotplugDevice(MemoryHotplugConfig),
@@ -202,6 +205,8 @@ pub enum VmmData {
202205
InstanceInformation(InstanceInfo),
203206
/// The microVM version.
204207
VmmVersion(String),
208+
/// The status of the memory hotplug device.
209+
VirtioMemStatus(VirtioMemStatus),
205210
}
206211

207212
/// Trait used for deduplicating the MMDS request handling across the two ApiControllers.
@@ -460,6 +465,7 @@ impl<'a> PrebootApiController<'a> {
460465
| Pause
461466
| Resume
462467
| GetBalloonStats
468+
| GetMemoryHotplugStatus
463469
| UpdateBalloon(_)
464470
| UpdateBalloonStatistics(_)
465471
| UpdateBlockDevice(_)
@@ -664,6 +670,13 @@ impl RuntimeApiController {
664670
.map(VmmData::BalloonStats)
665671
.map_err(VmmActionError::InternalVmm),
666672
GetFullVmConfig => Ok(VmmData::FullVmConfig((&self.vm_resources).into())),
673+
GetMemoryHotplugStatus => self
674+
.vmm
675+
.lock()
676+
.expect("Poisoned lock")
677+
.memory_hotplug_status()
678+
.map(VmmData::VirtioMemStatus)
679+
.map_err(VmmActionError::InternalVmm),
667680
GetMMDS => self.get_mmds(),
668681
GetVmMachineConfig => Ok(VmmData::MachineConfiguration(
669682
self.vm_resources.machine_config.clone(),

tests/host_tools/fcmetrics.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ def validate_fc_metrics(metrics):
153153
"machine_cfg_count",
154154
"mmds_count",
155155
"vmm_version_count",
156+
"hotplug_memory_count",
156157
],
157158
"i8042": [
158159
"error_count",

tests/integration_tests/functional/test_api.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -998,13 +998,21 @@ def test_api_memory_hotplug(uvm_plain):
998998
# Omitting optional values should be ok
999999
test_microvm.api.memory_hotplug.put(total_size_mib=1024)
10001000

1001+
# Get API should be rejected before boot
1002+
with pytest.raises(AssertionError):
1003+
test_microvm.api.memory_hotplug.get()
1004+
10011005
# Start the microvm
10021006
test_microvm.start()
10031007

1004-
# API should be rejected after boot
1008+
# Put API should be rejected after boot
10051009
with pytest.raises(RuntimeError):
10061010
test_microvm.api.memory_hotplug.put(total_size_mib=1024)
10071011

1012+
# Get API should work after boot
1013+
status = test_microvm.api.memory_hotplug.get().json()
1014+
assert status["total_size_mib"] == 1024
1015+
10081016

10091017
def test_api_balloon(uvm_nano):
10101018
"""

0 commit comments

Comments
 (0)