Skip to content

Commit f78ab80

Browse files
committed
feat(virtio-mem): wire PATCH support
Wire up PATCH requests with the virtio-mem device. All the validation is performed in the device, but the actual operation is not yet implemented. Signed-off-by: Riccardo Mancini <[email protected]>
1 parent fe46199 commit f78ab80

File tree

7 files changed

+127
-4
lines changed

7 files changed

+127
-4
lines changed

src/firecracker/src/api_server/parsed_request.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ 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;
3030
use crate::api_server::request::hotplug::memory::{
31-
parse_get_memory_hotplug, parse_put_memory_hotplug,
31+
parse_get_memory_hotplug, parse_patch_memory_hotplug, parse_put_memory_hotplug,
3232
};
3333
use crate::api_server::request::serial::parse_put_serial;
3434

@@ -119,6 +119,9 @@ impl TryFrom<&Request> for ParsedRequest {
119119
parse_patch_net(body, path_tokens.next())
120120
}
121121
(Method::Patch, "vm", Some(body)) => parse_patch_vm_state(body),
122+
(Method::Patch, "hotplug", Some(body)) if path_tokens.next() == Some("memory") => {
123+
parse_patch_memory_hotplug(body)
124+
}
122125
(Method::Patch, _, None) => method_to_error(Method::Patch),
123126
(method, unknown_uri, _) => Err(RequestError::InvalidPathMethod(
124127
unknown_uri.to_string(),

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

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
use micro_http::Body;
55
use vmm::logger::{IncMetric, METRICS};
66
use vmm::rpc_interface::VmmAction;
7-
use vmm::vmm_config::memory_hotplug::MemoryHotplugConfig;
7+
use vmm::vmm_config::memory_hotplug::{MemoryHotplugConfig, MemoryHotplugSizeUpdate};
88

99
use crate::api_server::parsed_request::{ParsedRequest, RequestError};
1010

@@ -23,11 +23,23 @@ pub(crate) fn parse_get_memory_hotplug() -> Result<ParsedRequest, RequestError>
2323
Ok(ParsedRequest::new_sync(VmmAction::GetMemoryHotplugStatus))
2424
}
2525

26+
pub(crate) fn parse_patch_memory_hotplug(body: &Body) -> Result<ParsedRequest, RequestError> {
27+
METRICS.patch_api_requests.hotplug_memory_count.inc();
28+
let config =
29+
serde_json::from_slice::<MemoryHotplugSizeUpdate>(body.raw()).inspect_err(|_| {
30+
METRICS.patch_api_requests.hotplug_memory_fails.inc();
31+
})?;
32+
Ok(ParsedRequest::new_sync(VmmAction::UpdateMemoryHotplugSize(
33+
config,
34+
)))
35+
}
36+
2637
#[cfg(test)]
2738
mod tests {
2839
use vmm::devices::virtio::mem::{
2940
VIRTIO_MEM_DEFAULT_BLOCK_SIZE_MIB, VIRTIO_MEM_DEFAULT_SLOT_SIZE_MIB,
3041
};
42+
use vmm::vmm_config::memory_hotplug::MemoryHotplugSizeUpdate;
3143

3244
use super::*;
3345
use crate::api_server::parsed_request::tests::vmm_action_from_request;
@@ -80,4 +92,27 @@ mod tests {
8092
VmmAction::GetMemoryHotplugStatus
8193
);
8294
}
95+
96+
#[test]
97+
fn test_parse_patch_memory_hotplug_request() {
98+
parse_patch_memory_hotplug(&Body::new("invalid_payload")).unwrap_err();
99+
100+
// PATCH with invalid fields.
101+
let body = r#"{
102+
"requested_size_mib": "bar"
103+
}"#;
104+
parse_patch_memory_hotplug(&Body::new(body)).unwrap_err();
105+
106+
// PATCH with valid input fields.
107+
let body = r#"{
108+
"requested_size_mib": 2048
109+
}"#;
110+
let expected_config = MemoryHotplugSizeUpdate {
111+
requested_size_mib: 2048,
112+
};
113+
assert_eq!(
114+
vmm_action_from_request(parse_patch_memory_hotplug(&Body::new(body)).unwrap()),
115+
VmmAction::UpdateMemoryHotplugSize(expected_config)
116+
);
117+
}
83118
}

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

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ pub enum VirtioMemError {
4343
EventFd(#[from] io::Error),
4444
/// Received error while sending an interrupt: {0}
4545
InterruptError(#[from] InterruptError),
46+
/// Size {0} is invalid: it must be a multiple of block size and less than the total size
47+
InvalidSize(u64),
48+
/// Device is not active
49+
DeviceNotActive,
4650
}
4751

4852
#[derive(Debug)]
@@ -200,6 +204,45 @@ impl VirtioMem {
200204
pub(crate) fn activate_event(&self) -> &EventFd {
201205
&self.activate_event
202206
}
207+
208+
/// Updates the requested size of the virtio-mem device.
209+
pub fn update_requested_size(
210+
&mut self,
211+
requested_size_mib: usize,
212+
) -> Result<(), VirtioMemError> {
213+
let requested_size = usize_to_u64(mib_to_bytes(requested_size_mib));
214+
if !self.is_activated() {
215+
return Err(VirtioMemError::DeviceNotActive);
216+
}
217+
218+
if requested_size % self.config.block_size != 0 {
219+
return Err(VirtioMemError::InvalidSize(requested_size));
220+
}
221+
if requested_size > self.config.region_size {
222+
return Err(VirtioMemError::InvalidSize(requested_size));
223+
}
224+
225+
// usable_region_size can only be increased
226+
if self.config.usable_region_size < requested_size {
227+
self.config.usable_region_size =
228+
requested_size.next_multiple_of(usize_to_u64(self.slot_size));
229+
debug!(
230+
"virtio-mem: Updated usable size to {} bytes",
231+
self.config.usable_region_size
232+
);
233+
}
234+
235+
self.config.requested_size = requested_size;
236+
debug!(
237+
"virtio-mem: Updated requested size to {} bytes",
238+
requested_size
239+
);
240+
// TODO(virtio-mem): trigger interrupt once we add handling for the requests
241+
// self.interrupt_trigger()
242+
// .trigger(VirtioInterruptType::Config)
243+
// .map_err(VirtioMemError::InterruptError)
244+
Ok(())
245+
}
203246
}
204247

205248
impl VirtioDevice for VirtioMem {

src/vmm/src/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,15 @@ impl Vmm {
609609
.map_err(VmmError::FindDeviceError)
610610
}
611611

612+
/// Returns the current state of the memory hotplug device.
613+
pub fn update_memory_hotplug_size(&self, requested_size_mib: usize) -> Result<(), VmmError> {
614+
self.device_manager
615+
.try_with_virtio_device_with_id(VIRTIO_MEM_DEV_ID, |dev: &mut VirtioMem| {
616+
dev.update_requested_size(requested_size_mib)
617+
})
618+
.map_err(VmmError::FindDeviceError)
619+
}
620+
612621
/// Signals Vmm to stop and exit.
613622
pub fn stop(&mut self, exit_code: FcExitCode) {
614623
// To avoid cycles, all teardown paths take the following route:

src/vmm/src/logger/metrics.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,10 @@ pub struct PatchRequestsMetrics {
479479
pub mmds_count: SharedIncMetric,
480480
/// Number of failures in PATCHing an mmds.
481481
pub mmds_fails: SharedIncMetric,
482+
/// Number of PATCHes to /hotplug/memory
483+
pub hotplug_memory_count: SharedIncMetric,
484+
/// Number of failed PATCHes to /hotplug/memory
485+
pub hotplug_memory_fails: SharedIncMetric,
482486
}
483487
impl PatchRequestsMetrics {
484488
/// Const default construction.
@@ -492,6 +496,8 @@ impl PatchRequestsMetrics {
492496
machine_cfg_fails: SharedIncMetric::new(),
493497
mmds_count: SharedIncMetric::new(),
494498
mmds_fails: SharedIncMetric::new(),
499+
hotplug_memory_count: SharedIncMetric::new(),
500+
hotplug_memory_fails: SharedIncMetric::new(),
495501
}
496502
}
497503
}

src/vmm/src/rpc_interface.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ use crate::vmm_config::drive::{BlockDeviceConfig, BlockDeviceUpdateConfig, Drive
2929
use crate::vmm_config::entropy::{EntropyDeviceConfig, EntropyDeviceError};
3030
use crate::vmm_config::instance_info::InstanceInfo;
3131
use crate::vmm_config::machine_config::{MachineConfig, MachineConfigError, MachineConfigUpdate};
32-
use crate::vmm_config::memory_hotplug::{MemoryHotplugConfig, MemoryHotplugConfigError};
32+
use crate::vmm_config::memory_hotplug::{
33+
MemoryHotplugConfig, MemoryHotplugConfigError, MemoryHotplugSizeUpdate,
34+
};
3335
use crate::vmm_config::metrics::{MetricsConfig, MetricsConfigError};
3436
use crate::vmm_config::mmds::{MmdsConfig, MmdsConfigError};
3537
use crate::vmm_config::net::{
@@ -113,6 +115,9 @@ pub enum VmmAction {
113115
/// Set the memory hotplug device using `MemoryHotplugConfig` as input. This action can only be
114116
/// called before the microVM has booted.
115117
SetMemoryHotplugDevice(MemoryHotplugConfig),
118+
/// Updates the memory hotplug device using `MemoryHotplugConfigUpdate` as input. This action
119+
/// can only be called after the microVM has booted.
120+
UpdateMemoryHotplugSize(MemoryHotplugSizeUpdate),
116121
/// Launch the microVM. This action can only be called before the microVM has booted.
117122
StartMicroVm,
118123
/// Send CTRL+ALT+DEL to the microVM, using the i8042 keyboard function. If an AT-keyboard
@@ -152,6 +157,8 @@ pub enum VmmActionError {
152157
EntropyDevice(#[from] EntropyDeviceError),
153158
/// Memory hotplug config error: {0}
154159
MemoryHotplugConfig(#[from] MemoryHotplugConfigError),
160+
/// Memory hotplug update error: {0}
161+
MemoryHotplugUpdate(VmmError),
155162
/// Internal VMM error: {0}
156163
InternalVmm(#[from] VmmError),
157164
/// Load snapshot error: {0}
@@ -469,6 +476,7 @@ impl<'a> PrebootApiController<'a> {
469476
| UpdateBalloon(_)
470477
| UpdateBalloonStatistics(_)
471478
| UpdateBlockDevice(_)
479+
| UpdateMemoryHotplugSize(_)
472480
| UpdateNetworkInterface(_) => Err(VmmActionError::OperationNotSupportedPreBoot),
473481
#[cfg(target_arch = "x86_64")]
474482
SendCtrlAltDel => Err(VmmActionError::OperationNotSupportedPreBoot),
@@ -709,7 +717,13 @@ impl RuntimeApiController {
709717
.map_err(VmmActionError::BalloonUpdate),
710718
UpdateBlockDevice(new_cfg) => self.update_block_device(new_cfg),
711719
UpdateNetworkInterface(netif_update) => self.update_net_rate_limiters(netif_update),
712-
720+
UpdateMemoryHotplugSize(cfg) => self
721+
.vmm
722+
.lock()
723+
.expect("Poisoned lock")
724+
.update_memory_hotplug_size(cfg.requested_size_mib)
725+
.map(|_| VmmData::Empty)
726+
.map_err(VmmActionError::MemoryHotplugUpdate),
713727
// Operations not allowed post-boot.
714728
ConfigureBootSource(_)
715729
| ConfigureLogger(_)
@@ -1181,6 +1195,11 @@ mod tests {
11811195
)));
11821196
#[cfg(target_arch = "x86_64")]
11831197
check_unsupported(preboot_request(VmmAction::SendCtrlAltDel));
1198+
check_unsupported(preboot_request(VmmAction::UpdateMemoryHotplugSize(
1199+
MemoryHotplugSizeUpdate {
1200+
requested_size_mib: 0,
1201+
},
1202+
)));
11841203
}
11851204

11861205
fn runtime_request(request: VmmAction) -> Result<VmmData, VmmActionError> {

src/vmm/src/vmm_config/memory_hotplug.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,14 @@ impl MemoryHotplugConfig {
8686
}
8787
}
8888

89+
/// Configuration for memory hotplug device.
90+
#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
91+
#[serde(deny_unknown_fields)]
92+
pub struct MemoryHotplugSizeUpdate {
93+
/// Requested size in MiB to resize the hotpluggable memory to.
94+
pub requested_size_mib: usize,
95+
}
96+
8997
#[cfg(test)]
9098
mod tests {
9199
use serde_json;

0 commit comments

Comments
 (0)