btrfs_cli/device/
stats.rs1use crate::{Format, Runnable};
2use anyhow::{Context, Result};
3use btrfs_uapi::{
4 device::{DeviceStats, device_info_all, device_stats},
5 filesystem::filesystem_info,
6};
7use clap::Parser;
8use std::{fs::File, os::unix::io::AsFd, path::PathBuf};
9
10#[derive(Parser, Debug)]
18pub struct DeviceStatsCommand {
19 #[clap(long, short)]
21 pub check: bool,
22
23 #[clap(long, short = 'z')]
25 pub reset: bool,
26
27 pub path: PathBuf,
29}
30
31impl Runnable for DeviceStatsCommand {
32 fn run(&self, _format: Format, _dry_run: bool) -> Result<()> {
33 let file = File::open(&self.path).with_context(|| {
34 format!("failed to open '{}'", self.path.display())
35 })?;
36 let fd = file.as_fd();
37
38 let fs = filesystem_info(fd).with_context(|| {
39 format!(
40 "failed to get filesystem info for '{}'",
41 self.path.display()
42 )
43 })?;
44
45 let devices = device_info_all(fd, &fs).with_context(|| {
46 format!("failed to get device info for '{}'", self.path.display())
47 })?;
48
49 if devices.is_empty() {
50 anyhow::bail!("no devices found for '{}'", self.path.display());
51 }
52
53 let mut any_nonzero = false;
54
55 for dev in &devices {
56 let stats =
57 device_stats(fd, dev.devid, self.reset).with_context(|| {
58 format!(
59 "failed to get stats for device {} ({})",
60 dev.devid, dev.path
61 )
62 })?;
63
64 print_stats(&dev.path, &stats);
65
66 if !stats.is_clean() {
67 any_nonzero = true;
68 }
69 }
70
71 if self.check && any_nonzero {
72 anyhow::bail!("one or more devices have non-zero error counters");
73 }
74
75 Ok(())
76 }
77}
78
79fn print_stats(path: &str, stats: &DeviceStats) {
82 let p = path;
83 println!("[{p}].{:<24} {}", "write_io_errs", stats.write_errs);
84 println!("[{p}].{:<24} {}", "read_io_errs", stats.read_errs);
85 println!("[{p}].{:<24} {}", "flush_io_errs", stats.flush_errs);
86 println!("[{p}].{:<24} {}", "corruption_errs", stats.corruption_errs);
87 println!("[{p}].{:<24} {}", "generation_errs", stats.generation_errs);
88}