Skip to content

Commit 11ab91d

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 d9ab0f2 commit 11ab91d

File tree

8 files changed

+94
-4
lines changed

8 files changed

+94
-4
lines changed

src/firecracker/src/api_server/parsed_request.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ impl ParsedRequest {
173173
Self::success_response_with_data(balloon_config)
174174
}
175175
VmmData::BalloonStats(stats) => Self::success_response_with_data(stats),
176+
VmmData::VirtioMemStatus(data) => Self::success_response_with_data(data),
176177
VmmData::InstanceInformation(info) => Self::success_response_with_data(info),
177178
VmmData::VmmVersion(version) => Self::success_response_with_data(
178179
&serde_json::json!({ "firecracker_version": version.as_str() }),
@@ -557,6 +558,9 @@ pub mod tests {
557558
VmmData::BalloonStats(stats) => {
558559
http_response(&serde_json::to_string(stats).unwrap(), 200)
559560
}
561+
VmmData::VirtioMemStatus(data) => {
562+
http_response(&serde_json::to_string(data).unwrap(), 200)
563+
}
560564
VmmData::Empty => http_response("", 204),
561565
VmmData::FullVmConfig(cfg) => {
562566
http_response(&serde_json::to_string(cfg).unwrap(), 200)

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ pub(crate) fn parse_put_memory_hotplug(body: &Body) -> Result<ParsedRequest, Req
1313
)))
1414
}
1515

16+
pub(crate) fn parse_get_memory_hotplug() -> Result<ParsedRequest, RequestError> {
17+
Ok(ParsedRequest::new_sync(VmmAction::GetMemoryHotplugStatus))
18+
}
19+
1620
#[cfg(test)]
1721
mod tests {
1822
use vmm::devices::virtio::mem::{VIRTIO_MEM_DEFAULT_BLOCK_SIZE, VIRTIO_MEM_DEFAULT_SLOT_SIZE};
@@ -61,4 +65,12 @@ mod tests {
6165
VmmAction::SetMemoryHotplugDevice(expected_config)
6266
);
6367
}
68+
69+
#[test]
70+
fn test_parse_parse_get_memory_hotplug_request() {
71+
assert_eq!(
72+
vmm_action_from_request(parse_get_memory_hotplug().unwrap()),
73+
VmmAction::GetMemoryHotplugStatus
74+
);
75+
}
6476
}

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ mod memory;
66
use micro_http::{Body, Method};
77

88
use crate::api_server::parsed_request::{ParsedRequest, RequestError};
9-
use crate::api_server::request::hotplug::memory::parse_put_memory_hotplug;
9+
use crate::api_server::request::hotplug::memory::{
10+
parse_get_memory_hotplug, parse_put_memory_hotplug,
11+
};
1012

1113
pub(crate) fn parse_hotplug(
1214
method: Method,
@@ -16,6 +18,7 @@ pub(crate) fn parse_hotplug(
1618
let token =
1719
token.ok_or_else(|| RequestError::InvalidPathMethod("hotplug".to_string(), method))?;
1820
match (method, token, body) {
21+
(Method::Get, "memory", None) => parse_get_memory_hotplug(),
1922
(Method::Put, "memory", Some(body)) => parse_put_memory_hotplug(body),
2023
(method, unknown_uri, _) => Err(RequestError::InvalidPathMethod(
2124
unknown_uri.to_string(),

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

Lines changed: 39 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()))
@@ -389,4 +415,17 @@ mod tests {
389415
mem.set_acked_features(456);
390416
assert_eq!(mem.acked_features(), 456);
391417
}
418+
419+
#[test]
420+
fn test_status() {
421+
let mut mem = default_virtio_mem();
422+
let status = mem.status();
423+
assert_eq!(status, 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+
}
392431
}

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: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ 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;
139139
use crate::devices::virtio::generated::virtio_ids;
140-
use crate::devices::virtio::mem::VirtioMemError;
140+
use crate::devices::virtio::mem::{VIRTIO_MEM_DEV_ID, VirtioMem, VirtioMemError, VirtioMemStatus};
141141
use crate::devices::virtio::net::Net;
142142
use crate::logger::{METRICS, MetricsError, error, info, warn};
143143
use crate::persist::{MicrovmState, MicrovmStateError, VmInfo};
@@ -621,6 +621,17 @@ impl Vmm {
621621
.map_err(VmmError::FindDeviceError)
622622
}
623623

624+
/// Returns the current state of the memory hotplug device.
625+
pub fn memory_hotplug_status(&self) -> Result<VirtioMemStatus, VmmError> {
626+
self.device_manager
627+
.with_virtio_device_with_id(
628+
virtio_ids::VIRTIO_ID_MEM,
629+
VIRTIO_MEM_DEV_ID,
630+
|dev: &mut VirtioMem| dev.status(),
631+
)
632+
.map_err(VmmError::FindDeviceError)
633+
}
634+
624635
/// Signals Vmm to stop and exit.
625636
pub fn stop(&mut self, exit_code: FcExitCode) {
626637
// To avoid cycles, all teardown paths take the following route:

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};
@@ -104,6 +105,8 @@ pub enum VmmAction {
104105
/// Set the entropy device using `EntropyDeviceConfig` as input. This action can only be called
105106
/// before the microVM has booted.
106107
SetEntropyDevice(EntropyDeviceConfig),
108+
/// Get the memory hotplug device configuration and status.
109+
GetMemoryHotplugStatus,
107110
/// Set the memory hotplug device using `MemoryHotplugConfig` as input. This action can only be
108111
/// called before the microVM has booted.
109112
SetMemoryHotplugDevice(MemoryHotplugConfig),
@@ -199,6 +202,8 @@ pub enum VmmData {
199202
InstanceInformation(InstanceInfo),
200203
/// The microVM version.
201204
VmmVersion(String),
205+
/// The status of the memory hotplug device.
206+
VirtioMemStatus(VirtioMemStatus),
202207
}
203208

204209
/// Trait used for deduplicating the MMDS request handling across the two ApiControllers.
@@ -453,6 +458,7 @@ impl<'a> PrebootApiController<'a> {
453458
| Pause
454459
| Resume
455460
| GetBalloonStats
461+
| GetMemoryHotplugStatus
456462
| UpdateBalloon(_)
457463
| UpdateBalloonStatistics(_)
458464
| UpdateBlockDevice(_)
@@ -658,6 +664,13 @@ impl RuntimeApiController {
658664
.map(VmmData::BalloonStats)
659665
.map_err(VmmActionError::InternalVmm),
660666
GetFullVmConfig => Ok(VmmData::FullVmConfig((&self.vm_resources).into())),
667+
GetMemoryHotplugStatus => self
668+
.vmm
669+
.lock()
670+
.expect("Poisoned lock")
671+
.memory_hotplug_status()
672+
.map(VmmData::VirtioMemStatus)
673+
.map_err(VmmActionError::InternalVmm),
661674
GetMMDS => self.get_mmds(),
662675
GetVmMachineConfig => Ok(VmmData::MachineConfiguration(
663676
self.vm_resources.machine_config.clone(),

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)