Skip to content

Commit aa29bff

Browse files
committed
refactor: Consolidate machine-config validation to fn update
Do all validation of the configuration provided to the `machine-config` endpoint inside of the `update` method (e.g. right before it gets applied), instead of splitting some of it into the Deserialization routine. Signed-off-by: Patrick Roy <[email protected]>
1 parent 027f0df commit aa29bff

File tree

3 files changed

+45
-161
lines changed

3 files changed

+45
-161
lines changed

src/firecracker/src/api_server/request/machine_configuration.rs

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -180,31 +180,24 @@ mod tests {
180180
parse_put_machine_config(&Body::new(body)).unwrap_err();
181181
}
182182

183-
// 5. Test that setting `smt: true` is successful on x86_64 while on aarch64, it is not.
183+
// 5. Test that setting `smt: true` is successful
184184
let body = r#"{
185185
"vcpu_count": 8,
186186
"mem_size_mib": 1024,
187187
"smt": true,
188188
"track_dirty_pages": true
189189
}"#;
190-
#[cfg(target_arch = "x86_64")]
191-
{
192-
let expected_config = MachineConfigUpdate {
193-
vcpu_count: Some(8),
194-
mem_size_mib: Some(1024),
195-
smt: Some(true),
196-
cpu_template: None,
197-
track_dirty_pages: Some(true),
198-
};
199-
assert_eq!(
200-
vmm_action_from_request(parse_put_machine_config(&Body::new(body)).unwrap()),
201-
VmmAction::UpdateVmConfiguration(expected_config)
202-
);
203-
}
204-
#[cfg(target_arch = "aarch64")]
205-
{
206-
parse_put_machine_config(&Body::new(body)).unwrap_err();
207-
}
190+
let expected_config = MachineConfigUpdate {
191+
vcpu_count: Some(8),
192+
mem_size_mib: Some(1024),
193+
smt: Some(true),
194+
cpu_template: None,
195+
track_dirty_pages: Some(true),
196+
};
197+
assert_eq!(
198+
vmm_action_from_request(parse_put_machine_config(&Body::new(body)).unwrap()),
199+
VmmAction::UpdateVmConfiguration(expected_config)
200+
);
208201
}
209202

210203
#[test]

src/vmm/src/resources.rs

Lines changed: 19 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -680,82 +680,6 @@ mod tests {
680680
_ => unreachable!(),
681681
}
682682

