1#[cfg(feature = "serde")]
2use serde::{Deserialize, Serialize};
3
4use std::collections::HashMap;
5use std::time::Duration;
6
7use derive_more::{Add, Sub, Sum};
8
9use crate::disk::disk_io_counters_per_partition;
10use crate::{Bytes, Count, Result};
11
12#[cfg_attr(feature = "serde", serde(crate = "renamed_serde"))]
13#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
14#[derive(Clone, Debug, Add, Sum, Default, Sub)]
15pub struct DiskIoCounters {
16 pub(crate) read_count: Count,
17 pub(crate) write_count: Count,
18 pub(crate) read_bytes: Bytes,
19 pub(crate) write_bytes: Bytes,
20
21 #[cfg(not(any(target_os = "netbsd", target_os = "openbsd")))]
22 pub(crate) read_time: Duration,
23 #[cfg(not(any(target_os = "netbsd", target_os = "openbsd")))]
24 pub(crate) write_time: Duration,
25
26 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
27 pub(crate) busy_time: Duration,
28
29 #[cfg(target_os = "linux")]
30 pub(crate) read_merged_count: Count,
31 #[cfg(target_os = "linux")]
32 pub(crate) write_merged_count: Count,
33}
34
35impl DiskIoCounters {
36 pub fn read_count(&self) -> Count {
38 self.read_count
39 }
40
41 pub fn write_count(&self) -> Count {
43 self.write_count
44 }
45
46 pub fn read_bytes(&self) -> Bytes {
48 self.read_bytes
49 }
50
51 pub fn write_bytes(&self) -> Bytes {
53 self.write_bytes
54 }
55}
56
57fn nowrap(prev: u64, current: u64, corrected: u64) -> u64 {
58 if current >= prev {
59 corrected + (current - prev)
60 } else {
61 corrected + current + ((u32::MAX as u64) - prev)
62 }
63}
64
65fn nowrap_struct(
66 prev: &DiskIoCounters,
67 current: &DiskIoCounters,
68 corrected: &DiskIoCounters,
69) -> DiskIoCounters {
70 DiskIoCounters {
71 read_count: nowrap(prev.read_count, current.read_count, corrected.read_count),
72 write_count: nowrap(prev.write_count, current.write_count, corrected.write_count),
73 read_bytes: nowrap(prev.read_bytes, current.read_bytes, corrected.read_bytes),
74 write_bytes: nowrap(prev.write_bytes, current.write_bytes, corrected.write_bytes),
75
76 #[cfg(not(any(target_os = "netbsd", target_os = "openbsd")))]
77 read_time: Duration::from_millis(nowrap(
78 prev.read_time.as_millis() as u64,
79 current.read_time.as_millis() as u64,
80 corrected.read_time.as_millis() as u64,
81 )),
82 #[cfg(not(any(target_os = "netbsd", target_os = "openbsd")))]
83 write_time: Duration::from_millis(nowrap(
84 prev.write_time.as_millis() as u64,
85 current.write_time.as_millis() as u64,
86 corrected.write_time.as_millis() as u64,
87 )),
88
89 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
90 busy_time: Duration::from_millis(nowrap(
91 prev.busy_time.as_millis() as u64,
92 current.busy_time.as_millis() as u64,
93 corrected.busy_time.as_millis() as u64,
94 )),
95
96 #[cfg(target_os = "linux")]
97 read_merged_count: nowrap(
98 prev.read_merged_count,
99 current.read_merged_count,
100 corrected.read_merged_count,
101 ),
102 #[cfg(target_os = "linux")]
103 write_merged_count: nowrap(
104 prev.write_merged_count,
105 current.write_merged_count,
106 corrected.write_merged_count,
107 ),
108 }
109}
110
111fn fix_io_counter_overflow(
112 prev: &HashMap<String, DiskIoCounters>,
113 current: &HashMap<String, DiskIoCounters>,
114 corrected: &HashMap<String, DiskIoCounters>,
115) -> HashMap<String, DiskIoCounters> {
116 current
117 .iter()
118 .map(|(name, current_counters)| {
119 if !prev.contains_key(name) || !corrected.contains_key(name) {
120 (name.clone(), current_counters.clone())
121 } else {
122 let prev_counters = &prev[name];
123 let corrected_counters = &corrected[name];
124
125 (
126 name.clone(),
127 nowrap_struct(prev_counters, current_counters, corrected_counters),
128 )
129 }
130 })
131 .collect()
132}
133
134#[derive(Clone, Debug, Default)]
137pub struct DiskIoCountersCollector {
138 prev_disk_io_counters_per_partition: Option<HashMap<String, DiskIoCounters>>,
139 corrected_disk_io_counters_per_partition: Option<HashMap<String, DiskIoCounters>>,
140}
141
142impl DiskIoCountersCollector {
143 pub fn disk_io_counters(&mut self) -> Result<DiskIoCounters> {
144 let sum = self.disk_io_counters_per_partition()?.into_values().sum();
145
146 Ok(sum)
147 }
148
149 pub fn disk_io_counters_per_partition(&mut self) -> Result<HashMap<String, DiskIoCounters>> {
150 let io_counters = disk_io_counters_per_partition()?;
151
152 let corrected_counters = match (
153 &self.prev_disk_io_counters_per_partition,
154 &self.corrected_disk_io_counters_per_partition,
155 ) {
156 (Some(prev), Some(corrected)) => fix_io_counter_overflow(prev, &io_counters, corrected),
157 _ => io_counters.clone(),
158 };
159
160 self.prev_disk_io_counters_per_partition = Some(io_counters);
161 self.corrected_disk_io_counters_per_partition = Some(corrected_counters.clone());
162
163 Ok(corrected_counters)
164 }
165}