1use std::io::Write;
12use std::path::PathBuf;
13
14use crate::fs::error::ErrorKind::*;
15use crate::fs::error::*;
16
17use crate::fs::{read_string_from, read_u64_from};
18use crate::fs::{
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 .find_map(|line| {
185 let mut parts = line.split_whitespace();
186 match (parts.next(), parts.next(), parts.next()) {
187 (Some("Total"), Some(val), None) => val.parse::<u64>().ok(),
188 _ => None,
189 }
190 })
191 .ok_or_else(|| Error::new(ParseError))
192}
193
194fn parse_blkio_data(s: String) -> Result<Vec<BlkIoData>> {
195 let r = s
196 .chars()
197 .map(|x| if x == ':' { ' ' } else { x })
198 .collect::<String>();
199
200 let r = r
201 .lines()
202 .flat_map(|x| x.split_whitespace())
203 .collect::<Vec<_>>();
204
205 let r = r.chunks(3).collect::<Vec<_>>();
206
207 let mut res = Vec::new();
208
209 let err = r.iter().try_for_each(|x| match x {
210 [major, minor, data] => {
211 res.push(BlkIoData {
212 major: major.parse::<i16>().unwrap(),
213 minor: minor.parse::<i16>().unwrap(),
214 data: data.parse::<u64>().unwrap(),
215 });
216 Ok(())
217 }
218 _ => Err(Error::new(ParseError)),
219 });
220
221 if err.is_err() {
222 Err(Error::new(ParseError))
223 } else {
224 Ok(res)
225 }
226}
227
228#[derive(Default, Debug)]
231#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
232pub struct BlkIoThrottle {
233 pub io_service_bytes: Vec<IoService>,
236 pub io_service_bytes_total: u64,
238 pub io_service_bytes_recursive: Vec<IoService>,
240 pub io_service_bytes_recursive_total: u64,
243 pub io_serviced: Vec<IoService>,
245 pub io_serviced_total: u64,
248 pub io_serviced_recursive: Vec<IoService>,
250 pub io_serviced_recursive_total: u64,
253 pub read_bps_device: Vec<BlkIoData>,
256 pub read_iops_device: Vec<BlkIoData>,
258 pub write_bps_device: Vec<BlkIoData>,
261 pub write_iops_device: Vec<BlkIoData>,
263}
264
265#[derive(Default, Debug)]
267#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
268pub struct BlkIo {
269 pub io_merged: Vec<IoService>,
271 pub io_merged_total: u64,
273 pub io_merged_recursive: Vec<IoService>,
275 pub io_merged_recursive_total: u64,
277 pub io_queued: Vec<IoService>,
279 pub io_queued_total: u64,
281 pub io_queued_recursive: Vec<IoService>,
283 pub io_queued_recursive_total: u64,
285 pub io_service_bytes: Vec<IoService>,
287 pub io_service_bytes_total: u64,
289 pub io_service_bytes_recursive: Vec<IoService>,
291 pub io_service_bytes_recursive_total: u64,
294 pub io_serviced: Vec<IoService>,
297 pub io_serviced_total: u64,
300 pub io_serviced_recursive: Vec<IoService>,
302 pub io_serviced_recursive_total: u64,
305 pub io_service_time: Vec<IoService>,
308 pub io_service_time_total: u64,
311 pub io_service_time_recursive: Vec<IoService>,
313 pub io_service_time_recursive_total: u64,
316 pub io_wait_time: Vec<IoService>,
318 pub io_wait_time_total: u64,
320 pub io_wait_time_recursive: Vec<IoService>,
322 pub io_wait_time_recursive_total: u64,
324 pub leaf_weight: u64,
327 pub leaf_weight_device: Vec<BlkIoData>,
329 pub sectors: Vec<BlkIoData>,
332 pub sectors_recursive: Vec<BlkIoData>,
334 pub throttle: BlkIoThrottle,
336 pub time: Vec<BlkIoData>,
338 pub time_recursive: Vec<BlkIoData>,
340 pub weight: u64,
342 pub weight_device: Vec<BlkIoData>,
344
345 pub io_stat: Vec<IoStat>,
347}
348
349impl ControllerInternal for BlkIoController {
350 fn control_type(&self) -> Controllers {
351 Controllers::BlkIo
352 }
353 fn get_path(&self) -> &PathBuf {
354 &self.path
355 }
356 fn get_path_mut(&mut self) -> &mut PathBuf {
357 &mut self.path
358 }
359 fn get_base(&self) -> &PathBuf {
360 &self.base
361 }
362
363 fn is_v2(&self) -> bool {
364 self.v2
365 }
366
367 fn apply(&self, res: &Resources) -> Result<()> {
368 let res: &BlkIoResources = &res.blkio;
370
371 if let Some(weight) = res.weight {
372 let _ = self.set_weight(weight as u64);
373 }
374 if let Some(leaf_weight) = res.leaf_weight {
375 let _ = self.set_leaf_weight(leaf_weight as u64);
376 }
377
378 for dev in &res.weight_device {
379 if let Some(weight) = dev.weight {
380 let _ = self.set_weight_for_device(dev.major, dev.minor, weight as u64);
381 }
382 if let Some(leaf_weight) = dev.leaf_weight {
383 let _ = self.set_leaf_weight_for_device(dev.major, dev.minor, leaf_weight as u64);
384 }
385 }
386
387 for dev in &res.throttle_read_bps_device {
388 let _ = self.throttle_read_bps_for_device(dev.major, dev.minor, dev.rate);
389 }
390
391 for dev in &res.throttle_write_bps_device {
392 let _ = self.throttle_write_bps_for_device(dev.major, dev.minor, dev.rate);
393 }
394
395 for dev in &res.throttle_read_iops_device {
396 let _ = self.throttle_read_iops_for_device(dev.major, dev.minor, dev.rate);
397 }
398
399 for dev in &res.throttle_write_iops_device {
400 let _ = self.throttle_write_iops_for_device(dev.major, dev.minor, dev.rate);
401 }
402
403 res.attrs.iter().for_each(|(k, v)| {
404 let _ = self.set(k, v);
405 });
406
407 Ok(())
408 }
409}
410
411impl ControllIdentifier for BlkIoController {
412 fn controller_type() -> Controllers {
413 Controllers::BlkIo
414 }
415}
416
417impl<'a> From<&'a Subsystem> for &'a BlkIoController {
418 fn from(sub: &'a Subsystem) -> &'a BlkIoController {
419 unsafe {
420 match sub {
421 Subsystem::BlkIo(c) => c,
422 _ => {
423 assert_eq!(1, 0);
424 let v = std::mem::MaybeUninit::uninit();
425 v.assume_init()
426 }
427 }
428 }
429 }
430}
431
432impl BlkIoController {
433 pub fn new(point: PathBuf, root: PathBuf, v2: bool) -> Self {
435 Self {
436 base: root,
437 path: point,
438 v2,
439 }
440 }
441
442 fn blkio_v2(&self) -> BlkIo {
443 BlkIo {
444 io_stat: self
445 .open_path("io.stat", false)
446 .and_then(read_string_from)
447 .map(parse_io_stat)
448 .unwrap_or_default(),
449 ..Default::default()
450 }
451 }
452
453 pub fn blkio(&self) -> BlkIo {
456 if self.v2 {
457 return self.blkio_v2();
458 }
459 BlkIo {
460 io_merged: self
461 .open_path("blkio.io_merged", false)
462 .and_then(read_string_from)
463 .and_then(parse_io_service)
464 .unwrap_or_default(),
465 io_merged_total: self
466 .open_path("blkio.io_merged", false)
467 .and_then(read_string_from)
468 .and_then(parse_io_service_total)
469 .unwrap_or_default(),
470 io_merged_recursive: self
471 .open_path("blkio.io_merged_recursive", false)
472 .and_then(read_string_from)
473 .and_then(parse_io_service)
474 .unwrap_or_default(),
475 io_merged_recursive_total: self
476 .open_path("blkio.io_merged_recursive", false)
477 .and_then(read_string_from)
478 .and_then(parse_io_service_total)
479 .unwrap_or_default(),
480 io_queued: self
481 .open_path("blkio.io_queued", false)
482 .and_then(read_string_from)
483 .and_then(parse_io_service)
484 .unwrap_or_default(),
485 io_queued_total: self
486 .open_path("blkio.io_queued", false)
487 .and_then(read_string_from)
488 .and_then(parse_io_service_total)
489 .unwrap_or_default(),
490 io_queued_recursive: self
491 .open_path("blkio.io_queued_recursive", false)
492 .and_then(read_string_from)
493 .and_then(parse_io_service)
494 .unwrap_or_default(),
495 io_queued_recursive_total: self
496 .open_path("blkio.io_queued_recursive", false)
497 .and_then(read_string_from)
498 .and_then(parse_io_service_total)
499 .unwrap_or_default(),
500 io_service_bytes: self
501 .open_path("blkio.io_service_bytes", false)
502 .and_then(read_string_from)
503 .and_then(parse_io_service)
504 .unwrap_or_default(),
505 io_service_bytes_total: self
506 .open_path("blkio.io_service_bytes", false)
507 .and_then(read_string_from)
508 .and_then(parse_io_service_total)
509 .unwrap_or_default(),
510 io_service_bytes_recursive: self
511 .open_path("blkio.io_service_bytes_recursive", false)
512 .and_then(read_string_from)
513 .and_then(parse_io_service)
514 .unwrap_or_default(),
515 io_service_bytes_recursive_total: self
516 .open_path("blkio.io_service_bytes_recursive", false)
517 .and_then(read_string_from)
518 .and_then(parse_io_service_total)
519 .unwrap_or_default(),
520 io_serviced: self
521 .open_path("blkio.io_serviced", false)
522 .and_then(read_string_from)
523 .and_then(parse_io_service)
524 .unwrap_or_default(),
525 io_serviced_total: self
526 .open_path("blkio.io_serviced", false)
527 .and_then(read_string_from)
528 .and_then(parse_io_service_total)
529 .unwrap_or_default(),
530 io_serviced_recursive: self
531 .open_path("blkio.io_serviced_recursive", false)
532 .and_then(read_string_from)
533 .and_then(parse_io_service)
534 .unwrap_or_default(),
535 io_serviced_recursive_total: self
536 .open_path("blkio.io_serviced_recursive", false)
537 .and_then(read_string_from)
538 .and_then(parse_io_service_total)
539 .unwrap_or_default(),
540 io_service_time: self
541 .open_path("blkio.io_service_time", false)
542 .and_then(read_string_from)
543 .and_then(parse_io_service)
544 .unwrap_or_default(),
545 io_service_time_total: self
546 .open_path("blkio.io_service_time", false)
547 .and_then(read_string_from)
548 .and_then(parse_io_service_total)
549 .unwrap_or_default(),
550 io_service_time_recursive: self
551 .open_path("blkio.io_service_time_recursive", false)
552 .and_then(read_string_from)
553 .and_then(parse_io_service)
554 .unwrap_or_default(),
555 io_service_time_recursive_total: self
556 .open_path("blkio.io_service_time_recursive", false)
557 .and_then(read_string_from)
558 .and_then(parse_io_service_total)
559 .unwrap_or_default(),
560 io_wait_time: self
561 .open_path("blkio.io_wait_time", false)
562 .and_then(read_string_from)
563 .and_then(parse_io_service)
564 .unwrap_or_default(),
565 io_wait_time_total: self
566 .open_path("blkio.io_wait_time", false)
567 .and_then(read_string_from)
568 .and_then(parse_io_service_total)
569 .unwrap_or_default(),
570 io_wait_time_recursive: self
571 .open_path("blkio.io_wait_time_recursive", false)
572 .and_then(read_string_from)
573 .and_then(parse_io_service)
574 .unwrap_or_default(),
575 io_wait_time_recursive_total: self
576 .open_path("blkio.io_wait_time_recursive", false)
577 .and_then(read_string_from)
578 .and_then(parse_io_service_total)
579 .unwrap_or_default(),
580 leaf_weight: self
581 .open_path("blkio.leaf_weight", false)
582 .and_then(read_u64_from)
583 .unwrap_or(0u64),
584 leaf_weight_device: self
585 .open_path("blkio.leaf_weight_device", false)
586 .and_then(read_string_from)
587 .and_then(parse_blkio_data)
588 .unwrap_or_default(),
589 sectors: self
590 .open_path("blkio.sectors", false)
591 .and_then(read_string_from)
592 .and_then(parse_blkio_data)
593 .unwrap_or_default(),
594 sectors_recursive: self
595 .open_path("blkio.sectors_recursive", false)
596 .and_then(read_string_from)
597 .and_then(parse_blkio_data)
598 .unwrap_or_default(),
599 throttle: BlkIoThrottle {
600 io_service_bytes: self
601 .open_path("blkio.throttle.io_service_bytes", false)
602 .and_then(read_string_from)
603 .and_then(parse_io_service)
604 .unwrap_or_default(),
605 io_service_bytes_total: self
606 .open_path("blkio.throttle.io_service_bytes", false)
607 .and_then(read_string_from)
608 .and_then(parse_io_service_total)
609 .unwrap_or_default(),
610 io_service_bytes_recursive: self
611 .open_path("blkio.throttle.io_service_bytes_recursive", false)
612 .and_then(read_string_from)
613 .and_then(parse_io_service)
614 .unwrap_or_default(),
615 io_service_bytes_recursive_total: self
616 .open_path("blkio.throttle.io_service_bytes_recursive", false)
617 .and_then(read_string_from)
618 .and_then(parse_io_service_total)
619 .unwrap_or_default(),
620 io_serviced: self
621 .open_path("blkio.throttle.io_serviced", false)
622 .and_then(read_string_from)
623 .and_then(parse_io_service)
624 .unwrap_or_default(),
625 io_serviced_total: self
626 .open_path("blkio.throttle.io_serviced", false)
627 .and_then(read_string_from)
628 .and_then(parse_io_service_total)
629 .unwrap_or_default(),
630 io_serviced_recursive: self
631 .open_path("blkio.throttle.io_serviced_recursive", false)
632 .and_then(read_string_from)
633 .and_then(parse_io_service)
634 .unwrap_or_default(),
635 io_serviced_recursive_total: self
636 .open_path("blkio.throttle.io_serviced_recursive", false)
637 .and_then(read_string_from)
638 .and_then(parse_io_service_total)
639 .unwrap_or_default(),
640 read_bps_device: self
641 .open_path("blkio.throttle.read_bps_device", false)
642 .and_then(read_string_from)
643 .and_then(parse_blkio_data)
644 .unwrap_or_default(),
645 read_iops_device: self
646 .open_path("blkio.throttle.read_iops_device", false)
647 .and_then(read_string_from)
648 .and_then(parse_blkio_data)
649 .unwrap_or_default(),
650 write_bps_device: self
651 .open_path("blkio.throttle.write_bps_device", false)
652 .and_then(read_string_from)
653 .and_then(parse_blkio_data)
654 .unwrap_or_default(),
655 write_iops_device: self
656 .open_path("blkio.throttle.write_iops_device", false)
657 .and_then(read_string_from)
658 .and_then(parse_blkio_data)
659 .unwrap_or_default(),
660 },
661 time: self
662 .open_path("blkio.time", false)
663 .and_then(read_string_from)
664 .and_then(parse_blkio_data)
665 .unwrap_or_default(),
666 time_recursive: self
667 .open_path("blkio.time_recursive", false)
668 .and_then(read_string_from)
669 .and_then(parse_blkio_data)
670 .unwrap_or_default(),
671 weight: self
672 .open_path("blkio.weight", false)
673 .and_then(read_u64_from)
674 .unwrap_or(0u64),
675 weight_device: self
676 .open_path("blkio.weight_device", false)
677 .and_then(read_string_from)
678 .and_then(parse_blkio_data)
679 .unwrap_or_default(),
680 io_stat: Vec::new(),
681 }
682 }
683
684 pub fn set_leaf_weight(&self, w: u64) -> Result<()> {
687 self.open_path("blkio.leaf_weight", true)
688 .and_then(|mut file| {
689 file.write_all(w.to_string().as_ref()).map_err(|e| {
690 Error::with_cause(
691 WriteFailed("blkio.leaf_weight".to_string(), w.to_string()),
692 e,
693 )
694 })
695 })
696 }
697
698 pub fn set_leaf_weight_for_device(&self, major: u64, minor: u64, weight: u64) -> Result<()> {
700 self.open_path("blkio.leaf_weight_device", true)
701 .and_then(|mut file| {
702 file.write_all(format!("{}:{} {}", major, minor, weight).as_ref())
703 .map_err(|e| {
704 Error::with_cause(
705 WriteFailed(
706 "blkio.leaf_weight_device".to_string(),
707 format!("{}:{} {}", major, minor, weight),
708 ),
709 e,
710 )
711 })
712 })
713 }
714
715 pub fn reset_stats(&self) -> Result<()> {
717 self.open_path("blkio.reset_stats", true)
718 .and_then(|mut file| {
719 file.write_all("1".to_string().as_ref()).map_err(|e| {
720 Error::with_cause(
721 WriteFailed("blkio.reset_stats".to_string(), "1".to_string()),
722 e,
723 )
724 })
725 })
726 }
727
728 pub fn throttle_read_bps_for_device(&self, major: u64, minor: u64, bps: u64) -> Result<()> {
731 let mut file_name = "blkio.throttle.read_bps_device";
732 let mut content = format!("{}:{} {}", major, minor, bps);
733 if self.v2 {
734 file_name = "io.max";
735 content = format!("{}:{} rbps={}", major, minor, bps);
736 }
737 self.open_path(file_name, true).and_then(|mut file| {
738 file.write_all(content.as_ref()).map_err(|e| {
739 Error::with_cause(WriteFailed(file_name.to_string(), content.to_string()), e)
740 })
741 })
742 }
743
744 pub fn throttle_read_iops_for_device(&self, major: u64, minor: u64, iops: u64) -> Result<()> {
747 let mut file_name = "blkio.throttle.read_iops_device";
748 let mut content = format!("{}:{} {}", major, minor, iops);
749 if self.v2 {
750 file_name = "io.max";
751 content = format!("{}:{} riops={}", major, minor, iops);
752 }
753 self.open_path(file_name, true).and_then(|mut file| {
754 file.write_all(content.as_ref()).map_err(|e| {
755 Error::with_cause(WriteFailed(file_name.to_string(), content.to_string()), e)
756 })
757 })
758 }
759 pub fn throttle_write_bps_for_device(&self, major: u64, minor: u64, bps: u64) -> Result<()> {
762 let mut file_name = "blkio.throttle.write_bps_device";
763 let mut content = format!("{}:{} {}", major, minor, bps);
764 if self.v2 {
765 file_name = "io.max";
766 content = format!("{}:{} wbps={}", major, minor, bps);
767 }
768 self.open_path(file_name, true).and_then(|mut file| {
769 file.write_all(content.as_ref()).map_err(|e| {
770 Error::with_cause(WriteFailed(file_name.to_string(), content.to_string()), e)
771 })
772 })
773 }
774
775 pub fn throttle_write_iops_for_device(&self, major: u64, minor: u64, iops: u64) -> Result<()> {
778 let mut file_name = "blkio.throttle.write_iops_device";
779 let mut content = format!("{}:{} {}", major, minor, iops);
780 if self.v2 {
781 file_name = "io.max";
782 content = format!("{}:{} wiops={}", major, minor, iops);
783 }
784 self.open_path(file_name, true).and_then(|mut file| {
785 file.write_all(content.as_ref()).map_err(|e| {
786 Error::with_cause(WriteFailed(file_name.to_string(), content.to_string()), e)
787 })
788 })
789 }
790
791 pub fn set_weight(&self, w: u64) -> Result<()> {
793 let mut file_name = "blkio.weight";
795 if self.v2 {
796 file_name = "io.bfq.weight";
797 }
798 self.open_path(file_name, true).and_then(|mut file| {
799 file.write_all(w.to_string().as_ref()).map_err(|e| {
800 Error::with_cause(WriteFailed(file_name.to_string(), w.to_string()), e)
801 })
802 })
803 }
804
805 pub fn set_weight_for_device(&self, major: u64, minor: u64, weight: u64) -> Result<()> {
807 let mut file_name = "blkio.weight_device";
808 if self.v2 {
809 file_name = "io.bfq.weight";
813 }
814 self.open_path(file_name, true).and_then(|mut file| {
815 file.write_all(format!("{}:{} {}", major, minor, weight).as_ref())
816 .map_err(|e| {
817 Error::with_cause(
818 WriteFailed(
819 file_name.to_string(),
820 format!("{}:{} {}", major, minor, weight),
821 ),
822 e,
823 )
824 })
825 })
826 }
827}
828
829impl CustomizedAttribute for BlkIoController {}
830#[cfg(test)]
831mod test {
832 use crate::fs::blkio::{parse_blkio_data, BlkIoData};
833 use crate::fs::blkio::{parse_io_service, parse_io_service_total, IoService};
834 use crate::fs::error::*;
835
836 static TEST_VALUE: &str = "\
8378:32 Read 4280320
8388:32 Write 0
8398:32 Sync 4280320
8408:32 Async 0
8418:32 Discard 1
8428:32 Total 4280320
8438:48 Read 5705479168
8448:48 Write 56096055296
8458:48 Sync 11213923328
8468:48 Async 50587611136
8478:48 Total 61801534464
8488:16 Read 10059776
8498:16 Write 0
8508:16 Sync 10059776
8518:16 Async 0
8528:16 Total 10059776
8538:0 Read 7192576
8548:0 Write 0
8558:0 Sync 7192576
8568:0 Async 0
8578:0 Total 7192576
858Total 61823067136
859 ";
860
861 static TEST_BLKIO_DATA: &str = "\
8628:48 454480833999
8638:32 228392923193
8648:16 772456885
8658:0 559583764
866 ";
867
868 #[test]
869 fn test_parse_io_service_total() {
870 let ok = parse_io_service_total(TEST_VALUE.to_string()).unwrap();
871 assert_eq!(ok, 61823067136);
872 }
873
874 #[test]
875 fn test_parse_io_service() {
876 let ok = parse_io_service(TEST_VALUE.to_string()).unwrap();
877 assert_eq!(
878 ok,
879 vec![
880 IoService {
881 major: 8,
882 minor: 32,
883 read: 4280320,
884 write: 0,
885 sync: 4280320,
886 r#async: 0,
887 discard: 1,
888 total: 4280320,
889 },
890 IoService {
891 major: 8,
892 minor: 48,
893 read: 5705479168,
894 write: 56096055296,
895 sync: 11213923328,
896 r#async: 50587611136,
897 discard: 0,
898 total: 61801534464,
899 },
900 IoService {
901 major: 8,
902 minor: 16,
903 read: 10059776,
904 write: 0,
905 sync: 10059776,
906 r#async: 0,
907 discard: 0,
908 total: 10059776,
909 },
910 IoService {
911 major: 8,
912 minor: 0,
913 read: 7192576,
914 write: 0,
915 sync: 7192576,
916 r#async: 0,
917 discard: 0,
918 total: 7192576,
919 }
920 ]
921 );
922
923 let invalid_values = vec![
924 "\
9258:32 Read 4280320
9268:32 Write a
9278:32 Async 1
928",
929 "\
9308:32 Read 4280320
931b:32 Write 1
9328:32 Async 1
933",
934 "\
9358:32 Read 4280320
9368:32 Write 1
9378:c Async 1
938",
939 ];
940
941 for value in invalid_values {
942 let err = parse_io_service(value.to_string()).unwrap_err();
943 assert_eq!(err.kind(), &ErrorKind::ParseError,);
944 }
945 }
946
947 #[test]
948 fn test_parse_blkio_data() {
949 assert_eq!(
950 parse_blkio_data(TEST_BLKIO_DATA.to_string()).unwrap(),
951 vec![
952 BlkIoData {
953 major: 8,
954 minor: 48,
955 data: 454480833999,
956 },
957 BlkIoData {
958 major: 8,
959 minor: 32,
960 data: 228392923193,
961 },
962 BlkIoData {
963 major: 8,
964 minor: 16,
965 data: 772456885,
966 },
967 BlkIoData {
968 major: 8,
969 minor: 0,
970 data: 559583764,
971 }
972 ]
973 );
974 }
975}