Skip to content

Clarify supported CPU models for CPU templates #5142

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .buildkite/pipeline_cpu_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ class BkStep(str, Enum):
"m5n.metal",
"m6i.metal",
"m6a.metal",
"m7a.metal-48xl",
],
},
}
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ and this project adheres to
WAITPKG CPUID bit in CPUID normalization. The feature enables a guest to put a
physical processor into an idle state, which is undesirable in a FaaS
environment since that is what the host wants to decide.
- [#5142](https://github.com/firecracker-microvm/firecracker/pull/5142):
Clarified what CPU models are supported by each existing CPU template.
Firecracker exits with an error if a CPU template is used on an unsupported
CPU model.

### Deprecated

Expand Down
20 changes: 10 additions & 10 deletions docs/cpu_templates/cpu-templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,14 @@ Firecracker supports two types of CPU templates:

At the moment the following set of static CPU templates are supported:

| CPU template | CPU vendor | CPU model |
| ------------ | ---------- | --------------------- |
| C3 | Intel | any |
| T2 | Intel | any |
| T2A | AMD | Milan |
| T2CL | Intel | Cascade Lake or newer |
| T2S | Intel | any |
| V1N1 | ARM | Neoverse V1 |
| CPU template | CPU vendor | CPU model |
| ------------ | ---------- | ------------------------------- |
| C3 | Intel | Skylake, Cascade Lake, Ice Lake |
| T2 | Intel | Skylake, Cascade Lake, Ice Lake |
| T2A | AMD | Milan |
| T2CL | Intel | Cascade Lake, Ice Lake |
| T2S | Intel | Skylake, Cascade Lake |
| V1N1 | ARM | Neoverse V1 |

T2 and C3 templates are mapped as close as possible to AWS T2 and C3 instances
in terms of CPU features. Note that on a microVM that is lauched with the C3
Expand All @@ -71,8 +71,8 @@ a performance assessment if they wish to use the T2S template. Note that
Firecracker expects the host to always be running the latest version of the
microcode.

The T2CL template is mapped to be close to Intel Cascade Lake. It is not safe to
use it on Intel CPUs older than Cascade Lake (such as Skylake).
The T2CL template is mapped to be close to Intel Cascade Lake. It is only safe
to use it on Intel Cascade Lake and Ice Lake.

The only AMD template is T2A. It is considered safe to be used with AMD Milan.

Expand Down
106 changes: 46 additions & 60 deletions src/vmm/src/arch/x86_64/cpu_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

use std::arch::x86_64::__cpuid as host_cpuid;
use std::cmp::{Eq, Ordering, PartialEq, PartialOrd};
use std::cmp::{Eq, PartialEq};

/// Structure representing x86_64 CPU model.
#[derive(Debug, Eq, PartialEq)]
Expand All @@ -19,6 +19,42 @@ pub struct CpuModel {
pub stepping: u8,
}

/// Family / Model / Stepping for Intel Skylake
pub const SKYLAKE_FMS: CpuModel = CpuModel {
extended_family: 0x0,
extended_model: 0x5,
family: 0x6,
model: 0x5,
stepping: 0x4,
};

/// Family / Model / Stepping for Intel Cascade Lake
pub const CASCADE_LAKE_FMS: CpuModel = CpuModel {
extended_family: 0x0,
extended_model: 0x5,
family: 0x6,
model: 0x5,
stepping: 0x7,
};

/// Family / Model / Stepping for Intel Ice Lake
pub const ICE_LAKE_FMS: CpuModel = CpuModel {
extended_family: 0x0,
extended_model: 0x6,
family: 0x6,
model: 0xa,
stepping: 0x6,
};

/// Family / Model / Stepping for AMD Milan
pub const MILAN_FMS: CpuModel = CpuModel {
extended_family: 0xa,
extended_model: 0x0,
family: 0xf,
model: 0x1,
stepping: 0x1,
};

impl CpuModel {
/// Get CPU model from current machine.
pub fn get_cpu_model() -> Self {
Expand All @@ -27,19 +63,6 @@ impl CpuModel {
let eax = unsafe { host_cpuid(0x1) }.eax;
CpuModel::from(&eax)
}

/// Check if the current CPU model is Intel Cascade Lake or later.
pub fn is_at_least_cascade_lake(&self) -> bool {
let cascade_lake = CpuModel {
extended_family: 0,
extended_model: 5,
family: 6,
model: 5,
stepping: 7,
};

self >= &cascade_lake
}
}

impl From<&u32> for CpuModel {
Expand All @@ -54,59 +77,22 @@ impl From<&u32> for CpuModel {
}
}

impl From<&CpuModel> for u32 {
fn from(cpu_model: &CpuModel) -> Self {
(u32::from(cpu_model.extended_family) << 20)
| (u32::from(cpu_model.extended_model) << 16)
| (u32::from(cpu_model.family) << 8)
| (u32::from(cpu_model.model) << 4)
| u32::from(cpu_model.stepping)
}
}

impl PartialOrd for CpuModel {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(u32::from(self).cmp(&u32::from(other)))
}
}

impl Ord for CpuModel {
fn cmp(&self, other: &Self) -> Ordering {
u32::from(self).cmp(&u32::from(other))
}
}

#[cfg(test)]
mod tests {
use super::*;

const SKYLAKE: CpuModel = CpuModel {
extended_family: 0,
extended_model: 5,
family: 6,
model: 5,
stepping: 4,
};

const CASCADE_LAKE: CpuModel = CpuModel {
extended_family: 0,
extended_model: 5,
family: 6,
model: 5,
stepping: 7,
};

#[test]
fn cpu_model_from() {
let skylake_eax = 0x00050654;
assert_eq!(u32::from(&SKYLAKE), skylake_eax);
assert_eq!(CpuModel::from(&skylake_eax), SKYLAKE);
}
assert_eq!(CpuModel::from(&skylake_eax), SKYLAKE_FMS);

#[test]
fn cpu_model_ord() {
assert_eq!(SKYLAKE, SKYLAKE);
assert!(SKYLAKE < CASCADE_LAKE);
assert!(CASCADE_LAKE > SKYLAKE);
let cascade_lake_eax = 0x00050657;
assert_eq!(CpuModel::from(&cascade_lake_eax), CASCADE_LAKE_FMS);

let ice_lake_eax = 0x000606a6;
assert_eq!(CpuModel::from(&ice_lake_eax), ICE_LAKE_FMS);

let milan_eax = 0x00a00f11;
assert_eq!(CpuModel::from(&milan_eax), MILAN_FMS);
}
}
51 changes: 31 additions & 20 deletions src/vmm/src/arch/x86_64/vcpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -832,17 +832,6 @@ mod tests {
(kvm, vm, vcpu)
}

fn is_at_least_cascade_lake() -> bool {
CpuModel::get_cpu_model()
>= (CpuModel {
extended_family: 0,
extended_model: 5,
family: 6,
model: 5,
stepping: 7,
})
}

fn create_vcpu_config(
kvm: &Kvm,
vcpu: &KvmVcpu,
Expand Down Expand Up @@ -915,24 +904,46 @@ mod tests {
// Test configure while using the T2S template.
let t2a_res = try_configure(&kvm, &mut vcpu, StaticCpuTemplate::T2A);

let cpu_model = CpuModel::get_cpu_model();
match &cpuid::common::get_vendor_id_from_host().unwrap() {
cpuid::VENDOR_ID_INTEL => {
assert!(t2_res);
assert!(c3_res);
assert!(t2s_res);
if is_at_least_cascade_lake() {
assert!(t2cl_res);
} else {
assert!(!t2cl_res);
}
assert_eq!(
t2_res,
StaticCpuTemplate::T2
.get_supported_cpu_models()
.contains(&cpu_model)
);
assert_eq!(
c3_res,
StaticCpuTemplate::C3
.get_supported_cpu_models()
.contains(&cpu_model)
);
assert_eq!(
t2s_res,
StaticCpuTemplate::T2S
.get_supported_cpu_models()
.contains(&cpu_model)
);
assert_eq!(
t2cl_res,
StaticCpuTemplate::T2CL
.get_supported_cpu_models()
.contains(&cpu_model)
);
assert!(!t2a_res);
}
cpuid::VENDOR_ID_AMD => {
assert!(!t2_res);
assert!(!c3_res);
assert!(!t2s_res);
assert!(!t2cl_res);
assert!(t2a_res);
assert_eq!(
t2a_res,
StaticCpuTemplate::T2A
.get_supported_cpu_models()
.contains(&cpu_model)
);
}
_ => {
assert!(!t2_res);
Expand Down
2 changes: 1 addition & 1 deletion src/vmm/src/cpu_config/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::cpu_config::templates::CustomCpuTemplate;
pub fn get_json_template(filename: &str) -> CustomCpuTemplate {
let json_path = [
env!("CARGO_MANIFEST_DIR"),
"../../tests/data/static_cpu_templates",
"../../tests/data/custom_cpu_templates",
filename,
]
.iter()
Expand Down
Loading