cgroups_rs/fs/
blkio.rs

1// Copyright (c) 2018 Levente Kurusa
2// Copyright (c) 2020 And Group
3//
4// SPDX-License-Identifier: Apache-2.0 or MIT
5//
6
7//! This module contains the implementation of the `blkio` cgroup subsystem.
8//!
9//! See the Kernel's documentation for more information about this subsystem, found at:
10//!  [Documentation/cgroup-v1/blkio-controller.txt](https://www.kernel.org/doc/Documentation/cgroup-v1/blkio-controller.txt)
11use 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/// A controller that allows controlling the `blkio` subsystem of a Cgroup.
24///
25/// In essence, using the `blkio` controller one can limit and throttle the tasks' usage of block
26/// devices in the control group.
27#[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))]
36/// Per-device information
37pub struct BlkIoData {
38    /// The major number of the device.
39    pub major: i16,
40    /// The minor number of the device.
41    pub minor: i16,
42    /// The data that is associated with the device.
43    pub data: u64,
44}
45
46#[derive(Eq, PartialEq, Debug, Default)]
47#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
48/// Per-device activity from the control group.
49pub struct IoService {
50    /// The major number of the device.
51    pub major: i16,
52    /// The minor number of the device.
53    pub minor: i16,
54    /// How many items were read from the device.
55    pub read: u64,
56    /// How many items were written to the device.
57    pub write: u64,
58    /// How many items were synchronously transferred.
59    pub sync: u64,
60    /// How many items were asynchronously transferred.
61    pub r#async: u64,
62    /// How many items were discarded.
63    pub discard: u64,
64    /// Total number of items transferred.
65    pub total: u64,
66}
67
68#[derive(Eq, PartialEq, Debug)]
69#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
70/// Per-device activity from the control group.
71/// Only for cgroup v2
72pub struct IoStat {
73    /// The major number of the device.
74    pub major: i16,
75    /// The minor number of the device.
76    pub minor: i16,
77    /// How many bytes were read from the device.
78    pub rbytes: u64,
79    /// How many bytes were written to the device.
80    pub wbytes: u64,
81    /// How many iops were read from the device.
82    pub rios: u64,
83    /// How many iops were written to the device.
84    pub wios: u64,
85    /// How many discard bytes were read from the device.
86    pub dbytes: u64,
87    /// How many discard iops were written to the device.
88    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            // new block device
123            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    // line:
160    // 8:0 rbytes=180224 wbytes=0 rios=3 wios=0 dbytes=0 dios=0
161    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/// Current state and statistics about how throttled are the block devices when accessed from the
229/// controller's control group.
230#[derive(Default, Debug)]
231#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
232pub struct BlkIoThrottle {
233    /// Statistics about the bytes transferred between the block devices by the tasks in this
234    /// control group.
235    pub io_service_bytes: Vec<IoService>,
236    /// Total amount of bytes transferred to and from the block devices.
237    pub io_service_bytes_total: u64,
238    /// Same as `io_service_bytes`, but contains all descendant control groups.
239    pub io_service_bytes_recursive: Vec<IoService>,
240    /// Total amount of bytes transferred to and from the block devices, including all descendant
241    /// control groups.
242    pub io_service_bytes_recursive_total: u64,
243    /// The number of I/O operations performed on the devices as seen by the throttling policy.
244    pub io_serviced: Vec<IoService>,
245    /// The total number of I/O operations performed on the devices as seen by the throttling
246    /// policy.
247    pub io_serviced_total: u64,
248    /// Same as `io_serviced`, but contains all descendant control groups.
249    pub io_serviced_recursive: Vec<IoService>,
250    /// Same as `io_serviced`, but contains all descendant control groups and contains only the
251    /// total amount.
252    pub io_serviced_recursive_total: u64,
253    /// The upper limit of bytes per second rate of read operation on the block devices by the
254    /// control group's tasks.
255    pub read_bps_device: Vec<BlkIoData>,
256    /// The upper limit of I/O operation per second, when said operation is a read operation.
257    pub read_iops_device: Vec<BlkIoData>,
258    /// The upper limit of bytes per second rate of write operation on the block devices by the
259    /// control group's tasks.
260    pub write_bps_device: Vec<BlkIoData>,
261    /// The upper limit of I/O operation per second, when said operation is a write operation.
262    pub write_iops_device: Vec<BlkIoData>,
263}
264
265/// Statistics and state of the block devices.
266#[derive(Default, Debug)]
267#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
268pub struct BlkIo {
269    /// The number of BIOS requests merged into I/O requests by the control group's tasks.
270    pub io_merged: Vec<IoService>,
271    /// Same as `io_merged`, but only reports the total number.
272    pub io_merged_total: u64,
273    /// Same as `io_merged`, but contains all descendant control groups.
274    pub io_merged_recursive: Vec<IoService>,
275    /// Same as `io_merged_recursive`, but only reports the total number.
276    pub io_merged_recursive_total: u64,
277    /// The number of requests queued for I/O operations by the tasks of the control group.
278    pub io_queued: Vec<IoService>,
279    /// Same as `io_queued`, but only reports the total number.
280    pub io_queued_total: u64,
281    /// Same as `io_queued`, but contains all descendant control groups.
282    pub io_queued_recursive: Vec<IoService>,
283    /// Same as `io_queued_recursive`, but contains all descendant control groups.
284    pub io_queued_recursive_total: u64,
285    /// The number of bytes transferred from and to the block device (as seen by the CFQ I/O scheduler).
286    pub io_service_bytes: Vec<IoService>,
287    /// Same as `io_service_bytes`, but contains all descendant control groups.
288    pub io_service_bytes_total: u64,
289    /// Same as `io_service_bytes`, but contains all descendant control groups.
290    pub io_service_bytes_recursive: Vec<IoService>,
291    /// Total amount of bytes transferred between the tasks and block devices, including the
292    /// descendant control groups' numbers.
293    pub io_service_bytes_recursive_total: u64,
294    /// The number of I/O operations (as seen by the CFQ I/O scheduler) between the devices and the
295    /// control group's tasks.
296    pub io_serviced: Vec<IoService>,
297    /// The total number of I/O operations performed on the devices as seen by the throttling
298    /// policy.
299    pub io_serviced_total: u64,
300    /// Same as `io_serviced`, but contains all descendant control groups.
301    pub io_serviced_recursive: Vec<IoService>,
302    /// Same as `io_serviced`, but contains all descendant control groups and contains only the
303    /// total amount.
304    pub io_serviced_recursive_total: u64,
305    /// The total time spent between dispatch and request completion for I/O requests (as seen by
306    /// the CFQ I/O scheduler) by the control group's tasks.
307    pub io_service_time: Vec<IoService>,
308    /// Same as `io_service_time`, but contains all descendant control groups and contains only the
309    /// total amount.
310    pub io_service_time_total: u64,
311    /// Same as `io_service_time`, but contains all descendant control groups.
312    pub io_service_time_recursive: Vec<IoService>,
313    /// Same as `io_service_time_recursive`, but contains all descendant control groups and only
314    /// the total amount.
315    pub io_service_time_recursive_total: u64,
316    /// Total amount of time spent waiting for a free slot in the CFQ I/O scheduler's queue.
317    pub io_wait_time: Vec<IoService>,
318    /// Same as `io_wait_time`, but only reports the total amount.
319    pub io_wait_time_total: u64,
320    /// Same as `io_wait_time`, but contains all descendant control groups.
321    pub io_wait_time_recursive: Vec<IoService>,
322    /// Same as `io_wait_time_recursive`, but only reports the total amount.
323    pub io_wait_time_recursive_total: u64,
324    /// How much weight do the control group's tasks have when competing against the descendant
325    /// control group's tasks.
326    pub leaf_weight: u64,
327    /// Same as `leaf_weight`, but per-block-device.
328    pub leaf_weight_device: Vec<BlkIoData>,
329    /// Total number of sectors transferred between the block devices and the control group's
330    /// tasks.
331    pub sectors: Vec<BlkIoData>,
332    /// Same as `sectors`, but contains all descendant control groups.
333    pub sectors_recursive: Vec<BlkIoData>,
334    /// Similar statistics, but as seen by the throttle policy.
335    pub throttle: BlkIoThrottle,
336    /// The time the control group had access to the I/O devices.
337    pub time: Vec<BlkIoData>,
338    /// Same as `time`, but contains all descendant control groups.
339    pub time_recursive: Vec<BlkIoData>,
340    /// The weight of this control group.
341    pub weight: u64,
342    /// Same as `weight`, but per-block-device.
343    pub weight_device: Vec<BlkIoData>,
344
345    /// IoStat for cgroup v2
346    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        // get the resources that apply to this controller
369        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    /// Constructs a new `BlkIoController` with `root` serving as the root of the control group.
434    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    /// Gathers statistics about and reports the state of the block devices used by the control
454    /// group's tasks.
455    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    /// Set the leaf weight on the control group's tasks, i.e., how are they weighted against the
685    /// descendant control groups' tasks.
686    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    /// Same as `set_leaf_weight()`, but settable per each block device.
699    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    /// Reset the statistics the kernel has gathered so far and start fresh.
716    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    /// Throttle the bytes per second rate of read operation affecting the block device
729    /// `major:minor` to `bps`.
730    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    /// Throttle the I/O operations per second rate of read operation affecting the block device
745    /// `major:minor` to `bps`.
746    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    /// Throttle the bytes per second rate of write operation affecting the block device
760    /// `major:minor` to `bps`.
761    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    /// Throttle the I/O operations per second rate of write operation affecting the block device
776    /// `major:minor` to `bps`.
777    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    /// Set the weight of the control group's tasks.
792    pub fn set_weight(&self, w: u64) -> Result<()> {
793        // Attation: may not find in high kernel version.
794        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    /// Same as `set_weight()`, but settable per each block device.
806    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            // Attation: there is no weight for device in runc
810            // https://github.com/opencontainers/runc/blob/46be7b612e2533c494e6a251111de46d8e286ed5/libcontainer/cgroups/fs2/io.go#L30
811            // may depends on IO schedulers https://wiki.ubuntu.com/Kernel/Reference/IOSchedulers
812            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}