#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn mem_avail_mib() -> i64 {
let s = std::fs::read_to_string("/proc/meminfo").unwrap_or_default();
for line in s.lines() {
if let Some(r) = line.strip_prefix("MemAvailable:") {
return r
.split_whitespace()
.next()
.and_then(|v| v.parse::<i64>().ok())
.unwrap_or(0)
/ 1024;
}
}
0
}
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn main() {
use std::time::Duration;
use supermachine::vmm::resources::MountSpec;
use supermachine::{Image, VmConfig};
let n: usize = std::env::args()
.nth(1)
.and_then(|s| s.parse().ok())
.unwrap_or(4);
let mib = 128u64;
let host_dir = std::env::temp_dir().join("sm-dax-dens-host");
let _ = std::fs::remove_dir_all(&host_dir);
std::fs::create_dir_all(&host_dir).expect("mkdir");
{
use std::io::Write;
let mut f = std::fs::File::create(host_dir.join("blob")).expect("create blob");
let chunk = {
let mut b = vec![0u8; 1 << 20];
for (i, x) in b.iter_mut().enumerate() {
*x = (i as u8).wrapping_mul(31).wrapping_add(7);
}
b
};
for _ in 0..mib {
f.write_all(&chunk).expect("write blob");
}
}
let dest = std::env::temp_dir().join("sm-dax-dens-img");
let _ = std::fs::remove_dir_all(&dest);
let image = Image::bake_kvm_auto("alpine", &dest).expect("bake");
let cfg = VmConfig::new().with_virtiofs(MountSpec::new(
host_dir.to_string_lossy().into_owned(),
"smfs0",
"/mnt/host",
));
let base = mem_avail_mib();
eprintln!("baseline MemAvailable = {base} MiB; file = {mib} MiB (shared via DAX)");
let mut held = Vec::new();
for i in 0..n {
let vm = image.start(&cfg).expect("start");
std::thread::sleep(Duration::from_millis(4000));
let o = vm
.exec_builder()
.argv([
"/bin/sh",
"-c",
"mount | grep -q 'dax=always' && dd if=/mnt/host/blob of=/dev/null bs=1M 2>/dev/null && echo dax-read-ok",
])
.output()
.expect("read");
let ok = String::from_utf8_lossy(&o.stdout).contains("dax-read-ok");
held.push(vm);
let used = base - mem_avail_mib();
eprintln!(
"VM #{i} (dax-read-ok={ok}): host used +{used} MiB total, {} MiB/VM avg",
used / (i as i64 + 1)
);
}
let used = base - mem_avail_mib();
let per_vm = used / n as i64;
eprintln!(
"\n=== DAX FLEET DENSITY: {n} VMs each DAX-read a {mib} MiB file ===\n\
host used +{used} MiB total, ~{per_vm} MiB/VM\n\
the {mib} MiB file is NOT in the per-VM cost (shared host page cache via\n\
DAX), vs ~148-252 MiB/VM for the non-DAX committed-squashfs path."
);
eprintln!(
"=== {} ===",
if per_vm < mib as i64 {
"PASS (file shared)"
} else {
"FAIL (file paid per-VM)"
}
);
drop(held);
}
#[cfg(not(all(target_os = "linux", target_arch = "x86_64")))]
fn main() {
eprintln!("kvm_dax_density is Linux/x86_64 only");
}