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