#[derive(Default)]
pub struct PreflightReport {
pub warnings: Vec<String>,
pub failures: Vec<String>,
}
impl PreflightReport {
pub fn warn(&mut self, msg: impl Into<String>) {
self.warnings.push(msg.into());
}
pub fn fail(&mut self, msg: impl Into<String>) {
self.failures.push(msg.into());
}
pub fn finish(self) {
for w in &self.warnings {
eprintln!("WARNING: {w}");
}
if !self.failures.is_empty() {
for f in &self.failures {
eprintln!("FAILURE: {f}");
}
panic!("preflight failed: results would be meaningless");
}
}
}
fn read_trim(path: &str) -> Option<String> {
std::fs::read_to_string(path)
.ok()
.map(|s| s.trim().to_string())
}
fn parse_cpu_list(s: &str) -> Vec<usize> {
let mut out = vec![];
for part in s.split(',').filter(|p| !p.is_empty()) {
if let Some((a, b)) = part.split_once('-') {
if let (Ok(a), Ok(b)) = (a.parse::<usize>(), b.parse::<usize>()) {
out.extend(a..=b);
}
} else if let Ok(n) = part.parse::<usize>() {
out.push(n);
}
}
out
}
fn read_cpu_list(path: &str) -> Vec<usize> {
read_trim(path)
.map(|s| parse_cpu_list(&s))
.unwrap_or_default()
}
fn siblings_of(c: usize) -> Vec<usize> {
read_cpu_list(&format!(
"/sys/devices/system/cpu/cpu{c}/topology/thread_siblings_list"
))
.into_iter()
.filter(|&s| s != c)
.collect()
}
pub fn release_build(r: &mut PreflightReport) {
if cfg!(debug_assertions) {
r.fail("debug build, build with `--release` instead");
}
}
pub fn cores_online(r: &mut PreflightReport, cores: &[usize]) {
let online = read_cpu_list("/sys/devices/system/cpu/online");
for &c in cores {
if !online.contains(&c) {
r.fail(format!("core {c} is offline"));
}
}
}
pub fn cores_isolated(r: &mut PreflightReport, cores: &[usize]) {
let isolated = read_cpu_list("/sys/devices/system/cpu/isolated");
for &c in cores {
if !isolated.contains(&c) {
r.warn(format!("core {c} not in isolcpus (got {isolated:?})"));
}
}
}
pub fn cores_nohz_full(r: &mut PreflightReport, cores: &[usize]) {
let nohz = read_cpu_list("/sys/devices/system/cpu/nohz_full");
for &c in cores {
if !nohz.contains(&c) {
r.warn(format!("core {c} not in nohz_full"));
}
}
}
pub fn cores_performance_governor(r: &mut PreflightReport, cores: &[usize]) {
for &c in cores {
let g = read_trim(&format!(
"/sys/devices/system/cpu/cpu{c}/cpufreq/scaling_governor"
));
match g.as_deref() {
Some("performance") => {}
Some(other) => r.warn(format!("core {c} governor = {other}, want performance")),
None => r.warn(format!("core {c} governor unreadable")),
}
}
}
pub fn turbo_disabled(r: &mut PreflightReport) {
match read_trim("/sys/devices/system/cpu/intel_pstate/no_turbo") {
Some(s) if s == "1" => return,
Some(other) => {
r.warn(format!("intel_pstate/no_turbo = {other}, want 1"));
return;
}
None => {}
}
match read_trim("/sys/devices/system/cpu/cpufreq/boost") {
Some(s) if s == "0" => {}
Some(other) => r.warn(format!("cpufreq/boost = {other}, want 0")),
None => r.warn("turbo state unreadable (neither Intel pstate nor AMD boost)"),
}
}
pub fn cores_distinct_physical(r: &mut PreflightReport, cores: &[usize]) {
for (i, &a) in cores.iter().enumerate() {
for &b in &cores[i + 1..] {
let id_a = read_trim(&format!("/sys/devices/system/cpu/cpu{a}/topology/core_id"));
let id_b = read_trim(&format!("/sys/devices/system/cpu/cpu{b}/topology/core_id"));
if id_a.is_some() && id_a == id_b {
r.fail(format!(
"cores {a} and {b} are SMT siblings of physical core {}",
id_a.unwrap_or_default()
));
}
}
}
}
pub fn cores_smt_siblings_quiet(r: &mut PreflightReport, cores: &[usize]) {
let isolated = read_cpu_list("/sys/devices/system/cpu/isolated");
for &c in cores {
for sib in siblings_of(c) {
let sib_online = read_trim(&format!("/sys/devices/system/cpu/cpu{sib}/online"))
.as_deref()
!= Some("0");
let sib_isolated = isolated.contains(&sib);
if sib_online && !sib_isolated {
r.warn(format!(
"SMT sibling of {c} is cpu {sib}, online and not isolated will steal execution units"
));
}
}
}
}
pub fn cores_share_l3(r: &mut PreflightReport, cores: &[usize]) {
let l3 = |c: usize| {
read_trim(&format!(
"/sys/devices/system/cpu/cpu{c}/cache/index3/shared_cpu_list"
))
};
for (i, &a) in cores.iter().enumerate() {
for &b in &cores[i + 1..] {
if let (Some(la), Some(lb)) = (l3(a), l3(b))
&& la != lb
{
r.warn(format!("cores {a} and {b} don't share L3 ({la} vs {lb})"));
}
}
}
}
pub fn hugepages_at_least(r: &mut PreflightReport, min: u64) {
let Some(meminfo) = std::fs::read_to_string("/proc/meminfo").ok() else {
r.warn("/proc/meminfo unreadable, cannot verify hugepage availability");
return;
};
let free = meminfo
.lines()
.find_map(|l| l.strip_prefix("HugePages_Free:"))
.and_then(|s| s.split_whitespace().next())
.and_then(|s| s.parse::<u64>().ok());
match free {
Some(n) if n >= min => {}
Some(n) => r.fail(format!(
"need >= {min} free 2MiB hugepages, have {n} (try `just enable-hugepages`)"
)),
None => r.warn("could not parse HugePages_Free from /proc/meminfo"),
}
}
pub fn tsc_invariant_and_nonstop(r: &mut PreflightReport) {
let Some(cpuinfo) = std::fs::read_to_string("/proc/cpuinfo").ok() else {
r.warn("/proc/cpuinfo unreadable, cannot verify TSC quality");
return;
};
for flag in ["constant_tsc", "nonstop_tsc"] {
if !cpuinfo.contains(flag) {
r.fail(format!("CPU missing `{flag}`; TSC unsuitable"));
}
}
}