683-
// Invalid vCPU number.
684-
json = format!(
685-
r#"{{
686-
"boot-source": {{
687-
"kernel_image_path": "{}",
688-
"boot_args": "console=ttyS0 reboot=k panic=1 pci=off"
689-
}},
690-
"drives": [
691-
{{
692-
"drive_id": "rootfs",
693-
"path_on_host": "{}",
694-
"is_root_device": true,
695-
"is_read_only": false
696-
}}
697-
],
698-
"machine-config": {{
699-
"vcpu_count": 0,
700-
"mem_size_mib": 1024
701-
}}
702-
}}"#,
703-
kernel_file.as_path().to_str().unwrap(),
704-
rootfs_file.as_path().to_str().unwrap()
705-
);
706-
707-
match VmResources::from_json(
708-
json.as_str(),
709-
&default_instance_info,
710-
HTTP_MAX_PAYLOAD_SIZE,
711-
None,
712-
) {
713-
Err(ResourcesError::InvalidJson(_)) => (),
714-
_ => unreachable!(),
715-
}
716-
717-
// Valid config for x86 but invalid on aarch64 because smt is not available.
718-
json = format!(
719-
r#"{{
720-
"boot-source": {{
721-
"kernel_image_path": "{}",
722-
"boot_args": "console=ttyS0 reboot=k panic=1 pci=off"
723-
}},
724-
"drives": [
725-
{{
726-
"drive_id": "rootfs",
727-
"path_on_host": "{}",
728-
"is_root_device": true,
729-
"is_read_only": false
730-
}}
731-
],
732-
"machine-config": {{
733-
"vcpu_count": 2,
734-
"mem_size_mib": 1024,
735-
"smt": true
736-
}}
737-
}}"#,
738-
kernel_file.as_path().to_str().unwrap(),
739-
rootfs_file.as_path().to_str().unwrap()
740-
);
741-
742-
#[cfg(target_arch = "x86_64")]
743-
VmResources::from_json(
744-
json.as_str(),
745-
&default_instance_info,
746-
HTTP_MAX_PAYLOAD_SIZE,
747-
None,
748-
)
749-
.unwrap();
750-
#[cfg(target_arch = "aarch64")]
751-
VmResources::from_json(
752-
json.as_str(),
753-
&default_instance_info,
754-
HTTP_MAX_PAYLOAD_SIZE,
755-
None,
756-
)
757-
.unwrap_err();
758-
759683
// Valid config for x86 but invalid on aarch64 since it uses cpu_template.
760684
json = format!(
761685
r#"{{
@@ -1330,7 +1254,7 @@ mod tests {
13301254
let mut aux_vm_config = MachineConfigUpdate {
13311255
vcpu_count: Some(32),
13321256
mem_size_mib: Some(512),
1333-
smt: Some(true),
1257+
smt: Some(false),
13341258
#[cfg(target_arch = "x86_64")]
13351259
cpu_template: Some(StaticCpuTemplate::T2),
13361260
#[cfg(target_arch = "aarch64")]
@@ -1359,7 +1283,25 @@ mod tests {
13591283
vm_resources.update_vm_config(&aux_vm_config),
13601284
Err(VmConfigError::InvalidVcpuCount)
13611285
);
1286+
1287+
// Check that SMT is not supported on aarch64, and that on x86_64 enabling it requires vcpu
1288+
// count to be even.
1289+
aux_vm_config.smt = Some(true);
1290+
#[cfg(target_arch = "aarch64")]
1291+
assert_eq!(
1292+
vm_resources.update_vm_config(&aux_vm_config),
1293+
Err(VmConfigError::SmtNotSupported)
1294+
);
1295+
aux_vm_config.vcpu_count = Some(3);
1296+
#[cfg(target_arch = "x86_64")]
1297+
assert_eq!(
1298+
vm_resources.update_vm_config(&aux_vm_config),
1299+
Err(VmConfigError::InvalidVcpuCount)
1300+
);
13621301
aux_vm_config.vcpu_count = Some(32);
1302+
#[cfg(target_arch = "x86_64")]
1303+
vm_resources.update_vm_config(&aux_vm_config).unwrap();
1304+
aux_vm_config.smt = Some(false);
13631305

13641306
// Invalid mem_size_mib.
13651307
aux_vm_config.mem_size_mib = Some(0);

src/vmm/src/vmm_config/machine_config.rs

Lines changed: 14 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0
33
use std::fmt::Debug;
44

5-
use serde::{de, Deserialize, Serialize};
5+
use serde::{Deserialize, Serialize};
66

77
use crate::cpu_config::templates::{CpuTemplateType, CustomCpuTemplate, StaticCpuTemplate};
88

@@ -20,23 +20,25 @@ pub enum VmConfigError {
2020
IncompatibleBalloonSize,
2121
/// The memory size (MiB) is invalid.
2222
InvalidMemorySize,
23-
/// The vCPU number is invalid! The vCPU number can only be 1 or an even number when SMT is enabled.
23+
/// The number of vCPUs must be greater than 0, less than {MAX_SUPPORTED_VCPUS:} and must be 1 or an even number if SMT is enabled.
2424
InvalidVcpuCount,
2525
/// Could not get the configuration of the previously installed balloon device to validate the memory size.
2626
InvalidVmState,
27+
/// Enabling simultaneous multithreading is not supported on aarch64.
28+
#[cfg(target_arch = "aarch64")]
29+
SmtNotSupported,
2730
}
2831

2932
/// Struct used in PUT `/machine-config` API call.
3033
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
3134
#[serde(deny_unknown_fields)]
3235
pub struct MachineConfig {
3336
/// Number of vcpu to start.
34-
#[serde(deserialize_with = "deserialize_vcpu_num")]
3537
pub vcpu_count: u8,
3638
/// The memory size in MiB.
3739
pub mem_size_mib: usize,
3840
/// Enables or disabled SMT.
39-
#[serde(default, deserialize_with = "deserialize_smt")]
41+
#[serde(default)]
4042
pub smt: bool,
4143
/// A CPU template that it is used to filter the CPU features exposed to the guest.
4244
#[serde(default, skip_serializing_if = "Option::is_none")]
@@ -62,21 +64,13 @@ impl Default for MachineConfig {
6264
#[serde(deny_unknown_fields)]
6365
pub struct MachineConfigUpdate {
6466
/// Number of vcpu to start.
65-
#[serde(
66-
default,
67-
skip_serializing_if = "Option::is_none",
68-
deserialize_with = "deserialize_vcpu_num"
69-
)]
67+
#[serde(default, skip_serializing_if = "Option::is_none")]
7068
pub vcpu_count: Option<u8>,
7169
/// The memory size in MiB.
7270
#[serde(skip_serializing_if = "Option::is_none")]
7371
pub mem_size_mib: Option<usize>,
7472
/// Enables or disabled SMT.
75-
#[serde(
76-
default,
77-
skip_serializing_if = "Option::is_none",
78-
deserialize_with = "deserialize_smt"
79-
)]
73+
#[serde(default, skip_serializing_if = "Option::is_none")]
8074
pub smt: Option<bool>,
8175
/// A CPU template that it is used to filter the CPU features exposed to the guest.
8276
#[serde(default, skip_serializing_if = "Option::is_none")]
@@ -146,7 +140,12 @@ impl VmConfig {
146140

147141
let smt = update.smt.unwrap_or(self.smt);
148142

149-
if vcpu_count == 0 {
143+
#[cfg(target_arch = "aarch64")]
144+
if smt {
145+
return Err(VmConfigError::SmtNotSupported);
146+
}
147+
148+
if vcpu_count == 0 || vcpu_count > MAX_SUPPORTED_VCPUS {
150149
return Err(VmConfigError::InvalidVcpuCount);
151150
}
152151

@@ -205,53 +204,3 @@ impl From<&VmConfig> for MachineConfig {
205204
}
206205
}
207206
}
208-
209-
/// Deserialization function for the `vcpu_num` field in `MachineConfig` and `MachineConfigUpdate`.
210-
/// This is called only when `vcpu_num` is present in the JSON configuration.
211-
/// `T` can be either `u8` or `Option<u8>` which both support ordering if `vcpu_num` is
212-
/// present in the JSON.
213-
fn deserialize_vcpu_num<'de, D, T>(d: D) -> Result<T, D::Error>
214-
where
215-
D: de::Deserializer<'de>,
216-
T: Deserialize<'de> + PartialOrd + From<u8> + Debug,
217-
{
218-
let val = T::deserialize(d)?;
219-
220-
if val > T::from(MAX_SUPPORTED_VCPUS) {
221-
return Err(de::Error::invalid_value(
222-
de::Unexpected::Other("vcpu_num"),
223-
&"number of vCPUs exceeds the maximum limitation",
224-
));
225-
}
226-
if val < T::from(1) {
227-
return Err(de::Error::invalid_value(
228-
de::Unexpected::Other("vcpu_num"),
229-
&"number of vCPUs should be larger than 0",
230-
));
231-
}
232-
233-
Ok(val)
234-
}
235-
236-
/// Deserialization function for the `smt` field in `MachineConfig` and `MachineConfigUpdate`.
237-
/// This is called only when `smt` is present in the JSON configuration.
238-
fn deserialize_smt<'de, D, T>(d: D) -> Result<T, D::Error>
239-
where
240-
D: de::Deserializer<'de>,
241-
T: Deserialize<'de> + PartialEq + From<bool> + Debug,
242-
{
243-
let val = T::deserialize(d)?;
244-
245-
// If this function was called it means that `smt` was specified in
246-
// the JSON. On aarch64 the only accepted value is `false` so throw an
247-
// error if `true` was specified.
248-
#[cfg(target_arch = "aarch64")]
249-
if val == T::from(true) {
250-
return Err(de::Error::invalid_value(
251-
de::Unexpected::Other("smt"),
252-
&"Enabling simultaneous multithreading is not supported on aarch64",
253-
));
254-
}
255-
256-
Ok(val)
257-
}

0 commit comments

Comments
 (0)