1use std::io::Write;
12use std::path::PathBuf;
13
14use crate::error::ErrorKind::*;
15use crate::error::*;
16
17use crate::{read_string_from, read_u64_from};
18use crate::{
19 BlkIoResources, ControllIdentifier, ControllerInternal, Controllers, CustomizedAttribute,
20 Resources, Subsystem,
21};
22
23#[derive(Debug, Clone)]
28pub struct BlkIoController {
29 base: PathBuf,
30 path: PathBuf,
31 v2: bool,
32}
33
34#[derive(Eq, PartialEq, Debug)]
35#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
36pub struct BlkIoData {
38 pub major: i16,
40 pub minor: i16,
42 pub data: u64,
44}
45
46#[derive(Eq, PartialEq, Debug, Default)]
47#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
48pub struct IoService {
50 pub major: i16,
52 pub minor: i16,
54 pub read: u64,
56 pub write: u64,
58 pub sync: u64,
60 pub r#async: u64,
62 pub discard: u64,
64 pub total: u64,
66}
67
68#[derive(Eq, PartialEq, Debug)]
69#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
70pub struct IoStat {
73 pub major: i16,
75 pub minor: i16,
77 pub rbytes: u64,
79 pub wbytes: u64,
81 pub rios: u64,
83 pub wios: u64,
85 pub dbytes: u64,
87 pub dios: u64,
89}
90
91fn parse_io_service(s: String) -> Result<Vec<IoService>> {
92 let mut io_services = Vec::<IoService>::new();
93 let mut io_service = IoService::default();
94
95 let lines = s
96 .lines()
97 .filter(|x| x.split_whitespace().count() == 3)
98 .map(|x| {
99 let mut spl = x.split_whitespace();
100 (
101 spl.next().unwrap(),
102 spl.next().unwrap(),
103 spl.next().unwrap(),
104 )
105 })
106 .map(|(a, b, c)| {
107 let mut spl = a.split(':');
108 (
109 spl.next().unwrap().parse::<i16>(),
110 spl.next().unwrap().parse::<i16>(),
111 b,
112 c,
113 )
114 })
115 .collect::<Vec<_>>();
116
117 for (major_num, minor_num, op, val) in lines.iter() {
118 let major = *major_num.as_ref().map_err(|_| Error::new(ParseError))?;
119 let minor = *minor_num.as_ref().map_err(|_| Error::new(ParseError))?;
120
121 if (major != io_service.major || minor != io_service.minor) && io_service.major != 0 {
122 io_services.push(io_service);
124 io_service = IoService::default();
125 }
126
127 io_service.major = major;
128 io_service.minor = minor;
129
130 let val = val.parse::<u64>().map_err(|_| Error::new(ParseError))?;
131
132 match *op {
133 "Read" => io_service.read = val,
134 "Write" => io_service.write = val,
135 "Sync" => io_service.sync = val,
136 "Async" => io_service.r#async = val,
137 "Discard" => io_service.discard = val,
138 "Total" => io_service.total = val,
139 _ => {}
140 }
141 }
142
143 if io_service.major != 0 {
144 io_services.push(io_service);
145 }
146
147 Ok(io_services)
148}
149
150fn get_value(s: &str) -> String {
151 let arr = s.split(':').collect::<Vec<&str>>();
152 if arr.len() != 2 {
153 return "0".to_string();
154 }
155 arr[1].to_string()
156}
157
158fn parse_io_stat(s: String) -> Vec<IoStat> {
159 s.lines()
162 .filter(|x| x.split_whitespace().count() == 7)
163 .map(|x| {
164 let arr = x.split_whitespace().collect::<Vec<&str>>();
165 let device = arr[0].split(':').collect::<Vec<&str>>();
166 let (major, minor) = (device[0], device[1]);
167
168 IoStat {
169 major: major.parse::<i16>().unwrap(),
170 minor: minor.parse::<i16>().unwrap(),
171 rbytes: get_value(arr[1]).parse::<u64>().unwrap(),
172 wbytes: get_value(arr[2]).parse::<u64>().unwrap(),
173 rios: get_value(arr[3]).parse::<u64>().unwrap(),
174 wios: get_value(arr[4]).parse::<u64>().unwrap(),
175 dbytes: get_value(arr[5]).parse::<u64>().unwrap(),
176 dios: get_value(arr[6]).parse::<u64>().unwrap(),
177 }
178 })
179 .collect::<Vec<IoStat>>()
180}
181
182fn parse_io_service_total(s: String) -> Result<u64> {
183 s.lines()
184 .filter(|x| x.split_whitespace().count() == 2)
185 .fold(Err(Error::new(ParseError)), |_, x| {
186 match x.split_whitespace().collect::<Vec<_>>().as_slice() {
187 ["Total", val] => val.parse::<u64>().map_err(|_| Error::new(ParseError)),
188 _ => Err(Error::new(ParseError)),
189 }
190 })
191}
192
193fn parse_blkio_data(s: String) -> Result<Vec<BlkIoData>> {
194 let r = s
195 .chars()
196 .map(|x| if x == ':' { ' ' } else { x })
197 .collect::<String>();
198
199 let r = r
200 .lines()
201 .flat_map(|x| x.split_whitespace())
202 .collect::<Vec<_>>();
203
204 let r = r.chunks(3).collect::<Vec<_>>();
205
206 let mut res = Vec::new();
207
208 let err = r.iter().try_for_each(|x| match x {
209 [major, minor, data] => {
210 res.push(BlkIoData {
211 major: major.parse::<i16>().unwrap(),
212 minor: minor.parse::<i16>().unwrap(),
213 data: data.parse::<u64>().unwrap(),
214 });
215 Ok(())
216 }
217 _ => Err(Error::new(ParseError)),
218 });
219
220 if err.is_err() {
221 Err(Error::new(ParseError))
222 } else {
223 Ok(res)
224 }
225}
226
227#[derive(Default, Debug)]
230#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
231pub struct BlkIoThrottle {
232 pub io_service_bytes: Vec<IoService>,
235 pub io_service_bytes_total: u64,
237 pub io_service_bytes_recursive: Vec<IoService>,
239 pub io_service_bytes_recursive_total: u64,
242 pub io_serviced: Vec<IoService>,
244 pub io_serviced_total: u64,
247 pub io_serviced_recursive: Vec<IoService>,
249 pub io_serviced_recursive_total: u64,
252 pub read_bps_device: Vec<BlkIoData>,
255 pub read_iops_device: Vec<BlkIoData>,
257 pub write_bps_device: Vec<BlkIoData>,
260 pub write_iops_device: Vec<BlkIoData>,
262}
263
264#[derive(Default, Debug)]
266#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
267pub struct BlkIo {
268 pub io_merged: Vec<IoService>,
270 pub io_merged_total: u64,
272 pub io_merged_recursive: Vec<IoService>,
274 pub io_merged_recursive_total: u64,
276 pub io_queued: Vec<IoService>,
278 pub io_queued_total: u64,
280 pub io_queued_recursive: Vec<IoService>,
282 pub io_queued_recursive_total: u64,
284 pub io_service_bytes: Vec<IoService>,
286 pub io_service_bytes_total: u64,
288 pub io_service_bytes_recursive: Vec<IoService>,
290 pub io_service_bytes_recursive_total: u64,
293 pub io_serviced: Vec<IoService>,
296 pub io_serviced_total: u64,
299 pub io_serviced_recursive: Vec<IoService>,
301 pub io_serviced_recursive_total: u64,
304 pub io_service_time: Vec<IoService>,
307 pub io_service_time_total: u64,
310 pub io_service_time_recursive: Vec<IoService>,
312 pub io_service_time_recursive_total: u64,
315 pub io_wait_time: Vec<IoService>,
317 pub io_wait_time_total: u64,
319 pub io_wait_time_recursive: Vec<IoService>,
321 pub io_wait_time_recursive_total: u64,
323 pub leaf_weight: u64,
326 pub leaf_weight_device: Vec<BlkIoData>,
328 pub sectors: Vec<BlkIoData>,
331 pub sectors_recursive: Vec<BlkIoData>,
333 pub throttle: BlkIoThrottle,
335 pub time: Vec<BlkIoData>,
337 pub time_recursive: Vec<BlkIoData>,
339 pub weight: u64,
341 pub weight_device: Vec<BlkIoData>,
343
344 pub io_stat: Vec<IoStat>,
346}
347
348impl ControllerInternal for BlkIoController {
349 fn control_type(&self) -> Controllers {
350 Controllers::BlkIo
351 }
352 fn get_path(&self) -> &PathBuf {
353 &self.path
354 }
355 fn get_path_mut(&mut self) -> &mut PathBuf {
356 &mut self.path
357 }
358 fn get_base(&self) -> &PathBuf {
359 &self.base
360 }
361
362 fn is_v2(&self) -> bool {
363 self.v2
364 }
365
366 fn apply(&self, res: &Resources) -> Result<()> {
367 let res: &BlkIoResources = &res.blkio;
369
370 if let Some(weight) = res.weight {
371 let _ = self.set_weight(weight as u64);
372 }
373 if let Some(leaf_weight) = res.leaf_weight {
374 let _ = self.set_leaf_weight(leaf_weight as u64);
375 }
376
377 for dev in &res.weight_device {
378 if let Some(weight) = dev.weight {
379 let _ = self.set_weight_for_device(dev.major, dev.minor, weight as u64);
380 }
381 if let Some(leaf_weight) = dev.leaf_weight {
382 let _ = self.set_leaf_weight_for_device(dev.major, dev.minor, leaf_weight as u64);
383 }
384 }
385
386 for dev in &res.throttle_read_bps_device {
387 let _ = self.throttle_read_bps_for_device(dev.major, dev.minor, dev.rate);
388 }
389
390 for dev in &res.throttle_write_bps_device {
391 let _ = self.throttle_write_bps_for_device(dev.major, dev.minor, dev.rate);
392 }
393
394 for dev in &res.throttle_read_iops_device {
395 let _ = self.throttle_read_iops_for_device(dev.major, dev.minor, dev.rate);
396 }
397
398 for dev in &res.throttle_write_iops_device {
399 let _ = self.throttle_write_iops_for_device(dev.major, dev.minor, dev.rate);
400 }
401
402 res.attrs.iter().for_each(|(k, v)| {
403 let _ = self.set(k, v);
404 });
405
406 Ok(())
407 }
408}
409
410impl ControllIdentifier for BlkIoController {
411 fn controller_type() -> Controllers {
412 Controllers::BlkIo
413 }
414}
415
416impl<'a> From<&'a Subsystem> for &'a BlkIoController {
417 fn from(sub: &'a Subsystem) -> &'a BlkIoController {
418 unsafe {
419 match sub {
420 Subsystem::BlkIo(c) => c,
421 _ => {
422 assert_eq!(1, 0);
423 let v = std::mem::MaybeUninit::uninit();
424 v.assume_init()
425 }
426 }
427 }
428 }
429}
430
431impl BlkIoController {
432 pub fn new(root: PathBuf, v2: bool) -> Self {
434 Self {
435 base: root.clone(),
436 path: root,
437 v2,
438 }
439 }
440
441 fn blkio_v2(&self) -> BlkIo {
442 BlkIo {
443 io_stat: self
444 .open_path("io.stat", false)
445 .and_then(read_string_from)
446 .map(parse_io_stat)
447 .unwrap_or_default(),
448 ..Default::default()
449 }
450 }
451
452 pub fn blkio(&self) -> BlkIo {
455 if self.v2 {
456 return self.blkio_v2();
457 }
458 BlkIo {
459 io_merged: self
460 .open_path("blkio.io_merged", false)
461 .and_then(read_string_from)
462 .and_then(parse_io_service)
463 .unwrap_or_default(),
464 io_merged_total: self
465 .open_path("blkio.io_merged", false)
466 .and_then(read_string_from)
467 .and_then(parse_io_service_total)
468 .unwrap_or_default(),
469 io_merged_recursive: self
470 .open_path("blkio.io_merged_recursive", false)
471 .and_then(read_string_from)
472 .and_then(parse_io_service)
473 .unwrap_or_default(),
474 io_merged_recursive_total: self
475 .open_path("blkio.io_merged_recursive", false)
476 .and_then(read_string_from)
477 .and_then(parse_io_service_total)
478 .unwrap_or_default(),
479 io_queued: self
480 .open_path("blkio.io_queued", false)
481 .and_then(read_string_from)
482 .and_then(parse_io_service)
483 .unwrap_or_default(),
484 io_queued_total: self
485 .open_path("blkio.io_queued", false)
486 .and_then(read_string_from)
487 .and_then(parse_io_service_total)
488 .unwrap_or_default(),
489 io_queued_recursive: self
490 .open_path("blkio.io_queued_recursive", false)
491 .and_then(read_string_from)
492 .and_then(parse_io_service)
493 .unwrap_or_default(),
494 io_queued_recursive_total: self
495 .open_path("blkio.io_queued_recursive", false)
496 .and_then(read_string_from)
497 .and_then(parse_io_service_total)
498 .unwrap_or_default(),
499 io_service_bytes: self
500 .open_path("blkio.io_service_bytes", false)
501 .and_then(read_string_from)
502 .and_then(parse_io_service)
503 .unwrap_or_default(),
504 io_service_bytes_total: self
505 .open_path("blkio.io_service_bytes", false)
506 .and_then(read_string_from)
507 .and_then(parse_io_service_total)
508 .unwrap_or_default(),
509 io_service_bytes_recursive: self
510 .open_path("blkio.io_service_bytes_recursive", false)
511 .and_then(read_string_from)
512 .and_then(parse_io_service)
513 .unwrap_or_default(),
514 io_service_bytes_recursive_total: self
515 .open_path("blkio.io_service_bytes_recursive", false)
516 .and_then(read_string_from)
517 .and_then(parse_io_service_total)
518 .unwrap_or_default(),
519 io_serviced: self
520 .open_path("blkio.io_serviced", false)
521 .and_then(read_string_from)
522 .and_then(parse_io_service)
523 .unwrap_or_default(),
524 io_serviced_total: self
525 .open_path("blkio.io_serviced", false)
526 .and_then(read_string_from)
527 .and_then(parse_io_service_total)
528 .unwrap_or_default(),
529 io_serviced_recursive: self
530 .open_path("blkio.io_serviced_recursive", false)
531 .and_then(read_string_from)
532 .and_then(parse_io_service)
533 .unwrap_or_default(),
534 io_serviced_recursive_total: self
535 .open_path("blkio.io_serviced_recursive", false)
536 .and_then(read_string_from)
537 .and_then(parse_io_service_total)
538 .unwrap_or_default(),
539 io_service_time: self
540 .open_path("blkio.io_service_time", false)
541 .and_then(read_string_from)
542 .and_then(parse_io_service)
543 .unwrap_or_default(),
544 io_service_time_total: self
545 .open_path("blkio.io_service_time", false)
546 .and_then(read_string_from)
547 .and_then(parse_io_service_total)
548 .unwrap_or_default(),
549 io_service_time_recursive: self
550 .open_path("blkio.io_service_time_recursive", false)
551 .and_then(read_string_from)
552 .and_then(parse_io_service)
553 .unwrap_or_default(),
554 io_service_time_recursive_total: self
555 .open_path("blkio.io_service_time_recursive", false)
556 .and_then(read_string_from)
557 .and_then(parse_io_service_total)
558 .unwrap_or_default(),
559 io_wait_time: self
560 .open_path("blkio.io_wait_time", false)
561 .and_then(read_string_from)
562 .and_then(parse_io_service)
563 .unwrap_or_default(),
564 io_wait_time_total: self
565 .open_path("blkio.io_wait_time", false)
566 .and_then(read_string_from)
567 .and_then(parse_io_service_total)
568 .unwrap_or_default(),
569 io_wait_time_recursive: self
570 .open_path("blkio.io_wait_time_recursive", false)
571 .and_then(read_string_from)
572 .and_then(parse_io_service)
573 .unwrap_or_default(),
574 io_wait_time_recursive_total: self
575 .open_path("blkio.io_wait_time_recursive", false)
576 .and_then(read_string_from)
577 .and_then(parse_io_service_total)
578 .unwrap_or_default(),
579 leaf_weight: self
580 .open_path("blkio.leaf_weight", false)
581 .and_then(read_u64_from)
582 .unwrap_or(0u64),
583 leaf_weight_device: self
584 .open_path("blkio.leaf_weight_device", false)
585 .and_then(read_string_from)
586 .and_then(parse_blkio_data)
587 .unwrap_or_default(),
588 sectors: self
589 .open_path("blkio.sectors", false)
590 .and_then(read_string_from)
591 .and_then(parse_blkio_data)
592 .unwrap_or_default(),
593 sectors_recursive: self
594 .open_path("blkio.sectors_recursive", false)
595 .and_then(read_string_from)
596 .and_then(parse_blkio_data)
597 .unwrap_or_default(),
598 throttle: BlkIoThrottle {
599 io_service_bytes: self
600 .open_path("blkio.throttle.io_service_bytes", false)
601 .and_then(read_string_from)
602 .and_then(parse_io_service)
603 .unwrap_or_default(),
604 io_service_bytes_total: self
605 .open_path("blkio.throttle.io_service_bytes", false)
606 .and_then(read_string_from)
607 .and_then(parse_io_service_total)
608 .unwrap_or_default(),
609 io_service_bytes_recursive: self
610 .open_path("blkio.throttle.io_service_bytes_recursive", false)
611 .and_then(read_string_from)
612 .and_then(parse_io_service)
613 .unwrap_or_default(),
614 io_service_bytes_recursive_total: self
615 .open_path("blkio.throttle.io_service_bytes_recursive", false)
616 .and_then(read_string_from)
617 .and_then(parse_io_service_total)
618 .unwrap_or_default(),
619 io_serviced: self
620 .open_path("blkio.throttle.io_serviced", false)
621 .and_then(read_string_from)
622 .and_then(parse_io_service)
623 .unwrap_or_default(),
624 io_serviced_total: self
625 .open_path("blkio.throttle.io_serviced", false)
626 .and_then(read_string_from)
627 .and_then(parse_io_service_total)
628 .unwrap_or_default(),
629 io_serviced_recursive: self
630 .open_path("blkio.throttle.io_serviced_recursive", false)
631 .and_then(read_string_from)
632 .and_then(parse_io_service)
633 .unwrap_or_default(),
634 io_serviced_recursive_total: self
635 .open_path("blkio.throttle.io_serviced_recursive", false)
636 .and_then(read_string_from)
637 .and_then(parse_io_service_total)
638 .unwrap_or_default(),
639 read_bps_device: self
640 .open_path("blkio.throttle.read_bps_device", false)
641 .and_then(read_string_from)
642 .and_then(parse_blkio_data)
643 .unwrap_or_default(),
644 read_iops_device: self
645 .open_path("blkio.throttle.read_iops_device", false)
646 .and_then(read_string_from)
647 .and_then(parse_blkio_data)
648 .unwrap_or_default(),
649 write_bps_device: self
650 .open_path("blkio.throttle.write_bps_device", false)
651 .and_then(read_string_from)
652 .and_then(parse_blkio_data)
653 .unwrap_or_default(),
654 write_iops_device: self
655 .open_path("blkio.throttle.write_iops_device", false)
656 .and_then(read_string_from)
657 .and_then(parse_blkio_data)
658 .unwrap_or_default(),
659 },
660 time: self
661 .open_path("blkio.time", false)
662 .and_then(read_string_from)
663 .and_then(parse_blkio_data)
664 .unwrap_or_default(),
665 time_recursive: self
666 .open_path("blkio.time_recursive", false)
667 .and_then(read_string_from)
668 .and_then(parse_blkio_data)
669 .unwrap_or_default(),
670 weight: self
671 .open_path("blkio.weight", false)
672 .and_then(read_u64_from)
673 .unwrap_or(0u64),
674 weight_device: self
675 .open_path("blkio.weight_device", false)
676 .and_then(read_string_from)
677 .and_then(parse_blkio_data)
678 .unwrap_or_default(),
679 io_stat: Vec::new(),
680 }
681 }
682
683 pub fn set_leaf_weight(&self, w: u64) -> Result<()> {
686 self.open_path("blkio.leaf_weight", true)
687 .and_then(|mut file| {
688 file.write_all(w.to_string().as_ref()).map_err(|e| {
689 Error::with_cause(
690 WriteFailed("blkio.leaf_weight".to_string(), w.to_string()),
691 e,
692 )
693 })
694 })
695 }
696
697 pub fn set_leaf_weight_for_device(&self, major: u64, minor: u64, weight: u64) -> Result<()> {
699 self.open_path("blkio.leaf_weight_device", true)
700 .and_then(|mut file| {
701 file.write_all(format!("{}:{} {}", major, minor, weight).as_ref())
702 .map_err(|e| {
703 Error::with_cause(
704 WriteFailed(
705 "blkio.leaf_weight_device".to_string(),
706 format!("{}:{} {}", major, minor, weight),
707 ),
708 e,
709 )
710 })
711 })
712 }
713
714 pub fn reset_stats(&self) -> Result<()> {
716 self.open_path("blkio.reset_stats", true)
717 .and_then(|mut file| {
718 file.write_all("1".to_string().as_ref()).map_err(|e| {
719 Error::with_cause(
720 WriteFailed("blkio.reset_stats".to_string(), "1".to_string()),
721 e,
722 )
723 })
724 })
725 }
726
727 pub fn throttle_read_bps_for_device(&self, major: u64, minor: u64, bps: u64) -> Result<()> {
730 let mut file_name = "blkio.throttle.read_bps_device";
731 let mut content = format!("{}:{} {}", major, minor, bps);
732 if self.v2 {
733 file_name = "io.max";
734 content = format!("{}:{} rbps={}", major, minor, bps);
735 }
736 self.open_path(file_name, true).and_then(|mut file| {
737 file.write_all(content.as_ref()).map_err(|e| {
738 Error::with_cause(WriteFailed(file_name.to_string(), content.to_string()), e)
739 })
740 })
741 }
742
743 pub fn throttle_read_iops_for_device(&self, major: u64, minor: u64, iops: u64) -> Result<()> {
746 let mut file_name = "blkio.throttle.read_iops_device";
747 let mut content = format!("{}:{} {}", major, minor, iops);
748 if self.v2 {
749 file_name = "io.max";
750 content = format!("{}:{} riops={}", major, minor, iops);
751 }
752 self.open_path(file_name, true).and_then(|mut file| {
753 file.write_all(content.as_ref()).map_err(|e| {
754 Error::with_cause(WriteFailed(file_name.to_string(), content.to_string()), e)
755 })
756 })
757 }
758 pub fn throttle_write_bps_for_device(&self, major: u64, minor: u64, bps: u64) -> Result<()> {
761 let mut file_name = "blkio.throttle.write_bps_device";
762 let mut content = format!("{}:{} {}", major, minor, bps);
763 if self.v2 {
764 file_name = "io.max";
765 content = format!("{}:{} wbps={}", major, minor, bps);
766 }
767 self.open_path(file_name, true).and_then(|mut file| {
768 file.write_all(content.as_ref()).map_err(|e| {
769 Error::with_cause(WriteFailed(file_name.to_string(), content.to_string()), e)
770 })
771 })
772 }
773
774 pub fn throttle_write_iops_for_device(&self, major: u64, minor: u64, iops: u64) -> Result<()> {
777 let mut file_name = "blkio.throttle.write_iops_device";
778 let mut content = format!("{}:{} {}", major, minor, iops);
779 if self.v2 {
780 file_name = "io.max";
781 content = format!("{}:{} wiops={}", major, minor, iops);
782 }
783 self.open_path(file_name, true).and_then(|mut file| {
784 file.write_all(content.as_ref()).map_err(|e| {
785 Error::with_cause(WriteFailed(file_name.to_string(), content.to_string()), e)
786 })
787 })
788 }
789
790 pub fn set_weight(&self, w: u64) -> Result<()> {
792 let mut file_name = "blkio.weight";
794 if self.v2 {
795 file_name = "io.bfq.weight";
796 }
797 self.open_path(file_name, true).and_then(|mut file| {
798 file.write_all(w.to_string().as_ref()).map_err(|e| {
799 Error::with_cause(WriteFailed(file_name.to_string(), w.to_string()), e)
800 })
801 })
802 }
803
804 pub fn set_weight_for_device(&self, major: u64, minor: u64, weight: u64) -> Result<()> {
806 let mut file_name = "blkio.weight_device";
807 if self.v2 {
808 file_name = "io.bfq.weight";
812 }
813 self.open_path(file_name, true).and_then(|mut file| {
814 file.write_all(format!("{}:{} {}", major, minor, weight).as_ref())
815 .map_err(|e| {
816 Error::with_cause(
817 WriteFailed(
818 file_name.to_string(),
819 format!("{}:{} {}", major, minor, weight),
820 ),
821 e,
822 )
823 })
824 })
825 }
826}
827
828impl CustomizedAttribute for BlkIoController {}
829#[cfg(test)]
830mod test {
831 use crate::blkio::{parse_blkio_data, BlkIoData};
832 use crate::blkio::{parse_io_service, parse_io_service_total, IoService};
833 use crate::error::*;
834
835 static TEST_VALUE: &str = "\
8368:32 Read 4280320
8378:32 Write 0
8388:32 Sync 4280320
8398:32 Async 0
8408:32 Discard 1
8418:32 Total 4280320
8428:48 Read 5705479168
8438:48 Write 56096055296
8448:48 Sync 11213923328
8458:48 Async 50587611136
8468:48 Total 61801534464
8478:16 Read 10059776
8488:16 Write 0
8498:16 Sync 10059776
8508:16 Async 0
8518:16 Total 10059776
8528:0 Read 7192576
8538:0 Write 0
8548:0 Sync 7192576
8558:0 Async 0
8568:0 Total 7192576
857Total 61823067136
858 ";
859
860 static TEST_BLKIO_DATA: &str = "\
8618:48 454480833999
8628:32 228392923193
8638:16 772456885
8648:0 559583764
865 ";
866
867 #[test]
868 fn test_parse_io_service_total() {
869 let ok = parse_io_service_total(TEST_VALUE.to_string()).unwrap();
870 assert_eq!(ok, 61823067136);
871 }
872
873 #[test]
874 fn test_parse_io_service() {
875 let ok = parse_io_service(TEST_VALUE.to_string()).unwrap();
876 assert_eq!(
877 ok,
878 vec![
879 IoService {
880 major: 8,
881 minor: 32,
882 read: 4280320,
883 write: 0,
884 sync: 4280320,
885 r#async: 0,
886 discard: 1,
887 total: 4280320,
888 },
889 IoService {
890 major: 8,
891 minor: 48,
892 read: 5705479168,
893 write: 56096055296,
894 sync: 11213923328,
895 r#async: 50587611136,
896 discard: 0,
897 total: 61801534464,
898 },
899 IoService {
900 major: 8,
901 minor: 16,
902 read: 10059776,
903 write: 0,
904 sync: 10059776,
905 r#async: 0,
906 discard: 0,
907 total: 10059776,
908 },
909 IoService {
910 major: 8,
911 minor: 0,
912 read: 7192576,
913 write: 0,
914 sync: 7192576,
915 r#async: 0,
916 discard: 0,
917 total: 7192576,
918 }
919 ]
920 );
921
922 let invalid_values = vec![
923 "\
9248:32 Read 4280320
9258:32 Write a
9268:32 Async 1
927",
928 "\
9298:32 Read 4280320
930b:32 Write 1
9318:32 Async 1
932",
933 "\
9348:32 Read 4280320
9358:32 Write 1
9368:c Async 1
937",
938 ];
939
940 for value in invalid_values {
941 let err = parse_io_service(value.to_string()).unwrap_err();
942 assert_eq!(err.kind(), &ErrorKind::ParseError,);
943 }
944 }
945
946 #[test]
947 fn test_parse_blkio_data() {
948 assert_eq!(
949 parse_blkio_data(TEST_BLKIO_DATA.to_string()).unwrap(),
950 vec![
951 BlkIoData {
952 major: 8,
953 minor: 48,
954 data: 454480833999,
955 },
956 BlkIoData {
957 major: 8,
958 minor: 32,
959 data: 228392923193,
960 },
961 BlkIoData {
962 major: 8,
963 minor: 16,
964 data: 772456885,
965 },
966 BlkIoData {
967 major: 8,
968 minor: 0,
969 data: 559583764,
970 }
971 ]
972 );
973 }
974}