use libnvme::{DsmAttr, DsmRange, Root};
const QEMU_MODEL: &str = "QEMU NVMe Ctrl";
fn main() -> Result<(), Box<dyn std::error::Error>> {
let root = Root::scan()?;
let mut exercised = 0;
for host in root.hosts() {
for subsys in host.subsystems() {
for ctrl in subsys.controllers() {
let model = ctrl.model().unwrap_or("?").trim();
if model != QEMU_MODEL {
println!(
"skipping {}: model {model:?} != {QEMU_MODEL:?} (refusing on real hardware)",
ctrl.name()?
);
continue;
}
for ns in ctrl.namespaces() {
let lba_size = ns.lba_size();
println!(
"exercising {} (NSID {}, {} B LBAs)",
ns.name()?,
ns.nsid(),
lba_size
);
let mut pattern = vec![0u8; lba_size as usize];
for (i, b) in pattern.iter_mut().enumerate() {
*b = (i & 0xFF) as u8;
}
ns.write(0, 1, &pattern).fua().execute()?;
println!(" write ok");
let got = ns.read_to_vec(0, 1)?;
assert_eq!(got, pattern, "read did not return the bytes we wrote");
println!(" read ok (round-trip matches)");
ns.compare(0, 1, &pattern).execute()?;
println!(" compare ok");
ns.verify(0, 1).execute()?;
println!(" verify ok");
ns.write_zeroes(0, 1).execute()?;
let zeroed = ns.read_to_vec(0, 1)?;
assert!(zeroed.iter().all(|&b| b == 0), "expected all-zero LBA");
println!(" write_zeroes ok");
ns.dsm(DsmAttr::DEALLOCATE)
.ranges(&[DsmRange::new(0, 1)])
.execute()?;
println!(" dsm(deallocate) ok");
ns.flush()?;
println!(" flush ok");
exercised += 1;
}
}
}
}
if exercised == 0 {
println!("(no QEMU virtual NVMe controllers found)");
} else {
println!("\nall I/O commands exercised on {exercised} namespace(s)");
}
Ok(())
}