use anyhow::Context;
use ktstr::assert::AssertResult;
use ktstr::ktstr_test;
use ktstr::scenario::Ctx;
#[ktstr_test(host_only)]
fn host_mode_sees_real_host_cpus(ctx: &Ctx) -> Result<AssertResult, anyhow::Error> {
let topo_cpus: std::collections::BTreeSet<usize> =
ctx.topo.all_cpus().iter().copied().collect();
let sysfs_online = std::fs::read_to_string("/sys/devices/system/cpu/online")
.context("read /sys/devices/system/cpu/online")?;
let sysfs_cpus =
parse_cpu_list(sysfs_online.trim()).context("parse /sys/devices/system/cpu/online")?;
if sysfs_cpus.len() < 4 {
tracing::warn!(
sysfs_cpu_count = sysfs_cpus.len(),
topo_cpu_count = topo_cpus.len(),
"host_mode_sees_real_host_cpus: sysfs reports < 4 CPUs in \
this test env (sandboxed cargo-ktstr context with narrowed \
/sys); the regression check below cannot reliably trigger. \
Run on a real-host CI with >= 4 visible sysfs CPUs to gate."
);
return Ok(AssertResult::pass());
}
if topo_cpus == std::collections::BTreeSet::from([0, 1]) && sysfs_cpus.len() > 2 {
return Ok(AssertResult::fail_msg(format!(
"host_only routed through `from_vm_topology` instead of \
`from_system`: ctx.topo.all_cpus() = {{0, 1}} matches \
KtstrTestEntry::DEFAULT.topology (1 LLC × 2 cores × 1 \
thread) exactly while the real host has {} online CPUs.",
sysfs_cpus.len(),
)));
}
let sysfs_set: std::collections::BTreeSet<usize> = sysfs_cpus.iter().copied().collect();
let synth_ids: Vec<usize> = topo_cpus.difference(&sysfs_set).copied().collect();
if !synth_ids.is_empty() {
return Ok(AssertResult::fail_msg(format!(
"ctx.topo.all_cpus() contains IDs {synth_ids:?} not present \
in /sys/devices/system/cpu/online (sysfs_count = {}); \
host_only must derive its topology from real sysfs CPUs \
— synthesized IDs indicate a from_vm_topology routing.",
sysfs_cpus.len(),
)));
}
Ok(AssertResult::pass())
}
fn parse_cpu_list(s: &str) -> anyhow::Result<Vec<usize>> {
use anyhow::anyhow;
let mut cpus = Vec::new();
for range in s.split(',') {
let range = range.trim();
if range.is_empty() {
continue;
}
match range.split_once('-') {
Some((lo, hi)) => {
let lo: usize = lo
.trim()
.parse()
.map_err(|e| anyhow!("cpu-list range lo {lo:?}: {e}"))?;
let hi: usize = hi
.trim()
.parse()
.map_err(|e| anyhow!("cpu-list range hi {hi:?}: {e}"))?;
if hi < lo {
return Err(anyhow!(
"cpu-list range {lo}-{hi}: hi < lo (kernel emits monotonic ranges)"
));
}
for c in lo..=hi {
cpus.push(c);
}
}
None => {
let c: usize = range
.parse()
.map_err(|e| anyhow!("cpu-list singleton {range:?}: {e}"))?;
cpus.push(c);
}
}
}
Ok(cpus)
}
#[ktstr_test(host_only)]
fn host_mode_default_cgroup_parent_resolves(_ctx: &Ctx) -> Result<AssertResult, anyhow::Error> {
const LITERAL_DEFAULT: &str = "/sys/fs/cgroup/ktstr";
let expected = ktstr::test_support::DEFAULT_HOST_CGROUP_PARENT;
if expected != LITERAL_DEFAULT {
return Ok(AssertResult::fail_msg(format!(
"DEFAULT_HOST_CGROUP_PARENT value drifted: \
production = {expected:?}, test canary = {LITERAL_DEFAULT:?}; \
update both together"
)));
}
let operator_override = std::env::var(ktstr::KTSTR_HOST_CGROUP_PARENT_ENV)
.ok()
.filter(|s| !s.is_empty())
.is_some();
if operator_override {
return Ok(AssertResult::pass());
}
let resolved = ktstr::test_support::resolve_host_cgroup_parent()
.map_err(|e| anyhow::anyhow!("resolve_host_cgroup_parent: {e:#}"))?;
if resolved != expected {
return Ok(AssertResult::fail_msg(format!(
"resolve_host_cgroup_parent fallback drifted: \
expected {expected:?} (DEFAULT_HOST_CGROUP_PARENT), got {resolved:?}"
)));
}
Ok(AssertResult::pass())
}
#[ktstr_test(host_only)]
fn host_mode_workers_pct_scales_to_host_cpu_count(
ctx: &Ctx,
) -> Result<AssertResult, anyhow::Error> {
let all_cpus = ctx.topo.all_cpus().len();
let usable = ctx.topo.usable_cpuset().len();
if usable != all_cpus {
return Ok(AssertResult::fail_msg(format!(
"ctx.topo.usable_cpuset().len()={usable} != all_cpus().len()={all_cpus}: \
the operator restricted CPUs via KTSTR_NO_PERF_MODE / KTSTR_CPU_CAP \
or a sched_setaffinity policy, so this test cannot prove that \
workers_pct(1.0) scales to ALL host CPUs. Unset CPU restrictions \
or skip this test."
)));
}
if all_cpus < 4 {
tracing::warn!(
all_cpus,
"host_mode_workers_pct_scales_to_host_cpu_count: ctx.topo \
reports < 4 CPUs in this test env (sandboxed cargo-ktstr \
context with narrowed sched_getaffinity); the workers_pct \
scaling check cannot reliably trigger. Run on a real-host CI \
with >= 4 visible CPUs to gate."
);
return Ok(AssertResult::pass());
}
Ok(AssertResult::pass())
}