btrfs_cli/device/
stats.rs1use crate::{Format, Runnable, util::open_path};
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::{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 = open_path(&self.path)?;
34 let fd = file.as_fd();
35
36 let fs = filesystem_info(fd).with_context(|| {
37 format!(
38 "failed to get filesystem info for '{}'",
39 self.path.display()
40 )
41 })?;
42
43 let devices = device_info_all(fd, &fs).with_context(|| {
44 format!("failed to get device info for '{}'", self.path.display())
45 })?;
46
47 if devices.is_empty() {
48 anyhow::bail!("no devices found for '{}'", self.path.display());
49 }
50
51 let mut any_nonzero = false;
52
53 for dev in &devices {
54 let stats =
55 device_stats(fd, dev.devid, self.reset).with_context(|| {
56 format!(
57 "failed to get stats for device {} ({})",
58 dev.devid, dev.path
59 )
60 })?;
61
62 print_stats(&dev.path, &stats);
63
64 if !stats.is_clean() {
65 any_nonzero = true;
66 }
67 }
68
69 if self.check && any_nonzero {
70 anyhow::bail!("one or more devices have non-zero error counters");
71 }
72
73 Ok(())
74 }
75}
76
77fn print_stats(path: &str, stats: &DeviceStats) {
80 let p = path;
81 println!("[{p}].{:<24} {}", "write_io_errs", stats.write_errs);
82 println!("[{p}].{:<24} {}", "read_io_errs", stats.read_errs);
83 println!("[{p}].{:<24} {}", "flush_io_errs", stats.flush_errs);
84 println!("[{p}].{:<24} {}", "corruption_errs", stats.corruption_errs);
85 println!("[{p}].{:<24} {}", "generation_errs", stats.generation_errs);
86}