1#![allow(clippy::redundant_closure_call)]
10
11use freebsd_libgeom_sys::*;
12use lazy_static::lazy_static;
13use std::{
14 ffi::CStr,
15 fmt,
16 io::{self, Error},
17 marker::PhantomData,
18 mem::{self, MaybeUninit},
19 ops::Sub,
20 os::raw::c_void,
21 pin::Pin,
22 ptr::NonNull
23};
24
25const BINTIME_SCALE: f64 = 5.421010862427522e-20;
27
28macro_rules! delta {
30 ($current: ident, $previous: ident, $field:ident, $index:expr) => {
31 {
32 let idx = $index as usize;
33 let old = if let Some(prev) = $previous {
34 unsafe {prev.devstat.as_ref() }.$field[idx]
35 } else {
36 0
37 };
38 let new = unsafe {$current.devstat.as_ref() }.$field[idx];
39 new - old
40 }
41 }
42}
43
44macro_rules! delta_t {
45 ($cur: expr, $prev: expr, $bintime:expr) => {
46 {
47 let old: bintime = if let Some(prev) = $prev {
48 $bintime(unsafe {prev.devstat.as_ref() })
49 } else {
50 bintime{sec: 0, frac: 0}
51 };
52 let new: bintime = $bintime(unsafe {$cur.devstat.as_ref() });
53 let mut dsec = new.sec - old.sec;
54 let (dfrac, overflow) = new.frac.overflowing_sub(old.frac);
55 if overflow {
56 dsec -= 1;
57 }
58 dsec as f64 + dfrac as f64 * BINTIME_SCALE
59 }
60 }
61}
62
63macro_rules! fields {
64 ($self: ident, $meth: ident, $field: ident) => {
65 pub fn $meth(&$self) -> u64 {
66 $self.$field
67 }
68 }
69}
70
71macro_rules! fields_per_sec {
72 ($self: ident, $meth: ident, $field: ident) => {
73 pub fn $meth(&$self) -> f64 {
74 if $self.etime > 0.0 {
75 $self.$field as f64 / $self.etime
76 } else {
77 0.0
78 }
79 }
80 }
81}
82
83macro_rules! kb_per_xfer {
84 ($self: ident, $meth: ident, $xfers: ident, $bytes: ident) => {
85 pub fn $meth(&$self) -> f64 {
86 if $self.$xfers > 0 {
87 $self.$bytes as f64 / (1<<10) as f64 / $self.$xfers as f64
88 } else {
89 0.0
90 }
91 }
92 }
93}
94
95macro_rules! mb_per_sec {
96 ($self: ident, $meth: ident, $field: ident) => {
97 pub fn $meth(&$self) -> f64 {
98 if $self.etime > 0.0 {
99 $self.$field as f64 / (1<<20) as f64 / $self.etime
100 } else {
101 0.0
102 }
103 }
104 }
105}
106
107macro_rules! ms_per_xfer {
108 ($self: ident, $meth: ident, $xfers: ident, $duration: ident) => {
109 pub fn $duration(&$self) -> f64 {
110 $self.$duration
111 }
112 pub fn $meth(&$self) -> f64 {
113 if $self.$xfers > 0 {
114 $self.$duration * 1000.0 / $self.$xfers as f64
115 } else {
116 0.0
117 }
118 }
119 }
120}
121
122lazy_static! {
123 static ref GEOM_STATS: io::Result<()> = {
124 let r = unsafe { geom_stats_open() };
125 if r != 0 {
126 Err(Error::last_os_error())
127 } else {
128 Ok(())
129 }
130 };
131}
132
133#[derive(Debug, Copy, Clone)]
135#[repr(transparent)]
136pub struct Devstat<'a>{
137 devstat: NonNull<devstat>,
138 phantom: PhantomData<&'a devstat>
139}
140
141impl<'a> Devstat<'a> {
142 pub fn id(&'a self) -> Id<'a> {
143 Id {
144 id: unsafe { self.devstat.as_ref() }.id,
145 phantom: PhantomData
146 }
147 }
148}
149
150#[derive(Clone, Copy, Debug)]
151#[non_exhaustive]
152pub enum GidentError {
153 NotAProvider
154}
155
156impl fmt::Display for GidentError {
157 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
158 match self {
159 GidentError::NotAProvider => {write!(f, "Not a GEOM provider")}
160 }
161 }
162}
163
164#[derive(Debug, Copy, Clone)]
166pub struct Gident<'a>{
167 ident: NonNull<gident>,
168 phantom: PhantomData<&'a Tree>
169}
170
171impl<'a> Gident<'a> {
172 pub fn is_consumer(&self) -> bool {
173 unsafe{self.ident.as_ref()}.lg_what == gident_ISCONSUMER
174 }
175
176 pub fn is_provider(&self) -> bool {
177 unsafe{self.ident.as_ref()}.lg_what == gident_ISPROVIDER
178 }
179
180 pub fn name(&self) -> Result<&'a CStr, GidentError> {
181 if !self.is_provider() {
182 Err(GidentError::NotAProvider)
183 } else {
184 unsafe{
185 let gprovider = self.ident.as_ref().lg_ptr as *const gprovider;
186 assert!(!gprovider.is_null());
187 Ok(CStr::from_ptr((*gprovider).lg_name))
188 }
189 }
190 }
191
192 pub fn rank(&self) -> Option<u32> {
194 if !self.is_provider() {
195 None
196 } else {
197 unsafe{
198 let gprovider = self.ident.as_ref().lg_ptr as *const gprovider;
199 assert!(!gprovider.is_null());
200 let geom = (*gprovider).lg_geom;
201 if geom.is_null() {
202 None
203 } else {
204 Some((*geom).lg_rank)
205 }
206 }
207 }
208 }
209}
210
211#[derive(Debug, Copy, Clone)]
215pub struct Id<'a> {
216 id: *const c_void,
217 phantom: PhantomData<&'a Devstat<'a>>
218}
219
220pub struct SnapshotPairIter<'a> {
223 cur: &'a mut Snapshot,
224 prev: Option<&'a mut Snapshot>
225}
226
227impl<'a> SnapshotPairIter<'a> {
228 fn new(cur: &'a mut Snapshot, prev: Option<&'a mut Snapshot>) -> Self {
229 SnapshotPairIter{cur, prev}
230 }
231}
232
233impl<'a> Iterator for SnapshotPairIter<'a> {
234 type Item = (Devstat<'a>, Option<Devstat<'a>>);
235
236 fn next(&mut self) -> Option<Self::Item> {
237 let ps = if let Some(prev) = self.prev.as_mut() {
238 let praw = unsafe {geom_stats_snapshot_next(prev.0.as_mut()) };
239 NonNull::new(praw)
240 .map(|devstat| Devstat{devstat, phantom: PhantomData})
241 } else {
242 None
243 };
244 let craw = unsafe {geom_stats_snapshot_next(self.cur.0.as_mut()) };
245 NonNull::new(craw)
246 .map(|devstat| (Devstat{devstat, phantom: PhantomData}, ps))
247 }
248}
249
250impl<'a> Drop for SnapshotPairIter<'a> {
251 fn drop(&mut self) {
252 self.cur.reset();
253 if let Some(prev) = self.prev.as_mut() {
254 prev.reset()
255 }
256 }
257}
258
259pub struct Snapshot(NonNull<c_void>);
264
265impl Snapshot {
266 pub fn iter(&mut self) -> SnapshotIter {
268 SnapshotIter(self)
269 }
270
271 pub fn iter_pair<'a>(&'a mut self, prev: Option<&'a mut Snapshot>)
274 -> SnapshotPairIter<'a>
275 {
276 SnapshotPairIter::new(self, prev)
277 }
278
279 pub fn new() -> io::Result<Self> {
283 GEOM_STATS.as_ref().unwrap();
284 let raw = unsafe { geom_stats_snapshot_get() };
285 NonNull::new(raw)
286 .map(Snapshot)
287 .ok_or_else(Error::last_os_error)
288 }
289
290 fn reset(&mut self) {
292 unsafe {geom_stats_snapshot_reset(self.0.as_mut())}
293 }
294
295 pub fn timestamp(&mut self) -> Timespec {
299 let inner = unsafe {
300 let mut ts = MaybeUninit::uninit();
301 geom_stats_snapshot_timestamp(self.0.as_mut(), ts.as_mut_ptr());
302 ts.assume_init()
303 };
304 Timespec(inner)
305 }
306}
307
308impl Drop for Snapshot {
309 fn drop(&mut self) {
310 unsafe { geom_stats_snapshot_free(self.0.as_mut()) };
311 }
312}
313
314pub struct SnapshotIter<'a>(&'a mut Snapshot);
316
317impl<'a> Iterator for SnapshotIter<'a> {
318 type Item = Devstat<'a>;
319
320 fn next(&mut self) -> Option<Self::Item> {
321 let raw = unsafe {geom_stats_snapshot_next(self.0.0.as_mut()) };
322 NonNull::new(raw)
323 .map(|devstat| Devstat{devstat, phantom: PhantomData})
324 }
325}
326
327impl<'a> Drop for SnapshotIter<'a> {
328 fn drop(&mut self) {
329 self.0.reset();
330 }
331}
332
333pub struct Statistics<'a>{
342 current: Devstat<'a>,
343 previous: Option<Devstat<'a>>,
344 etime: f64,
345 total_bytes: u64,
346 total_bytes_free: u64,
347 total_bytes_read: u64,
348 total_bytes_write: u64,
349 total_blocks: u64,
350 total_blocks_free: u64,
351 total_blocks_read: u64,
352 total_blocks_write: u64,
353 total_duration: f64,
354 total_duration_free: f64,
355 total_duration_other: f64,
356 total_duration_read: f64,
357 total_duration_write: f64,
358 total_transfers: u64,
359 total_transfers_free: u64,
360 total_transfers_other: u64,
361 total_transfers_read: u64,
362 total_transfers_write: u64,
363}
364
365impl<'a> Statistics<'a> {
366 pub fn compute(
373 current: Devstat<'a>,
374 previous: Option<Devstat<'a>>,
375 etime: f64) -> Self
376 {
377 let cur = unsafe { current.devstat.as_ref() };
378
379 let total_transfers_read = delta!(current, previous, operations,
380 devstat_trans_flags_DEVSTAT_READ);
381 let total_transfers_write = delta!(current, previous, operations,
382 devstat_trans_flags_DEVSTAT_WRITE);
383 let total_transfers_other = delta!(current, previous, operations,
384 devstat_trans_flags_DEVSTAT_NO_DATA);
385 let total_transfers_free = delta!(current, previous, operations,
386 devstat_trans_flags_DEVSTAT_FREE);
387 let total_transfers = total_transfers_read + total_transfers_write +
388 total_transfers_other + total_transfers_free;
389
390 let total_bytes_free = delta!(current, previous, bytes,
391 devstat_trans_flags_DEVSTAT_FREE);
392 let total_bytes_read = delta!(current, previous, bytes,
393 devstat_trans_flags_DEVSTAT_READ);
394 let total_bytes_write = delta!(current, previous, bytes,
395 devstat_trans_flags_DEVSTAT_WRITE);
396 let total_bytes = total_bytes_read + total_bytes_write +
397 total_bytes_free;
398
399 let block_denominator = if cur.block_size > 0 {
400 cur.block_size as u64
401 } else {
402 512u64
403 };
404 let total_blocks = total_bytes / block_denominator;
405 let total_blocks_free = total_bytes_free / block_denominator;
406 let total_blocks_read = total_bytes_read / block_denominator;
407 let total_blocks_write = total_bytes_write / block_denominator;
408
409 let total_duration_free = delta_t!(current, previous,
410 |ds: &devstat|
411 ds.duration[devstat_trans_flags_DEVSTAT_FREE as usize]
412 );
413 let total_duration_read = delta_t!(current, previous,
414 |ds: &devstat|
415 ds.duration[devstat_trans_flags_DEVSTAT_READ as usize]
416 );
417 let total_duration_write = delta_t!(current, previous,
418 |ds: &devstat|
419 ds.duration[devstat_trans_flags_DEVSTAT_WRITE as usize]
420 );
421 let total_duration_other = delta_t!(current, previous,
422 |ds: &devstat|
423 ds.duration[devstat_trans_flags_DEVSTAT_NO_DATA as usize]
424 );
425 let total_duration = total_duration_read + total_duration_write +
426 total_duration_other + total_duration_free;
427
428 Self{
429 current,
430 previous,
431 etime,
432 total_bytes,
433 total_bytes_free,
434 total_bytes_read,
435 total_bytes_write,
436 total_blocks,
437 total_blocks_free,
438 total_blocks_read,
439 total_blocks_write,
440 total_duration,
441 total_duration_free,
442 total_duration_other,
443 total_duration_read,
444 total_duration_write,
445 total_transfers,
446 total_transfers_free,
447 total_transfers_other,
448 total_transfers_read,
449 total_transfers_write,
450 }
451 }
452
453 pub fn busy_time(&self) -> f64 {
454 let bt = unsafe{ self.current.devstat.as_ref() };
455 bt.busy_time.sec as f64 + bt.busy_time.frac as f64 * BINTIME_SCALE
456 }
457
458 pub fn busy_pct(&self) -> f64 {
461 let delta = delta_t!(self.current, &self.previous,
462 |ds: &devstat| ds.busy_time);
463 (delta / self.etime * 100.0).max(0.0)
464 }
465
466 pub fn queue_length(&self) -> u32 {
469 let cur = unsafe {self.current.devstat.as_ref() };
470 cur.start_count - cur.end_count
471 }
472
473 fields!{self, total_bytes, total_bytes}
474 fields!{self, total_bytes_free, total_bytes_free}
475 fields!{self, total_bytes_read, total_bytes_read}
476 fields!{self, total_bytes_write, total_bytes_write}
477 fields!{self, total_blocks, total_blocks}
478 fields!{self, total_blocks_free, total_blocks_free}
479 fields!{self, total_blocks_read, total_blocks_read}
480 fields!{self, total_blocks_write, total_blocks_write}
481 fields!{self, total_transfers, total_transfers}
482 fields!{self, total_transfers_free, total_transfers_free}
483 fields!{self, total_transfers_read, total_transfers_read}
484 fields!{self, total_transfers_other, total_transfers_other}
485 fields!{self, total_transfers_write, total_transfers_write}
486 fields_per_sec!{self, blocks_per_second, total_blocks}
487 fields_per_sec!{self, blocks_per_second_free, total_blocks_free}
488 fields_per_sec!{self, blocks_per_second_read, total_blocks_read}
489 fields_per_sec!{self, blocks_per_second_write, total_blocks_write}
490 kb_per_xfer!{self, kb_per_transfer, total_transfers, total_bytes}
491 kb_per_xfer!{self, kb_per_transfer_free, total_transfers_free, total_bytes}
492 kb_per_xfer!{self, kb_per_transfer_read, total_transfers_read, total_bytes}
493 kb_per_xfer!{self, kb_per_transfer_write, total_transfers_write,
494 total_bytes}
495 ms_per_xfer!{self, ms_per_transaction, total_transfers, total_duration}
496 ms_per_xfer!{self, ms_per_transaction_free, total_transfers_free,
497 total_duration_free}
498 ms_per_xfer!{self, ms_per_transaction_read, total_transfers_read,
499 total_duration_read}
500 ms_per_xfer!{self, ms_per_transaction_other, total_transfers_other,
501 total_duration_other}
502 ms_per_xfer!{self, ms_per_transaction_write, total_transfers_write,
503 total_duration_write}
504 mb_per_sec!{self, mb_per_second, total_bytes}
505 mb_per_sec!{self, mb_per_second_free, total_bytes_free}
506 mb_per_sec!{self, mb_per_second_read, total_bytes_read}
507 mb_per_sec!{self, mb_per_second_write, total_bytes_write}
508 fields_per_sec!{self, transfers_per_second, total_transfers}
509 fields_per_sec!{self, transfers_per_second_free, total_transfers_free}
510 fields_per_sec!{self, transfers_per_second_other, total_transfers_other}
511 fields_per_sec!{self, transfers_per_second_read, total_transfers_read}
512 fields_per_sec!{self, transfers_per_second_write, total_transfers_write}
513}
514
515#[repr(transparent)]
517#[derive(Debug, Copy, Clone)]
518pub struct Timespec(freebsd_libgeom_sys::timespec);
520
521impl From<Timespec> for f64 {
522 fn from(ts: Timespec) -> f64 {
523 ts.0.tv_sec as f64 + ts.0.tv_nsec as f64 * 1e-9
524 }
525}
526
527impl Sub for Timespec {
528 type Output = Self;
529
530 fn sub(self, rhs: Timespec) -> Self::Output {
531 let mut tv_sec = self.0.tv_sec - rhs.0.tv_sec;
532 let mut tv_nsec = self.0.tv_nsec - rhs.0.tv_nsec;
533 if tv_nsec < 0 {
534 tv_sec -= 1;
535 tv_nsec += 1_000_000_000;
536 }
537 Self(freebsd_libgeom_sys::timespec {tv_sec, tv_nsec})
538 }
539}
540
541#[derive(Debug)]
543#[repr(transparent)]
544pub struct Tree(Pin<Box<gmesh>>);
545
546impl Tree {
547 pub fn lookup<'a>(&'a mut self, id: Id) -> Option<Gident<'a>> {
549 let raw = unsafe {
550 geom_lookupid(&mut *self.0, id.id)
551 };
552 NonNull::new(raw)
553 .map(|ident| Gident{ident, phantom: PhantomData})
554 }
555
556 pub fn new() -> io::Result<Self> {
558 let (inner, r) = unsafe {
559 let mut inner = Box::pin(mem::zeroed());
560 let r = geom_gettree(&mut *inner);
561 (inner, r)
562 };
563 if r != 0 {
564 Err(Error::last_os_error())
565 } else {
566 Ok(Tree(inner))
567 }
568 }
569}
570
571impl Drop for Tree {
572 fn drop(&mut self) {
573 unsafe { geom_deletetree(&mut *self.0) };
574 }
575}
576
577#[cfg(test)]
578mod t {
579 use super::*;
580 use approx::*;
581
582 mod delta_t {
583 use super::*;
584
585 macro_rules! devstat {
586 ($bintime: expr) => {{
587 let inner = unsafe{ devstat {
588 busy_time: $bintime,
589 .. mem::zeroed()
590 }};
591 let outer = Devstat {
592 devstat: NonNull::from(&inner),
593 phantom: PhantomData
594 };
595 (outer, inner)
596 }}
597 }
598
599 #[test]
600 fn zero() {
601 let (prev, _prev) = devstat!(bintime{sec: 0, frac: 0});
602 let (cur, _cur) = devstat!(bintime{sec: 0, frac: 0});
603 let r = delta_t!(cur, Some(prev), |ds: &devstat| ds.busy_time);
604 assert_relative_eq!(r, 0.0);
605 }
606
607 #[test]
608 fn half() {
609 let (prev, _prev) = devstat!(bintime{sec: 0, frac: 0});
610 let (cur, _cur) = devstat!(bintime{sec: 0, frac: 1<<63});
611 let r = delta_t!(cur, Some(prev), |ds: &devstat| ds.busy_time);
612 assert_relative_eq!(r, 0.5);
613 }
614
615 #[test]
616 fn half2() {
617 let (prev, _prev) = devstat!(bintime{sec: 0, frac: 1<<63});
618 let (cur, _cur) = devstat!(bintime{sec: 1, frac: 0});
619 let r = delta_t!(cur, Some(prev), |ds: &devstat| ds.busy_time);
620 assert_relative_eq!(r, 0.5);
621 }
622
623 #[test]
624 fn one() {
625 let (prev, _prev) = devstat!(bintime{sec: 0, frac: 0});
626 let (cur, _cur) = devstat!(bintime{sec: 1, frac: 0});
627 let r = delta_t!(cur, Some(prev), |ds: &devstat| ds.busy_time);
628 assert_relative_eq!(r, 1.0);
629 }
630
631 #[test]
632 fn neg() {
633 let (prev, _prev) = devstat!(bintime{sec: 1, frac: 1<<62});
634 let (cur, _cur) = devstat!(bintime{sec: 0, frac: 0});
635 let r = delta_t!(cur, Some(prev), |ds: &devstat| ds.busy_time);
636 assert_relative_eq!(r, -1.25);
637 }
638 }
639}