mirror of
https://github.com/kata-containers/cgroups-rs.git
synced 2026-08-05 02:13:23 +00:00
Run rustfmt
This commit is contained in:
committed by
Levente Kurusa
parent
5660656e3a
commit
2149e1c0c4
491
src/blkio.rs
491
src/blkio.rs
@@ -1,13 +1,15 @@
|
||||
//! This module contains the implementation of the `blkio` cgroup subsystem.
|
||||
//!
|
||||
//!
|
||||
//! See the Kernel's documentation for more information about this subsystem, found at:
|
||||
//! [Documentation/cgroup-v1/blkio-controller.txt](https://www.kernel.org/doc/Documentation/cgroup-v1/blkio-controller.txt)
|
||||
use std::path::PathBuf;
|
||||
use std::io::{Read, Write};
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use {CgroupError, BlkIoResources, Controllers, Controller, Resources, ControllIdentifier, Subsystem};
|
||||
use CgroupError::*;
|
||||
use {
|
||||
BlkIoResources, CgroupError, ControllIdentifier, Controller, Controllers, Resources, Subsystem,
|
||||
};
|
||||
|
||||
/// A controller that allows controlling the `blkio` subsystem of a Cgroup.
|
||||
///
|
||||
@@ -102,35 +104,31 @@ fn parse_io_service_total(s: String) -> Result<u64, CgroupError> {
|
||||
}
|
||||
|
||||
fn parse_blkio_data(s: String) -> Result<Vec<BlkIoData>, CgroupError> {
|
||||
let r = s.chars()
|
||||
let r = s
|
||||
.chars()
|
||||
.map(|x| if x == ':' { ' ' } else { x })
|
||||
.collect::<String>();
|
||||
|
||||
let r = r.lines()
|
||||
let r = r
|
||||
.lines()
|
||||
.flat_map(|x| x.split_whitespace())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let r = r.chunks(3)
|
||||
.collect::<Vec<_>>();
|
||||
let r = r.chunks(3).collect::<Vec<_>>();
|
||||
|
||||
let mut res = Vec::new();
|
||||
|
||||
let err = r.iter()
|
||||
.try_for_each(|x|
|
||||
match x {
|
||||
[major, minor, data] => {
|
||||
res.push(BlkIoData {
|
||||
major: major.parse::<i16>().unwrap(),
|
||||
minor: minor.parse::<i16>().unwrap(),
|
||||
data: data.parse::<u64>().unwrap(),
|
||||
});
|
||||
Ok(())
|
||||
},
|
||||
_ => {
|
||||
Err(CgroupError::ParseError)
|
||||
}
|
||||
}
|
||||
);
|
||||
let err = r.iter().try_for_each(|x| match x {
|
||||
[major, minor, data] => {
|
||||
res.push(BlkIoData {
|
||||
major: major.parse::<i16>().unwrap(),
|
||||
minor: minor.parse::<i16>().unwrap(),
|
||||
data: data.parse::<u64>().unwrap(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(CgroupError::ParseError),
|
||||
});
|
||||
|
||||
if err.is_err() {
|
||||
return Err(CgroupError::ParseError);
|
||||
@@ -256,10 +254,18 @@ pub struct BlkIo {
|
||||
}
|
||||
|
||||
impl Controller for BlkIoController {
|
||||
fn control_type(&self) -> Controllers { Controllers::BlkIo }
|
||||
fn get_path(&self) -> &PathBuf { &self.path }
|
||||
fn get_path_mut(&mut self) -> &mut PathBuf { &mut self.path }
|
||||
fn get_base(&self) -> &PathBuf { &self.base }
|
||||
fn control_type(&self) -> Controllers {
|
||||
Controllers::BlkIo
|
||||
}
|
||||
fn get_path(&self) -> &PathBuf {
|
||||
&self.path
|
||||
}
|
||||
fn get_path_mut(&mut self) -> &mut PathBuf {
|
||||
&mut self.path
|
||||
}
|
||||
fn get_base(&self) -> &PathBuf {
|
||||
&self.base
|
||||
}
|
||||
|
||||
fn apply(&self, res: &Resources) -> Result<(), CgroupError> {
|
||||
/* get the resources that apply to this controller */
|
||||
@@ -308,7 +314,7 @@ impl<'a> From<&'a Subsystem> for &'a BlkIoController {
|
||||
_ => {
|
||||
assert_eq!(1, 0);
|
||||
::std::mem::uninitialized()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -345,172 +351,216 @@ impl BlkIoController {
|
||||
/// group's tasks.
|
||||
pub fn blkio(&self) -> BlkIo {
|
||||
BlkIo {
|
||||
io_merged: self.open_path("blkio.io_merged", false)
|
||||
io_merged: self
|
||||
.open_path("blkio.io_merged", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_io_service)
|
||||
.unwrap_or(Vec::new()),
|
||||
io_merged_total: self.open_path("blkio.io_merged", false)
|
||||
io_merged_total: self
|
||||
.open_path("blkio.io_merged", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_io_service_total)
|
||||
.unwrap_or(0),
|
||||
io_merged_recursive: self.open_path("blkio.io_merged_recursive", false)
|
||||
io_merged_recursive: self
|
||||
.open_path("blkio.io_merged_recursive", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_io_service)
|
||||
.unwrap_or(Vec::new()),
|
||||
io_merged_recursive_total: self.open_path("blkio.io_merged_recursive", false)
|
||||
io_merged_recursive_total: self
|
||||
.open_path("blkio.io_merged_recursive", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_io_service_total)
|
||||
.unwrap_or(0),
|
||||
io_queued: self.open_path("blkio.io_queued", false)
|
||||
io_queued: self
|
||||
.open_path("blkio.io_queued", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_io_service)
|
||||
.unwrap_or(Vec::new()),
|
||||
io_queued_total: self.open_path("blkio.io_queued", false)
|
||||
io_queued_total: self
|
||||
.open_path("blkio.io_queued", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_io_service_total)
|
||||
.unwrap_or(0),
|
||||
io_queued_recursive: self.open_path("blkio.io_queued_recursive", false)
|
||||
io_queued_recursive: self
|
||||
.open_path("blkio.io_queued_recursive", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_io_service)
|
||||
.unwrap_or(Vec::new()),
|
||||
io_queued_recursive_total: self.open_path("blkio.io_queued_recursive", false)
|
||||
io_queued_recursive_total: self
|
||||
.open_path("blkio.io_queued_recursive", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_io_service_total)
|
||||
.unwrap_or(0),
|
||||
io_service_bytes: self.open_path("blkio.io_service_bytes", false)
|
||||
io_service_bytes: self
|
||||
.open_path("blkio.io_service_bytes", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_io_service)
|
||||
.unwrap_or(Vec::new()),
|
||||
io_service_bytes_total: self.open_path("blkio.io_service_bytes", false)
|
||||
io_service_bytes_total: self
|
||||
.open_path("blkio.io_service_bytes", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_io_service_total)
|
||||
.unwrap_or(0),
|
||||
io_service_bytes_recursive: self.open_path("blkio.io_service_bytes_recursive", false)
|
||||
io_service_bytes_recursive: self
|
||||
.open_path("blkio.io_service_bytes_recursive", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_io_service)
|
||||
.unwrap_or(Vec::new()),
|
||||
io_service_bytes_recursive_total: self.open_path("blkio.io_service_bytes_recursive", false)
|
||||
io_service_bytes_recursive_total: self
|
||||
.open_path("blkio.io_service_bytes_recursive", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_io_service_total)
|
||||
.unwrap_or(0),
|
||||
io_serviced: self.open_path("blkio.io_serviced", false)
|
||||
io_serviced: self
|
||||
.open_path("blkio.io_serviced", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_io_service)
|
||||
.unwrap_or(Vec::new()),
|
||||
io_serviced_total: self.open_path("blkio.io_serviced", false)
|
||||
io_serviced_total: self
|
||||
.open_path("blkio.io_serviced", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_io_service_total)
|
||||
.unwrap_or(0),
|
||||
io_serviced_recursive: self.open_path("blkio.io_serviced_recursive", false)
|
||||
io_serviced_recursive: self
|
||||
.open_path("blkio.io_serviced_recursive", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_io_service)
|
||||
.unwrap_or(Vec::new()),
|
||||
io_serviced_recursive_total: self.open_path("blkio.io_serviced_recursive", false)
|
||||
io_serviced_recursive_total: self
|
||||
.open_path("blkio.io_serviced_recursive", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_io_service_total)
|
||||
.unwrap_or(0),
|
||||
io_service_time: self.open_path("blkio.io_service_time", false)
|
||||
io_service_time: self
|
||||
.open_path("blkio.io_service_time", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_io_service)
|
||||
.unwrap_or(Vec::new()),
|
||||
io_service_time_total: self.open_path("blkio.io_service_time", false)
|
||||
io_service_time_total: self
|
||||
.open_path("blkio.io_service_time", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_io_service_total)
|
||||
.unwrap_or(0),
|
||||
io_service_time_recursive: self.open_path("blkio.io_service_time_recursive", false)
|
||||
io_service_time_recursive: self
|
||||
.open_path("blkio.io_service_time_recursive", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_io_service)
|
||||
.unwrap_or(Vec::new()),
|
||||
io_service_time_recursive_total: self.open_path("blkio.io_service_time_recursive", false)
|
||||
io_service_time_recursive_total: self
|
||||
.open_path("blkio.io_service_time_recursive", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_io_service_total)
|
||||
.unwrap_or(0),
|
||||
io_wait_time: self.open_path("blkio.io_wait_time", false)
|
||||
io_wait_time: self
|
||||
.open_path("blkio.io_wait_time", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_io_service)
|
||||
.unwrap_or(Vec::new()),
|
||||
io_wait_time_total: self.open_path("blkio.io_wait_time", false)
|
||||
io_wait_time_total: self
|
||||
.open_path("blkio.io_wait_time", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_io_service_total)
|
||||
.unwrap_or(0),
|
||||
io_wait_time_recursive: self.open_path("blkio.io_wait_time_recursive", false)
|
||||
io_wait_time_recursive: self
|
||||
.open_path("blkio.io_wait_time_recursive", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_io_service)
|
||||
.unwrap_or(Vec::new()),
|
||||
io_wait_time_recursive_total: self.open_path("blkio.io_wait_time_recursive", false)
|
||||
io_wait_time_recursive_total: self
|
||||
.open_path("blkio.io_wait_time_recursive", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_io_service_total)
|
||||
.unwrap_or(0),
|
||||
leaf_weight: self.open_path("blkio.leaf_weight", false).and_then(|file| {
|
||||
read_u64_from(file)
|
||||
}).unwrap_or(0u64),
|
||||
leaf_weight_device: self.open_path("blkio.leaf_weight_device", false).and_then(|file| {
|
||||
read_string_from(file)
|
||||
}).unwrap_or("".to_string()),
|
||||
sectors: self.open_path("blkio.sectors", false).and_then(|file| {
|
||||
read_string_from(file)
|
||||
}).unwrap_or("".to_string()),
|
||||
sectors_recursive: self.open_path("blkio.sectors_recursive", false).and_then(|file| {
|
||||
read_string_from(file)
|
||||
}).unwrap_or("".to_string()),
|
||||
leaf_weight: self
|
||||
.open_path("blkio.leaf_weight", false)
|
||||
.and_then(|file| read_u64_from(file))
|
||||
.unwrap_or(0u64),
|
||||
leaf_weight_device: self
|
||||
.open_path("blkio.leaf_weight_device", false)
|
||||
.and_then(|file| read_string_from(file))
|
||||
.unwrap_or("".to_string()),
|
||||
sectors: self
|
||||
.open_path("blkio.sectors", false)
|
||||
.and_then(|file| read_string_from(file))
|
||||
.unwrap_or("".to_string()),
|
||||
sectors_recursive: self
|
||||
.open_path("blkio.sectors_recursive", false)
|
||||
.and_then(|file| read_string_from(file))
|
||||
.unwrap_or("".to_string()),
|
||||
throttle: BlkIoThrottle {
|
||||
io_service_bytes: self.open_path("blkio.throttle.io_service_bytes", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_io_service)
|
||||
.unwrap_or(Vec::new()),
|
||||
io_service_bytes_total: self.open_path("blkio.throttle.io_service_bytes", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_io_service_total)
|
||||
.unwrap_or(0),
|
||||
io_service_bytes_recursive: self.open_path("blkio.throttle.io_service_bytes_recursive", false)
|
||||
io_service_bytes: self
|
||||
.open_path("blkio.throttle.io_service_bytes", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_io_service)
|
||||
.unwrap_or(Vec::new()),
|
||||
io_service_bytes_recursive_total: self.open_path("blkio.throttle.io_service_bytes_recursive", false)
|
||||
io_service_bytes_total: self
|
||||
.open_path("blkio.throttle.io_service_bytes", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_io_service_total)
|
||||
.unwrap_or(0),
|
||||
io_serviced: self.open_path("blkio.throttle.io_serviced", false)
|
||||
io_service_bytes_recursive: self
|
||||
.open_path("blkio.throttle.io_service_bytes_recursive", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_io_service)
|
||||
.unwrap_or(Vec::new()),
|
||||
io_serviced_total: self.open_path("blkio.throttle.io_serviced", false)
|
||||
io_service_bytes_recursive_total: self
|
||||
.open_path("blkio.throttle.io_service_bytes_recursive", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_io_service_total)
|
||||
.unwrap_or(0),
|
||||
io_serviced_recursive: self.open_path("blkio.throttle.io_serviced_recursive", false)
|
||||
io_serviced: self
|
||||
.open_path("blkio.throttle.io_serviced", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_io_service)
|
||||
.unwrap_or(Vec::new()),
|
||||
io_serviced_recursive_total: self.open_path("blkio.throttle.io_serviced_recursive", false)
|
||||
io_serviced_total: self
|
||||
.open_path("blkio.throttle.io_serviced", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_io_service_total)
|
||||
.unwrap_or(0),
|
||||
read_bps_device: self.open_path("blkio.throttle.read_bps_device", false).and_then(|file| {
|
||||
read_string_from(file)
|
||||
}).unwrap_or("".to_string()),
|
||||
read_iops_device: self.open_path("blkio.throttle.read_iops_device", false).and_then(|file| {
|
||||
read_string_from(file)
|
||||
}).unwrap_or("".to_string()),
|
||||
write_bps_device: self.open_path("blkio.throttle.write_bps_device", false).and_then(|file| {
|
||||
read_string_from(file)
|
||||
}).unwrap_or("".to_string()),
|
||||
write_iops_device: self.open_path("blkio.throttle.write_iops_device", false).and_then(|file| {
|
||||
read_string_from(file)
|
||||
}).unwrap_or("".to_string()),
|
||||
io_serviced_recursive: self
|
||||
.open_path("blkio.throttle.io_serviced_recursive", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_io_service)
|
||||
.unwrap_or(Vec::new()),
|
||||
io_serviced_recursive_total: self
|
||||
.open_path("blkio.throttle.io_serviced_recursive", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_io_service_total)
|
||||
.unwrap_or(0),
|
||||
read_bps_device: self
|
||||
.open_path("blkio.throttle.read_bps_device", false)
|
||||
.and_then(|file| read_string_from(file))
|
||||
.unwrap_or("".to_string()),
|
||||
read_iops_device: self
|
||||
.open_path("blkio.throttle.read_iops_device", false)
|
||||
.and_then(|file| read_string_from(file))
|
||||
.unwrap_or("".to_string()),
|
||||
write_bps_device: self
|
||||
.open_path("blkio.throttle.write_bps_device", false)
|
||||
.and_then(|file| read_string_from(file))
|
||||
.unwrap_or("".to_string()),
|
||||
write_iops_device: self
|
||||
.open_path("blkio.throttle.write_iops_device", false)
|
||||
.and_then(|file| read_string_from(file))
|
||||
.unwrap_or("".to_string()),
|
||||
},
|
||||
time: self.open_path("blkio.time", false)
|
||||
time: self
|
||||
.open_path("blkio.time", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_blkio_data)
|
||||
.unwrap_or(Vec::new()),
|
||||
time_recursive: self.open_path("blkio.time_recursive", false)
|
||||
time_recursive: self
|
||||
.open_path("blkio.time_recursive", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_blkio_data)
|
||||
.unwrap_or(Vec::new()),
|
||||
weight: self.open_path("blkio.weight", false).and_then(|file| {
|
||||
read_u64_from(file)
|
||||
}).unwrap_or(0u64),
|
||||
weight_device: self.open_path("blkio.weight_device", false)
|
||||
weight: self
|
||||
.open_path("blkio.weight", false)
|
||||
.and_then(|file| read_u64_from(file))
|
||||
.unwrap_or(0u64),
|
||||
weight_device: self
|
||||
.open_path("blkio.weight_device", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_blkio_data)
|
||||
.unwrap_or(Vec::new()),
|
||||
@@ -520,77 +570,116 @@ impl BlkIoController {
|
||||
/// Set the leaf weight on the control group's tasks, i.e., how are they weighted against the
|
||||
/// descendant control groups' tasks.
|
||||
pub fn set_leaf_weight(&self, w: u64) -> Result<(), CgroupError> {
|
||||
self.open_path("blkio.leaf_weight", true).and_then(|mut file| {
|
||||
file.write_all(w.to_string().as_ref()).map_err(CgroupError::WriteError)
|
||||
})
|
||||
self.open_path("blkio.leaf_weight", true)
|
||||
.and_then(|mut file| {
|
||||
file.write_all(w.to_string().as_ref())
|
||||
.map_err(CgroupError::WriteError)
|
||||
})
|
||||
}
|
||||
|
||||
/// Same as `set_leaf_weight()`, but settable per each block device.
|
||||
pub fn set_leaf_weight_for_device(&self, d: String) -> Result<(), CgroupError> {
|
||||
self.open_path("blkio.leaf_weight_device", true).and_then(|mut file| {
|
||||
file.write_all(d.as_ref()).map_err(CgroupError::WriteError)
|
||||
})
|
||||
self.open_path("blkio.leaf_weight_device", true)
|
||||
.and_then(|mut file| file.write_all(d.as_ref()).map_err(CgroupError::WriteError))
|
||||
}
|
||||
|
||||
/// Reset the statistics the kernel has gathered so far and start fresh.
|
||||
pub fn reset_stats(&self) -> Result<(), CgroupError> {
|
||||
self.open_path("blkio.leaf_weight_device", true).and_then(|mut file| {
|
||||
file.write_all("1".to_string().as_ref()).map_err(CgroupError::WriteError)
|
||||
})
|
||||
self.open_path("blkio.leaf_weight_device", true)
|
||||
.and_then(|mut file| {
|
||||
file.write_all("1".to_string().as_ref())
|
||||
.map_err(CgroupError::WriteError)
|
||||
})
|
||||
}
|
||||
|
||||
/// Throttle the bytes per second rate of read operation affecting the block device
|
||||
/// `major:minor` to `bps`.
|
||||
pub fn throttle_read_bps_for_device(&self, major: u64, minor: u64, bps: u64) -> Result<(), CgroupError> {
|
||||
self.open_path("blkio.throttle.read_bps_device", true).and_then(|mut file| {
|
||||
file.write_all(format!("{}:{} {}", major, minor, bps).to_string().as_ref()).map_err(CgroupError::WriteError)
|
||||
})
|
||||
pub fn throttle_read_bps_for_device(
|
||||
&self,
|
||||
major: u64,
|
||||
minor: u64,
|
||||
bps: u64,
|
||||
) -> Result<(), CgroupError> {
|
||||
self.open_path("blkio.throttle.read_bps_device", true)
|
||||
.and_then(|mut file| {
|
||||
file.write_all(format!("{}:{} {}", major, minor, bps).to_string().as_ref())
|
||||
.map_err(CgroupError::WriteError)
|
||||
})
|
||||
}
|
||||
|
||||
/// Throttle the I/O operations per second rate of read operation affecting the block device
|
||||
/// `major:minor` to `bps`.
|
||||
pub fn throttle_read_iops_for_device(&self, major: u64, minor: u64, iops: u64) -> Result<(), CgroupError> {
|
||||
self.open_path("blkio.throttle.read_iops_device", true).and_then(|mut file| {
|
||||
file.write_all(format!("{}:{} {}", major, minor, iops).to_string().as_ref()).map_err(CgroupError::WriteError)
|
||||
})
|
||||
pub fn throttle_read_iops_for_device(
|
||||
&self,
|
||||
major: u64,
|
||||
minor: u64,
|
||||
iops: u64,
|
||||
) -> Result<(), CgroupError> {
|
||||
self.open_path("blkio.throttle.read_iops_device", true)
|
||||
.and_then(|mut file| {
|
||||
file.write_all(format!("{}:{} {}", major, minor, iops).to_string().as_ref())
|
||||
.map_err(CgroupError::WriteError)
|
||||
})
|
||||
}
|
||||
/// Throttle the bytes per second rate of write operation affecting the block device
|
||||
/// `major:minor` to `bps`.
|
||||
pub fn throttle_write_bps_for_device(&self, major: u64, minor: u64, bps: u64) -> Result<(), CgroupError> {
|
||||
self.open_path("blkio.throttle.write_bps_device", true).and_then(|mut file| {
|
||||
file.write_all(format!("{}:{} {}", major, minor, bps).to_string().as_ref()).map_err(CgroupError::WriteError)
|
||||
})
|
||||
pub fn throttle_write_bps_for_device(
|
||||
&self,
|
||||
major: u64,
|
||||
minor: u64,
|
||||
bps: u64,
|
||||
) -> Result<(), CgroupError> {
|
||||
self.open_path("blkio.throttle.write_bps_device", true)
|
||||
.and_then(|mut file| {
|
||||
file.write_all(format!("{}:{} {}", major, minor, bps).to_string().as_ref())
|
||||
.map_err(CgroupError::WriteError)
|
||||
})
|
||||
}
|
||||
|
||||
/// Throttle the I/O operations per second rate of write operation affecting the block device
|
||||
/// `major:minor` to `bps`.
|
||||
pub fn throttle_write_iops_for_device(&self, major: u64, minor: u64, iops: u64) -> Result<(), CgroupError> {
|
||||
self.open_path("blkio.throttle.write_iops_device", true).and_then(|mut file| {
|
||||
file.write_all(format!("{}:{} {}", major, minor, iops).to_string().as_ref()).map_err(CgroupError::WriteError)
|
||||
})
|
||||
pub fn throttle_write_iops_for_device(
|
||||
&self,
|
||||
major: u64,
|
||||
minor: u64,
|
||||
iops: u64,
|
||||
) -> Result<(), CgroupError> {
|
||||
self.open_path("blkio.throttle.write_iops_device", true)
|
||||
.and_then(|mut file| {
|
||||
file.write_all(format!("{}:{} {}", major, minor, iops).to_string().as_ref())
|
||||
.map_err(CgroupError::WriteError)
|
||||
})
|
||||
}
|
||||
|
||||
/// Set the weight of the control group's tasks.
|
||||
pub fn set_weight(&self, w: u64) -> Result<(), CgroupError> {
|
||||
self.open_path("blkio.leaf_weight", true).and_then(|mut file| {
|
||||
file.write_all(w.to_string().as_ref()).map_err(CgroupError::WriteError)
|
||||
})
|
||||
self.open_path("blkio.leaf_weight", true)
|
||||
.and_then(|mut file| {
|
||||
file.write_all(w.to_string().as_ref())
|
||||
.map_err(CgroupError::WriteError)
|
||||
})
|
||||
}
|
||||
|
||||
/// Same as `set_weight()`, but settable per each block device.
|
||||
pub fn set_weight_for_device(&self, major: u64, minor: u64, weight: u64) -> Result<(), CgroupError> {
|
||||
self.open_path("blkio.weight_device", true).and_then(|mut file| {
|
||||
file.write_all(format!("{}:{} {}", major, minor, weight).as_ref())
|
||||
.map_err(CgroupError::WriteError)
|
||||
})
|
||||
pub fn set_weight_for_device(
|
||||
&self,
|
||||
major: u64,
|
||||
minor: u64,
|
||||
weight: u64,
|
||||
) -> Result<(), CgroupError> {
|
||||
self.open_path("blkio.weight_device", true)
|
||||
.and_then(|mut file| {
|
||||
file.write_all(format!("{}:{} {}", major, minor, weight).as_ref())
|
||||
.map_err(CgroupError::WriteError)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use blkio::{IoService, parse_io_service, parse_io_service_total};
|
||||
use blkio::{BlkIoData, parse_blkio_data};
|
||||
use ::CgroupError;
|
||||
use blkio::{parse_blkio_data, BlkIoData};
|
||||
use blkio::{parse_io_service, parse_io_service_total, IoService};
|
||||
use CgroupError;
|
||||
|
||||
const test_value: &str = "\
|
||||
8:32 Read 4280320
|
||||
@@ -645,75 +734,87 @@ Total 61823067136
|
||||
|
||||
#[test]
|
||||
fn test_parse_io_service_total() {
|
||||
assert_eq!(parse_io_service_total(test_value.to_string()), Ok(61823067136));
|
||||
assert_eq!(
|
||||
parse_io_service_total(test_value.to_string()),
|
||||
Ok(61823067136)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_io_service() {
|
||||
assert_eq!(parse_io_service(test_value.to_string()), Ok(vec![
|
||||
IoService {
|
||||
major: 8,
|
||||
minor: 32,
|
||||
read: 4280320,
|
||||
write: 0,
|
||||
sync: 4280320,
|
||||
async: 0,
|
||||
total: 4280320,
|
||||
},
|
||||
IoService {
|
||||
major: 8,
|
||||
minor: 48,
|
||||
read: 5705479168,
|
||||
write: 56096055296,
|
||||
sync: 11213923328,
|
||||
async: 50587611136,
|
||||
total: 61801534464,
|
||||
},
|
||||
IoService {
|
||||
major: 8,
|
||||
minor: 16,
|
||||
read: 10059776,
|
||||
write: 0,
|
||||
sync: 10059776,
|
||||
async: 0,
|
||||
total: 10059776,
|
||||
},
|
||||
IoService {
|
||||
major: 8,
|
||||
minor: 0 ,
|
||||
read: 7192576,
|
||||
write: 0,
|
||||
sync: 7192576,
|
||||
async: 0,
|
||||
total: 7192576,
|
||||
}
|
||||
]));
|
||||
assert_eq!(parse_io_service(test_wrong_value.to_string()), Err(CgroupError::ParseError));
|
||||
assert_eq!(
|
||||
parse_io_service(test_value.to_string()),
|
||||
Ok(vec![
|
||||
IoService {
|
||||
major: 8,
|
||||
minor: 32,
|
||||
read: 4280320,
|
||||
write: 0,
|
||||
sync: 4280320,
|
||||
async: 0,
|
||||
total: 4280320,
|
||||
},
|
||||
IoService {
|
||||
major: 8,
|
||||
minor: 48,
|
||||
read: 5705479168,
|
||||
write: 56096055296,
|
||||
sync: 11213923328,
|
||||
async: 50587611136,
|
||||
total: 61801534464,
|
||||
},
|
||||
IoService {
|
||||
major: 8,
|
||||
minor: 16,
|
||||
read: 10059776,
|
||||
write: 0,
|
||||
sync: 10059776,
|
||||
async: 0,
|
||||
total: 10059776,
|
||||
},
|
||||
IoService {
|
||||
major: 8,
|
||||
minor: 0,
|
||||
read: 7192576,
|
||||
write: 0,
|
||||
sync: 7192576,
|
||||
async: 0,
|
||||
total: 7192576,
|
||||
}
|
||||
])
|
||||
);
|
||||
assert_eq!(
|
||||
parse_io_service(test_wrong_value.to_string()),
|
||||
Err(CgroupError::ParseError)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_blkio_data() {
|
||||
assert_eq!(parse_blkio_data(test_blkio_data.to_string()), Ok(vec![
|
||||
BlkIoData {
|
||||
major: 8,
|
||||
minor: 48,
|
||||
data: 454480833999,
|
||||
},
|
||||
BlkIoData {
|
||||
major: 8,
|
||||
minor: 32,
|
||||
data: 228392923193,
|
||||
},
|
||||
BlkIoData {
|
||||
major: 8,
|
||||
minor: 16,
|
||||
data: 772456885,
|
||||
},
|
||||
BlkIoData {
|
||||
major: 8,
|
||||
minor: 0,
|
||||
data: 559583764,
|
||||
}
|
||||
]));
|
||||
assert_eq!(
|
||||
parse_blkio_data(test_blkio_data.to_string()),
|
||||
Ok(vec![
|
||||
BlkIoData {
|
||||
major: 8,
|
||||
minor: 48,
|
||||
data: 454480833999,
|
||||
},
|
||||
BlkIoData {
|
||||
major: 8,
|
||||
minor: 32,
|
||||
data: 228392923193,
|
||||
},
|
||||
BlkIoData {
|
||||
major: 8,
|
||||
minor: 16,
|
||||
data: 772456885,
|
||||
},
|
||||
BlkIoData {
|
||||
major: 8,
|
||||
minor: 0,
|
||||
data: 559583764,
|
||||
}
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
//! This module handles cgroup operations. Start here!
|
||||
|
||||
use {CgroupError, CgroupPid, Resources, ControllIdentifier, Controller, Hierarchy, Subsystem};
|
||||
use {CgroupError, CgroupPid, ControllIdentifier, Controller, Hierarchy, Resources, Subsystem};
|
||||
|
||||
use std::convert::From;
|
||||
|
||||
|
||||
/// A control group is the central structure to this crate.
|
||||
///
|
||||
///
|
||||
/// # What are control groups?
|
||||
///
|
||||
/// Lifting over from the Linux kernel sources:
|
||||
/// Lifting over from the Linux kernel sources:
|
||||
///
|
||||
/// > Control Groups provide a mechanism for aggregating/partitioning sets of
|
||||
/// > tasks, and all their future children, into hierarchical groups with
|
||||
@@ -26,7 +25,6 @@ pub struct Cgroup<'b> {
|
||||
}
|
||||
|
||||
impl<'b> Cgroup<'b> {
|
||||
|
||||
/// Create this control group.
|
||||
fn create(&self) {
|
||||
for subsystem in &self.subsystems {
|
||||
@@ -55,8 +53,11 @@ impl<'b> Cgroup<'b> {
|
||||
/// destroyed.
|
||||
pub fn load(hier: &Hierarchy, path: String) -> Cgroup {
|
||||
let mut subsystems = hier.subsystems();
|
||||
if path != "" {
|
||||
subsystems = subsystems.into_iter().map(|x| x.enter(&path)).collect::<Vec<_>>();
|
||||
if path != "" {
|
||||
subsystems = subsystems
|
||||
.into_iter()
|
||||
.map(|x| x.enter(&path))
|
||||
.collect::<Vec<_>>();
|
||||
}
|
||||
|
||||
let cg = Cgroup {
|
||||
@@ -79,28 +80,28 @@ impl<'b> Cgroup<'b> {
|
||||
/// actually removed, and remove the descendants first if not. In the future, this behavior
|
||||
/// will change.
|
||||
pub fn delete(self) {
|
||||
self.subsystems.into_iter().for_each(|sub| {
|
||||
match sub {
|
||||
Subsystem::Pid(pidc) => pidc.delete(),
|
||||
Subsystem::Mem(c) => c.delete(),
|
||||
Subsystem::CpuSet(c) => c.delete(),
|
||||
Subsystem::CpuAcct(c) => c.delete(),
|
||||
Subsystem::Cpu(c) => c.delete(),
|
||||
Subsystem::Devices(c) => c.delete(),
|
||||
Subsystem::Freezer(c) => c.delete(),
|
||||
Subsystem::NetCls(c) => c.delete(),
|
||||
Subsystem::BlkIo(c) => c.delete(),
|
||||
Subsystem::PerfEvent(c) => c.delete(),
|
||||
Subsystem::NetPrio(c) => c.delete(),
|
||||
Subsystem::HugeTlb(c) => c.delete(),
|
||||
Subsystem::Rdma(c) => c.delete(),
|
||||
}
|
||||
self.subsystems.into_iter().for_each(|sub| match sub {
|
||||
Subsystem::Pid(pidc) => pidc.delete(),
|
||||
Subsystem::Mem(c) => c.delete(),
|
||||
Subsystem::CpuSet(c) => c.delete(),
|
||||
Subsystem::CpuAcct(c) => c.delete(),
|
||||
Subsystem::Cpu(c) => c.delete(),
|
||||
Subsystem::Devices(c) => c.delete(),
|
||||
Subsystem::Freezer(c) => c.delete(),
|
||||
Subsystem::NetCls(c) => c.delete(),
|
||||
Subsystem::BlkIo(c) => c.delete(),
|
||||
Subsystem::PerfEvent(c) => c.delete(),
|
||||
Subsystem::NetPrio(c) => c.delete(),
|
||||
Subsystem::HugeTlb(c) => c.delete(),
|
||||
Subsystem::Rdma(c) => c.delete(),
|
||||
});
|
||||
}
|
||||
|
||||
/// Apply a set of resource limits to the control group.
|
||||
pub fn apply(&self, res: &Resources) -> Result<(), CgroupError> {
|
||||
self.subsystems.iter().try_fold((), |_, e| e.to_controller().apply(res))
|
||||
self.subsystems
|
||||
.iter()
|
||||
.try_fold((), |_, e| e.to_controller().apply(res))
|
||||
}
|
||||
|
||||
/// Retrieve a container based on type inference.
|
||||
@@ -114,8 +115,9 @@ impl<'b> Cgroup<'b> {
|
||||
/// .expect("No cpu controller attached!");
|
||||
/// ```
|
||||
pub fn controller_of<'a, T>(self: &'a Self) -> Option<&'a T>
|
||||
where &'a T: From<&'a Subsystem>,
|
||||
T: Controller + ControllIdentifier,
|
||||
where
|
||||
&'a T: From<&'a Subsystem>,
|
||||
T: Controller + ControllIdentifier,
|
||||
{
|
||||
for i in &self.subsystems {
|
||||
if i.to_controller().control_type() == T::controller_type() {
|
||||
@@ -139,19 +141,25 @@ impl<'b> Cgroup<'b> {
|
||||
|
||||
/// Attach a task to the control group.
|
||||
pub fn add_task(&self, pid: CgroupPid) -> Result<(), CgroupError> {
|
||||
self.subsystems().iter().try_for_each(|sub| sub.to_controller().add_task(&pid))
|
||||
self.subsystems()
|
||||
.iter()
|
||||
.try_for_each(|sub| sub.to_controller().add_task(&pid))
|
||||
}
|
||||
|
||||
/// Returns an Iterator that can be used to iterate over the tasks that are currently in the
|
||||
/// control group.
|
||||
pub fn tasks(&self) -> Vec<CgroupPid> {
|
||||
/* Collect the tasks from all subsystems */
|
||||
let mut v = self.subsystems().iter()
|
||||
let mut v = self
|
||||
.subsystems()
|
||||
.iter()
|
||||
.map(|x| x.to_controller().tasks())
|
||||
.fold(vec![], |mut acc, mut x| { acc.append(&mut x); acc });
|
||||
.fold(vec![], |mut acc, mut x| {
|
||||
acc.append(&mut x);
|
||||
acc
|
||||
});
|
||||
v.sort();
|
||||
v.dedup();
|
||||
v
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
76
src/cpu.rs
76
src/cpu.rs
@@ -1,21 +1,23 @@
|
||||
//! This module contains the implementation of the `cpu` cgroup subsystem.
|
||||
//!
|
||||
//!
|
||||
//! See the Kernel's documentation for more information about this subsystem, found at:
|
||||
//! [Documentation/scheduler/sched-design-CFS.txt](https://www.kernel.org/doc/Documentation/scheduler/sched-design-CFS.txt)
|
||||
//! paragraph 7 ("GROUP SCHEDULER EXTENSIONS TO CFS").
|
||||
use std::path::PathBuf;
|
||||
use std::io::{Read, Write};
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use {CgroupError, CpuResources, Controllers, Controller, Resources, ControllIdentifier, Subsystem};
|
||||
use {
|
||||
CgroupError, ControllIdentifier, Controller, Controllers, CpuResources, Resources, Subsystem,
|
||||
};
|
||||
|
||||
/// A controller that allows controlling the `cpu` subsystem of a Cgroup.
|
||||
///
|
||||
///
|
||||
/// In essence, it allows gathering information about how much the tasks inside the control group
|
||||
/// are using the CPU and creating rules that limit their usage. Note that this crate does not yet
|
||||
/// support managing realtime tasks.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CpuController{
|
||||
pub struct CpuController {
|
||||
base: PathBuf,
|
||||
path: PathBuf,
|
||||
}
|
||||
@@ -30,10 +32,18 @@ pub struct Cpu {
|
||||
}
|
||||
|
||||
impl Controller for CpuController {
|
||||
fn control_type(&self) -> Controllers { Controllers::Cpu}
|
||||
fn get_path(&self) -> &PathBuf { &self.path }
|
||||
fn get_path_mut(&mut self) -> &mut PathBuf { &mut self.path }
|
||||
fn get_base(&self) -> &PathBuf { &self.base }
|
||||
fn control_type(&self) -> Controllers {
|
||||
Controllers::Cpu
|
||||
}
|
||||
fn get_path(&self) -> &PathBuf {
|
||||
&self.path
|
||||
}
|
||||
fn get_path_mut(&mut self) -> &mut PathBuf {
|
||||
&mut self.path
|
||||
}
|
||||
fn get_base(&self) -> &PathBuf {
|
||||
&self.base
|
||||
}
|
||||
|
||||
fn apply(&self, res: &Resources) -> Result<(), CgroupError> {
|
||||
/* get the resources that apply to this controller */
|
||||
@@ -74,7 +84,7 @@ impl<'a> From<&'a Subsystem> for &'a CpuController {
|
||||
_ => {
|
||||
assert_eq!(1, 0);
|
||||
::std::mem::uninitialized()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -102,42 +112,46 @@ impl CpuController {
|
||||
/// Returns CPU time statistics based on the processes in the control group.
|
||||
pub fn cpu(&self) -> Cpu {
|
||||
Cpu {
|
||||
stat: self.open_path("cpu.stat", false).and_then(|mut file| {
|
||||
let mut s = String::new();
|
||||
let res = file.read_to_string(&mut s);
|
||||
match res {
|
||||
Ok(_) => Ok(s),
|
||||
Err(e) => Err(CgroupError::ReadError(e)),
|
||||
}
|
||||
}).unwrap_or("".to_string()),
|
||||
stat: self
|
||||
.open_path("cpu.stat", false)
|
||||
.and_then(|mut file| {
|
||||
let mut s = String::new();
|
||||
let res = file.read_to_string(&mut s);
|
||||
match res {
|
||||
Ok(_) => Ok(s),
|
||||
Err(e) => Err(CgroupError::ReadError(e)),
|
||||
}
|
||||
}).unwrap_or("".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Configures the CPU bandwidth (in relative relation to other control groups and this control
|
||||
/// group's parent).
|
||||
///
|
||||
///
|
||||
/// For example, setting control group `A`'s `shares` to `100`, and control group `B`'s
|
||||
/// `shares` to `200` ensures that control group `B` receives twice as much as CPU bandwidth.
|
||||
/// (Assuming both `A` and `B` are of the same parent)
|
||||
pub fn set_shares(&self, shares: u64) -> Result<(), CgroupError> {
|
||||
self.open_path("cpu.shares", true).and_then(|mut file| {
|
||||
file.write_all(shares.to_string().as_ref()).map_err(CgroupError::WriteError)
|
||||
file.write_all(shares.to_string().as_ref())
|
||||
.map_err(CgroupError::WriteError)
|
||||
})
|
||||
}
|
||||
|
||||
/// Retrieve the CPU bandwidth that this control group (relative to other control groups and
|
||||
/// this control group's parent) can use.
|
||||
pub fn shares(&self) -> Result<u64, CgroupError> {
|
||||
self.open_path("cpu.shares", false)
|
||||
.and_then(read_u64_from)
|
||||
self.open_path("cpu.shares", false).and_then(read_u64_from)
|
||||
}
|
||||
|
||||
/// Specify a period (when using the CFS scheduler) of time in microseconds for how often this
|
||||
/// control group's access to the CPU should be reallocated.
|
||||
pub fn set_cfs_period(&self, us: u64) -> Result<(), CgroupError> {
|
||||
self.open_path("cpu.cfs_period_us", true).and_then(|mut file| {
|
||||
file.write_all(us.to_string().as_ref()).map_err(CgroupError::WriteError)
|
||||
})
|
||||
self.open_path("cpu.cfs_period_us", true)
|
||||
.and_then(|mut file| {
|
||||
file.write_all(us.to_string().as_ref())
|
||||
.map_err(CgroupError::WriteError)
|
||||
})
|
||||
}
|
||||
|
||||
/// Retrieve the period of time of how often this cgroup's access to the CPU should be
|
||||
@@ -150,11 +164,13 @@ impl CpuController {
|
||||
/// Specify a quota (when using the CFS scheduler) of time in microseconds for which all tasks
|
||||
/// in this control group can run during one period (see: `set_cfs_period()`).
|
||||
pub fn set_cfs_quota(&self, us: u64) -> Result<(), CgroupError> {
|
||||
self.open_path("cpu.cfs_quota_us", true).and_then(|mut file| {
|
||||
file.write_all(us.to_string().as_ref()).map_err(CgroupError::WriteError)
|
||||
})
|
||||
self.open_path("cpu.cfs_quota_us", true)
|
||||
.and_then(|mut file| {
|
||||
file.write_all(us.to_string().as_ref())
|
||||
.map_err(CgroupError::WriteError)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/// Retrieve the quota of time for which all tasks in this cgroup can run during one period, in
|
||||
/// microseconds.
|
||||
pub fn cfs_quota(&self) -> Result<u64, CgroupError> {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
//! This module contains the implementation of the `cpuacct` cgroup subsystem.
|
||||
//!
|
||||
//!
|
||||
//! See the Kernel's documentation for more information about this subsystem, found at:
|
||||
//! [Documentation/cgroup-v1/cpuacct.txt](https://www.kernel.org/doc/Documentation/cgroup-v1/cpuacct.txt)
|
||||
use std::path::PathBuf;
|
||||
use std::io::{Read, Write};
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use {CgroupError, Controllers, Resources, Subsystem, ControllIdentifier, Controller};
|
||||
use {CgroupError, ControllIdentifier, Controller, Controllers, Resources, Subsystem};
|
||||
|
||||
/// A controller that allows controlling the `cpuacct` subsystem of a Cgroup.
|
||||
///
|
||||
@@ -50,10 +50,18 @@ pub struct CpuAcct {
|
||||
}
|
||||
|
||||
impl Controller for CpuAcctController {
|
||||
fn control_type(&self) -> Controllers { Controllers::CpuAcct }
|
||||
fn get_path(&self) -> &PathBuf { &self.path }
|
||||
fn get_path_mut(&mut self) -> &mut PathBuf { &mut self.path }
|
||||
fn get_base(&self) -> &PathBuf { &self.base }
|
||||
fn control_type(&self) -> Controllers {
|
||||
Controllers::CpuAcct
|
||||
}
|
||||
fn get_path(&self) -> &PathBuf {
|
||||
&self.path
|
||||
}
|
||||
fn get_path_mut(&mut self) -> &mut PathBuf {
|
||||
&mut self.path
|
||||
}
|
||||
fn get_base(&self) -> &PathBuf {
|
||||
&self.base
|
||||
}
|
||||
|
||||
fn apply(&self, _res: &Resources) -> Result<(), CgroupError> {
|
||||
Ok(())
|
||||
@@ -74,7 +82,7 @@ impl<'a> From<&'a Subsystem> for &'a CpuAcctController {
|
||||
_ => {
|
||||
assert_eq!(1, 0);
|
||||
::std::mem::uninitialized()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,7 +109,6 @@ fn read_string_from(mut file: File) -> Result<String, CgroupError> {
|
||||
}
|
||||
|
||||
impl CpuAcctController {
|
||||
|
||||
/// Contructs a new `CpuAcctController` with `oroot` serving as the root of the control group.
|
||||
pub fn new(oroot: PathBuf) -> Self {
|
||||
let mut root = oroot;
|
||||
@@ -115,32 +122,44 @@ impl CpuAcctController {
|
||||
/// Gathers the statistics that are available in the control group into a `CpuAcct` structure.
|
||||
pub fn cpuacct(&self) -> CpuAcct {
|
||||
CpuAcct {
|
||||
stat: self.open_path("cpuacct.stat", false)
|
||||
.and_then(|file| read_string_from(file)).unwrap_or("".to_string()),
|
||||
usage: self.open_path("cpuacct.usage", false)
|
||||
.and_then(|file| read_u64_from(file))
|
||||
.unwrap_or(0),
|
||||
usage_all: self.open_path("cpuacct.usage_all", false)
|
||||
.and_then(|file| read_string_from(file)).unwrap_or("".to_string()),
|
||||
usage_percpu: self.open_path("cpuacct.usage_percpu", false)
|
||||
.and_then(|file| read_string_from(file)).unwrap_or("".to_string()),
|
||||
usage_percpu_sys: self.open_path("cpuacct.usage_percpu_sys", false)
|
||||
.and_then(|file| read_string_from(file)).unwrap_or("".to_string()),
|
||||
usage_percpu_user: self.open_path("cpuacct.usage_percpu_user", false)
|
||||
.and_then(|file| read_string_from(file)).unwrap_or("".to_string()),
|
||||
usage_sys: self.open_path("cpuacct.usage_sys", false)
|
||||
.and_then(|file| read_u64_from(file))
|
||||
.unwrap_or(0),
|
||||
usage_user: self.open_path("cpuacct.usage_user", false)
|
||||
.and_then(|file| read_u64_from(file))
|
||||
.unwrap_or(0),
|
||||
stat: self
|
||||
.open_path("cpuacct.stat", false)
|
||||
.and_then(|file| read_string_from(file))
|
||||
.unwrap_or("".to_string()),
|
||||
usage: self
|
||||
.open_path("cpuacct.usage", false)
|
||||
.and_then(|file| read_u64_from(file))
|
||||
.unwrap_or(0),
|
||||
usage_all: self
|
||||
.open_path("cpuacct.usage_all", false)
|
||||
.and_then(|file| read_string_from(file))
|
||||
.unwrap_or("".to_string()),
|
||||
usage_percpu: self
|
||||
.open_path("cpuacct.usage_percpu", false)
|
||||
.and_then(|file| read_string_from(file))
|
||||
.unwrap_or("".to_string()),
|
||||
usage_percpu_sys: self
|
||||
.open_path("cpuacct.usage_percpu_sys", false)
|
||||
.and_then(|file| read_string_from(file))
|
||||
.unwrap_or("".to_string()),
|
||||
usage_percpu_user: self
|
||||
.open_path("cpuacct.usage_percpu_user", false)
|
||||
.and_then(|file| read_string_from(file))
|
||||
.unwrap_or("".to_string()),
|
||||
usage_sys: self
|
||||
.open_path("cpuacct.usage_sys", false)
|
||||
.and_then(|file| read_u64_from(file))
|
||||
.unwrap_or(0),
|
||||
usage_user: self
|
||||
.open_path("cpuacct.usage_user", false)
|
||||
.and_then(|file| read_u64_from(file))
|
||||
.unwrap_or(0),
|
||||
}
|
||||
}
|
||||
|
||||
/// Reset the statistics the kernel has gathered about the control group.
|
||||
pub fn reset(&self) -> Result<(), CgroupError> {
|
||||
self.open_path("cpuacct.usage", true).and_then(|mut file| {
|
||||
file.write_all(b"0").map_err(CgroupError::WriteError)
|
||||
})
|
||||
self.open_path("cpuacct.usage", true)
|
||||
.and_then(|mut file| file.write_all(b"0").map_err(CgroupError::WriteError))
|
||||
}
|
||||
}
|
||||
|
||||
263
src/cpuset.rs
263
src/cpuset.rs
@@ -1,16 +1,18 @@
|
||||
//! This module contains the implementation of the `cpuset` cgroup subsystem.
|
||||
//!
|
||||
//!
|
||||
//! See the Kernel's documentation for more information about this subsystem, found at:
|
||||
//! [Documentation/cgroup-v1/cpusets.txt](https://www.kernel.org/doc/Documentation/cgroup-v1/cpusets.txt)
|
||||
use std::path::PathBuf;
|
||||
use std::io::{Read, Write};
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use {CgroupError, CpuResources, Resources, Controller, ControllIdentifier, Subsystem, Controllers};
|
||||
use CgroupError::*;
|
||||
use {
|
||||
CgroupError, ControllIdentifier, Controller, Controllers, CpuResources, Resources, Subsystem,
|
||||
};
|
||||
|
||||
/// A controller that allows controlling the `cpuset` subsystem of a Cgroup.
|
||||
///
|
||||
///
|
||||
/// In essence, this controller is responsible for restricting the tasks in the control group to a
|
||||
/// set of CPUs and/or memory nodes.
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -50,11 +52,11 @@ pub struct CpuSet {
|
||||
/// the memory pressure for control groups or not.
|
||||
pub memory_pressure_enabled: Option<bool>,
|
||||
/// If true, filesystem buffers are spread across evenly between the nodes specified in `mems`.
|
||||
pub memory_spread_page: bool,
|
||||
pub memory_spread_page: bool,
|
||||
/// If true, kernel slab caches for file I/O are spread across evenly between the nodes
|
||||
/// specified in `mems`.
|
||||
pub memory_spread_slab: bool,
|
||||
/// The list of memory nodes the tasks of the control group can use.
|
||||
pub memory_spread_slab: bool,
|
||||
/// The list of memory nodes the tasks of the control group can use.
|
||||
///
|
||||
/// The format is the same as the `cpus`, `effective_cpus` and `effective_mems` fields.
|
||||
pub mems: Vec<(u64, u64)>,
|
||||
@@ -73,14 +75,21 @@ pub struct CpuSet {
|
||||
/// | 5 | Immediately balance the load between CPUs even if the system is NUMA |
|
||||
/// | 6 | Immediately balance the load between all CPUs |
|
||||
pub sched_relax_domain_level: u64,
|
||||
|
||||
}
|
||||
|
||||
impl Controller for CpuSetController {
|
||||
fn control_type(&self) -> Controllers { Controllers::CpuSet }
|
||||
fn get_path(&self) -> &PathBuf { &self.path }
|
||||
fn get_path_mut(&mut self) -> &mut PathBuf { &mut self.path }
|
||||
fn get_base(&self) -> &PathBuf { &self.base }
|
||||
fn control_type(&self) -> Controllers {
|
||||
Controllers::CpuSet
|
||||
}
|
||||
fn get_path(&self) -> &PathBuf {
|
||||
&self.path
|
||||
}
|
||||
fn get_path_mut(&mut self) -> &mut PathBuf {
|
||||
&mut self.path
|
||||
}
|
||||
fn get_base(&self) -> &PathBuf {
|
||||
&self.base
|
||||
}
|
||||
|
||||
fn apply(&self, res: &Resources) -> Result<(), CgroupError> {
|
||||
/* get the resources that apply to this controller */
|
||||
@@ -109,7 +118,7 @@ impl<'a> From<&'a Subsystem> for &'a CpuSetController {
|
||||
_ => {
|
||||
assert_eq!(1, 0);
|
||||
::std::mem::uninitialized()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -184,13 +193,16 @@ impl CpuSetController {
|
||||
pub fn cpuset(&self) -> CpuSet {
|
||||
CpuSet {
|
||||
cpu_exclusive: {
|
||||
self.open_path("cpuset.cpu_exclusive", false).and_then(|file| {
|
||||
read_u64_from(file)
|
||||
}).map(|x| x == 1).unwrap_or(false)
|
||||
self.open_path("cpuset.cpu_exclusive", false)
|
||||
.and_then(|file| read_u64_from(file))
|
||||
.map(|x| x == 1)
|
||||
.unwrap_or(false)
|
||||
},
|
||||
cpus: {
|
||||
self.open_path("cpuset.cpus", false).and_then(read_string_from)
|
||||
.and_then(parse_range).unwrap_or(Vec::new())
|
||||
self.open_path("cpuset.cpus", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_range)
|
||||
.unwrap_or(Vec::new())
|
||||
},
|
||||
effective_cpus: {
|
||||
self.open_path("cpuset.effective_cpus", false)
|
||||
@@ -205,31 +217,45 @@ impl CpuSetController {
|
||||
.unwrap_or(Vec::new())
|
||||
},
|
||||
mem_exclusive: {
|
||||
self.open_path("cpuset.mem_exclusive", false).and_then(read_u64_from)
|
||||
.map(|x| x == 1).unwrap_or(false)
|
||||
self.open_path("cpuset.mem_exclusive", false)
|
||||
.and_then(read_u64_from)
|
||||
.map(|x| x == 1)
|
||||
.unwrap_or(false)
|
||||
},
|
||||
mem_hardwall: {
|
||||
self.open_path("cpuset.mem_hardwall", false).and_then(read_u64_from)
|
||||
.map(|x| x == 1).unwrap_or(false)
|
||||
self.open_path("cpuset.mem_hardwall", false)
|
||||
.and_then(read_u64_from)
|
||||
.map(|x| x == 1)
|
||||
.unwrap_or(false)
|
||||
},
|
||||
memory_migrate: {
|
||||
self.open_path("cpuset.memory_migrate", false).and_then(read_u64_from)
|
||||
.map(|x| x == 1).unwrap_or(false)
|
||||
self.open_path("cpuset.memory_migrate", false)
|
||||
.and_then(read_u64_from)
|
||||
.map(|x| x == 1)
|
||||
.unwrap_or(false)
|
||||
},
|
||||
memory_pressure: {
|
||||
self.open_path("cpuset.memory_pressure", false).and_then(read_u64_from).unwrap_or(0)
|
||||
self.open_path("cpuset.memory_pressure", false)
|
||||
.and_then(read_u64_from)
|
||||
.unwrap_or(0)
|
||||
},
|
||||
memory_pressure_enabled: {
|
||||
self.open_path("cpuset.memory_pressure_enabled", false).and_then(read_u64_from)
|
||||
.map(|x| x == 1).ok()
|
||||
self.open_path("cpuset.memory_pressure_enabled", false)
|
||||
.and_then(read_u64_from)
|
||||
.map(|x| x == 1)
|
||||
.ok()
|
||||
},
|
||||
memory_spread_page: {
|
||||
self.open_path("cpuset.memory_spread_page", false).and_then(read_u64_from)
|
||||
.map(|x| x == 1).unwrap_or(false)
|
||||
self.open_path("cpuset.memory_spread_page", false)
|
||||
.and_then(read_u64_from)
|
||||
.map(|x| x == 1)
|
||||
.unwrap_or(false)
|
||||
},
|
||||
memory_spread_slab: {
|
||||
self.open_path("cpuset.memory_spread_slab", false).and_then(read_u64_from)
|
||||
.map(|x| x == 1).unwrap_or(false)
|
||||
self.open_path("cpuset.memory_spread_slab", false)
|
||||
.and_then(read_u64_from)
|
||||
.map(|x| x == 1)
|
||||
.unwrap_or(false)
|
||||
},
|
||||
mems: {
|
||||
self.open_path("cpuset.mems", false)
|
||||
@@ -238,11 +264,14 @@ impl CpuSetController {
|
||||
.unwrap_or(Vec::new())
|
||||
},
|
||||
sched_load_balance: {
|
||||
self.open_path("cpuset.sched_load_balance", false).and_then(read_u64_from)
|
||||
.map(|x| x == 1).unwrap_or(false)
|
||||
self.open_path("cpuset.sched_load_balance", false)
|
||||
.and_then(read_u64_from)
|
||||
.map(|x| x == 1)
|
||||
.unwrap_or(false)
|
||||
},
|
||||
sched_relax_domain_level: {
|
||||
self.open_path("cpuset.sched_relax_domain_level", false).and_then(read_u64_from)
|
||||
self.open_path("cpuset.sched_relax_domain_level", false)
|
||||
.and_then(read_u64_from)
|
||||
.unwrap_or(0)
|
||||
},
|
||||
}
|
||||
@@ -251,25 +280,27 @@ impl CpuSetController {
|
||||
/// Control whether the CPUs selected via `set_cpus()` should be exclusive to this control
|
||||
/// group or not.
|
||||
pub fn set_cpu_exclusive(&self, b: bool) -> Result<(), CgroupError> {
|
||||
self.open_path("cpuset.cpu_exclusive", true).and_then(|mut file| {
|
||||
if b {
|
||||
file.write_all(b"1").map_err(CgroupError::WriteError)
|
||||
} else {
|
||||
file.write_all(b"0").map_err(CgroupError::WriteError)
|
||||
}
|
||||
})
|
||||
self.open_path("cpuset.cpu_exclusive", true)
|
||||
.and_then(|mut file| {
|
||||
if b {
|
||||
file.write_all(b"1").map_err(CgroupError::WriteError)
|
||||
} else {
|
||||
file.write_all(b"0").map_err(CgroupError::WriteError)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Control whether the memory nodes selected via `set_memss()` should be exclusive to this control
|
||||
/// group or not.
|
||||
pub fn set_mem_exclusive(&self, b: bool) -> Result<(), CgroupError> {
|
||||
self.open_path("cpuset.mem_exclusive", true).and_then(|mut file| {
|
||||
if b {
|
||||
file.write_all(b"1").map_err(CgroupError::WriteError)
|
||||
} else {
|
||||
file.write_all(b"0").map_err(CgroupError::WriteError)
|
||||
}
|
||||
})
|
||||
self.open_path("cpuset.mem_exclusive", true)
|
||||
.and_then(|mut file| {
|
||||
if b {
|
||||
file.write_all(b"1").map_err(CgroupError::WriteError)
|
||||
} else {
|
||||
file.write_all(b"0").map_err(CgroupError::WriteError)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Set the CPUs that the tasks in this control group can run on.
|
||||
@@ -278,7 +309,8 @@ impl CpuSetController {
|
||||
/// be represented via dashes.
|
||||
pub fn set_cpus(&self, cpus: &String) -> Result<(), CgroupError> {
|
||||
self.open_path("cpuset.cpus", true).and_then(|mut file| {
|
||||
file.write_all(cpus.as_ref()).map_err(CgroupError::WriteError)
|
||||
file.write_all(cpus.as_ref())
|
||||
.map_err(CgroupError::WriteError)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -287,7 +319,8 @@ impl CpuSetController {
|
||||
/// Syntax is the same as with `set_cpus()`.
|
||||
pub fn set_mems(&self, mems: &String) -> Result<(), CgroupError> {
|
||||
self.open_path("cpuset.mems", true).and_then(|mut file| {
|
||||
file.write_all(mems.as_ref()).map_err(CgroupError::WriteError)
|
||||
file.write_all(mems.as_ref())
|
||||
.map_err(CgroupError::WriteError)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -297,70 +330,77 @@ impl CpuSetController {
|
||||
/// Note that some kernel allocations, most notably those that are made in interrupt handlers
|
||||
/// may disregard this.
|
||||
pub fn set_hardwall(&self, b: bool) -> Result<(), CgroupError> {
|
||||
self.open_path("cpuset.mem_hardwall", true).and_then(|mut file| {
|
||||
if b {
|
||||
file.write_all(b"1").map_err(CgroupError::WriteError)
|
||||
} else {
|
||||
file.write_all(b"0").map_err(CgroupError::WriteError)
|
||||
}
|
||||
})
|
||||
self.open_path("cpuset.mem_hardwall", true)
|
||||
.and_then(|mut file| {
|
||||
if b {
|
||||
file.write_all(b"1").map_err(CgroupError::WriteError)
|
||||
} else {
|
||||
file.write_all(b"0").map_err(CgroupError::WriteError)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Controls whether the kernel should attempt to rebalance the load between the CPUs specified in the
|
||||
/// `cpus` field of this control group.
|
||||
pub fn set_load_balancing(&self, b: bool) -> Result<(), CgroupError> {
|
||||
self.open_path("cpuset.sched_load_balance", true).and_then(|mut file| {
|
||||
if b {
|
||||
file.write_all(b"1").map_err(CgroupError::WriteError)
|
||||
} else {
|
||||
file.write_all(b"0").map_err(CgroupError::WriteError)
|
||||
}
|
||||
})
|
||||
self.open_path("cpuset.sched_load_balance", true)
|
||||
.and_then(|mut file| {
|
||||
if b {
|
||||
file.write_all(b"1").map_err(CgroupError::WriteError)
|
||||
} else {
|
||||
file.write_all(b"0").map_err(CgroupError::WriteError)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Contorl how much effort the kernel should invest in rebalacing the control group.
|
||||
///
|
||||
/// See @CpuSet 's similar field for more information.
|
||||
pub fn set_rebalance_relax_domain_level(&self, i: i64) -> Result<(), CgroupError> {
|
||||
self.open_path("cpuset.sched_relax_domain_level", true).and_then(|mut file| {
|
||||
file.write_all(i.to_string().as_ref()).map_err(CgroupError::WriteError)
|
||||
})
|
||||
self.open_path("cpuset.sched_relax_domain_level", true)
|
||||
.and_then(|mut file| {
|
||||
file.write_all(i.to_string().as_ref())
|
||||
.map_err(CgroupError::WriteError)
|
||||
})
|
||||
}
|
||||
|
||||
/// Control whether when using `set_mems()` the existing memory used by the tasks should be
|
||||
/// migrated over to the now-selected nodes.
|
||||
pub fn set_memory_migration(&self, b: bool) -> Result<(), CgroupError> {
|
||||
self.open_path("cpuset.memory_migrate", true).and_then(|mut file| {
|
||||
if b {
|
||||
file.write_all(b"1").map_err(CgroupError::WriteError)
|
||||
} else {
|
||||
file.write_all(b"0").map_err(CgroupError::WriteError)
|
||||
}
|
||||
})
|
||||
self.open_path("cpuset.memory_migrate", true)
|
||||
.and_then(|mut file| {
|
||||
if b {
|
||||
file.write_all(b"1").map_err(CgroupError::WriteError)
|
||||
} else {
|
||||
file.write_all(b"0").map_err(CgroupError::WriteError)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Control whether filesystem buffers should be evenly split across the nodes selected via
|
||||
/// `set_mems()`.
|
||||
pub fn set_memory_spread_page(&self, b: bool) -> Result<(), CgroupError> {
|
||||
self.open_path("cpuset.memory_spread_page", true).and_then(|mut file| {
|
||||
if b {
|
||||
file.write_all(b"1").map_err(CgroupError::WriteError)
|
||||
} else {
|
||||
file.write_all(b"0").map_err(CgroupError::WriteError)
|
||||
}
|
||||
})
|
||||
self.open_path("cpuset.memory_spread_page", true)
|
||||
.and_then(|mut file| {
|
||||
if b {
|
||||
file.write_all(b"1").map_err(CgroupError::WriteError)
|
||||
} else {
|
||||
file.write_all(b"0").map_err(CgroupError::WriteError)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Control whether the kernel's slab cache for file I/O should be evenly split across the
|
||||
/// nodes selected via `set_mems()`.
|
||||
pub fn set_memory_spread_slab(&self, b: bool) -> Result<(), CgroupError> {
|
||||
self.open_path("cpuset.memory_spread_slab", true).and_then(|mut file| {
|
||||
if b {
|
||||
file.write_all(b"1").map_err(CgroupError::WriteError)
|
||||
} else {
|
||||
file.write_all(b"0").map_err(CgroupError::WriteError)
|
||||
}
|
||||
})
|
||||
self.open_path("cpuset.memory_spread_slab", true)
|
||||
.and_then(|mut file| {
|
||||
if b {
|
||||
file.write_all(b"1").map_err(CgroupError::WriteError)
|
||||
} else {
|
||||
file.write_all(b"0").map_err(CgroupError::WriteError)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Control whether the kernel should collect information to calculate memory pressure for
|
||||
@@ -372,13 +412,14 @@ impl CpuSetController {
|
||||
if !self.path_exists("cpuset.memory_pressure_enabled") {
|
||||
return Err(CgroupError::InvalidOperation);
|
||||
}
|
||||
self.open_path("cpuset.memory_pressure_enabled", true).and_then(|mut file| {
|
||||
if b {
|
||||
file.write_all(b"1").map_err(CgroupError::WriteError)
|
||||
} else {
|
||||
file.write_all(b"0").map_err(CgroupError::WriteError)
|
||||
}
|
||||
})
|
||||
self.open_path("cpuset.memory_pressure_enabled", true)
|
||||
.and_then(|mut file| {
|
||||
if b {
|
||||
file.write_all(b"1").map_err(CgroupError::WriteError)
|
||||
} else {
|
||||
file.write_all(b"0").map_err(CgroupError::WriteError)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -387,20 +428,22 @@ mod tests {
|
||||
use cpuset;
|
||||
#[test]
|
||||
fn test_parse_range() {
|
||||
let test_cases = vec!["1,2,4-6,9".to_string(),
|
||||
"".to_string(),
|
||||
"1".to_string(),
|
||||
"1-111".to_string(),
|
||||
"1,2,3,4".to_string(),
|
||||
"1-5,6-7,8-9".to_string()
|
||||
];
|
||||
let expecteds = vec![vec![(1, 1), (2, 2), (4, 6), (9, 9)],
|
||||
vec![],
|
||||
vec![(1, 1)],
|
||||
vec![(1, 111)],
|
||||
vec![(1, 1), (2, 2), (3, 3), (4, 4)],
|
||||
vec![(1, 5), (6, 7), (8, 9)]
|
||||
];
|
||||
let test_cases = vec![
|
||||
"1,2,4-6,9".to_string(),
|
||||
"".to_string(),
|
||||
"1".to_string(),
|
||||
"1-111".to_string(),
|
||||
"1,2,3,4".to_string(),
|
||||
"1-5,6-7,8-9".to_string(),
|
||||
];
|
||||
let expecteds = vec![
|
||||
vec![(1, 1), (2, 2), (4, 6), (9, 9)],
|
||||
vec![],
|
||||
vec![(1, 1)],
|
||||
vec![(1, 111)],
|
||||
vec![(1, 1), (2, 2), (3, 3), (4, 4)],
|
||||
vec![(1, 5), (6, 7), (8, 9)],
|
||||
];
|
||||
|
||||
for (i, case) in test_cases.into_iter().enumerate() {
|
||||
let range = cpuset::parse_range(case.clone());
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
//! This module contains the implementation of the `devices` cgroup subsystem.
|
||||
//!
|
||||
//!
|
||||
//! See the Kernel's documentation for more information about this subsystem, found at:
|
||||
//! [Documentation/cgroup-v1/devices.txt](https://www.kernel.org/doc/Documentation/cgroup-v1/devices.txt)
|
||||
use std::path::PathBuf;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use {DeviceResource, CgroupError, DeviceResources, Controllers, Controller, Resources, ControllIdentifier, Subsystem};
|
||||
use {
|
||||
CgroupError, ControllIdentifier, Controller, Controllers, DeviceResource, DeviceResources,
|
||||
Resources, Subsystem,
|
||||
};
|
||||
|
||||
/// A controller that allows controlling the `devices` subsystem of a Cgroup.
|
||||
///
|
||||
/// In essence, using the devices controller, it is possible to allow or disallow sets of devices to
|
||||
/// be used by the control group's tasks.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DevicesController{
|
||||
pub struct DevicesController {
|
||||
base: PathBuf,
|
||||
path: PathBuf,
|
||||
}
|
||||
@@ -29,7 +32,9 @@ pub enum DeviceType {
|
||||
}
|
||||
|
||||
impl Default for DeviceType {
|
||||
fn default() -> Self { DeviceType::All }
|
||||
fn default() -> Self {
|
||||
DeviceType::All
|
||||
}
|
||||
}
|
||||
|
||||
impl DeviceType {
|
||||
@@ -124,10 +129,18 @@ impl DevicePermissions {
|
||||
}
|
||||
|
||||
impl Controller for DevicesController {
|
||||
fn control_type(&self) -> Controllers { Controllers::Devices }
|
||||
fn get_path(&self) -> &PathBuf { &self.path }
|
||||
fn get_path_mut(&mut self) -> &mut PathBuf { &mut self.path }
|
||||
fn get_base(&self) -> &PathBuf { &self.base }
|
||||
fn control_type(&self) -> Controllers {
|
||||
Controllers::Devices
|
||||
}
|
||||
fn get_path(&self) -> &PathBuf {
|
||||
&self.path
|
||||
}
|
||||
fn get_path_mut(&mut self) -> &mut PathBuf {
|
||||
&mut self.path
|
||||
}
|
||||
fn get_base(&self) -> &PathBuf {
|
||||
&self.base
|
||||
}
|
||||
|
||||
fn apply(&self, res: &Resources) -> Result<(), CgroupError> {
|
||||
/* get the resources that apply to this controller */
|
||||
@@ -161,7 +174,7 @@ impl<'a> From<&'a Subsystem> for &'a DevicesController {
|
||||
_ => {
|
||||
assert_eq!(1, 0);
|
||||
::std::mem::uninitialized()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -182,13 +195,31 @@ impl DevicesController {
|
||||
///
|
||||
/// When `-1` is passed as `major` or `minor`, the kernel interprets that value as "any",
|
||||
/// meaning that it will match any device.
|
||||
pub fn allow_device(&self, devtype: DeviceType, major: i64, minor: i64, perm: &Vec<DevicePermissions>) -> Result<(), CgroupError> {
|
||||
let perms = perm.iter().map(DevicePermissions::to_char).collect::<String>();
|
||||
let minor = if minor == -1 { "*".to_string() } else { format!("{}", minor) };
|
||||
let major = if major == -1 { "*".to_string() } else { format!("{}", major) };
|
||||
pub fn allow_device(
|
||||
&self,
|
||||
devtype: DeviceType,
|
||||
major: i64,
|
||||
minor: i64,
|
||||
perm: &Vec<DevicePermissions>,
|
||||
) -> Result<(), CgroupError> {
|
||||
let perms = perm
|
||||
.iter()
|
||||
.map(DevicePermissions::to_char)
|
||||
.collect::<String>();
|
||||
let minor = if minor == -1 {
|
||||
"*".to_string()
|
||||
} else {
|
||||
format!("{}", minor)
|
||||
};
|
||||
let major = if major == -1 {
|
||||
"*".to_string()
|
||||
} else {
|
||||
format!("{}", major)
|
||||
};
|
||||
let final_str = format!("{} {}:{} {}", devtype.to_char(), major, minor, perms);
|
||||
self.open_path("devices.allow", true).and_then(|mut file| {
|
||||
file.write_all(final_str.as_ref()).map_err(CgroupError::WriteError)
|
||||
file.write_all(final_str.as_ref())
|
||||
.map_err(CgroupError::WriteError)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -196,13 +227,31 @@ impl DevicesController {
|
||||
///
|
||||
/// When `-1` is passed as `major` or `minor`, the kernel interprets that value as "any",
|
||||
/// meaning that it will match any device.
|
||||
pub fn deny_device(&self, devtype: DeviceType, major: i64, minor: i64, perm: &Vec<DevicePermissions>) -> Result<(), CgroupError> {
|
||||
let perms = perm.iter().map(DevicePermissions::to_char).collect::<String>();
|
||||
let minor = if minor == -1 { "*".to_string() } else { format!("{}", minor) };
|
||||
let major = if major == -1 { "*".to_string() } else { format!("{}", major) };
|
||||
pub fn deny_device(
|
||||
&self,
|
||||
devtype: DeviceType,
|
||||
major: i64,
|
||||
minor: i64,
|
||||
perm: &Vec<DevicePermissions>,
|
||||
) -> Result<(), CgroupError> {
|
||||
let perms = perm
|
||||
.iter()
|
||||
.map(DevicePermissions::to_char)
|
||||
.collect::<String>();
|
||||
let minor = if minor == -1 {
|
||||
"*".to_string()
|
||||
} else {
|
||||
format!("{}", minor)
|
||||
};
|
||||
let major = if major == -1 {
|
||||
"*".to_string()
|
||||
} else {
|
||||
format!("{}", major)
|
||||
};
|
||||
let final_str = format!("{} {}:{} {}", devtype.to_char(), major, minor, perms);
|
||||
self.open_path("devices.deny", true).and_then(|mut file| {
|
||||
file.write_all(final_str.as_ref()).map_err(CgroupError::WriteError)
|
||||
file.write_all(final_str.as_ref())
|
||||
.map_err(CgroupError::WriteError)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
//! This module contains the implementation of the `freezer` cgroup subsystem.
|
||||
//!
|
||||
//!
|
||||
//! See the Kernel's documentation for more information about this subsystem, found at:
|
||||
//! [Documentation/cgroup-v1/freezer-subsystem.txt](https://www.kernel.org/doc/Documentation/cgroup-v1/freezer-subsystem.txt)
|
||||
use std::path::PathBuf;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use {CgroupError, Controllers, Controller, Resources, ControllIdentifier, Subsystem};
|
||||
use {CgroupError, ControllIdentifier, Controller, Controllers, Resources, Subsystem};
|
||||
|
||||
/// A controller that allows controlling the `freezer` subsystem of a Cgroup.
|
||||
///
|
||||
@@ -16,7 +16,7 @@ use {CgroupError, Controllers, Controller, Resources, ControllIdentifier, Subsys
|
||||
/// Note that if the control group is currently in the `Frozen` or `Freezing` state, then no
|
||||
/// processes can be added to it.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FreezerController{
|
||||
pub struct FreezerController {
|
||||
base: PathBuf,
|
||||
path: PathBuf,
|
||||
}
|
||||
@@ -32,10 +32,18 @@ pub enum FreezerState {
|
||||
}
|
||||
|
||||
impl Controller for FreezerController {
|
||||
fn control_type(&self) -> Controllers { Controllers::Freezer }
|
||||
fn get_path(&self) -> &PathBuf { &self.path }
|
||||
fn get_path_mut(&mut self) -> &mut PathBuf { &mut self.path }
|
||||
fn get_base(&self) -> &PathBuf { &self.base }
|
||||
fn control_type(&self) -> Controllers {
|
||||
Controllers::Freezer
|
||||
}
|
||||
fn get_path(&self) -> &PathBuf {
|
||||
&self.path
|
||||
}
|
||||
fn get_path_mut(&mut self) -> &mut PathBuf {
|
||||
&mut self.path
|
||||
}
|
||||
fn get_base(&self) -> &PathBuf {
|
||||
&self.base
|
||||
}
|
||||
|
||||
fn apply(&self, _res: &Resources) -> Result<(), CgroupError> {
|
||||
Ok(())
|
||||
@@ -56,7 +64,7 @@ impl<'a> From<&'a Subsystem> for &'a FreezerController {
|
||||
_ => {
|
||||
assert_eq!(1, 0);
|
||||
::std::mem::uninitialized()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,14 +84,16 @@ impl FreezerController {
|
||||
/// Freezes the processes in the control group.
|
||||
pub fn freeze(&self) -> Result<(), CgroupError> {
|
||||
self.open_path("freezer.state", true).and_then(|mut file| {
|
||||
file.write_all("FROZEN".to_string().as_ref()).map_err(CgroupError::WriteError)
|
||||
file.write_all("FROZEN".to_string().as_ref())
|
||||
.map_err(CgroupError::WriteError)
|
||||
})
|
||||
}
|
||||
|
||||
/// Thaws, that is, unfreezes the processes in the control group.
|
||||
pub fn thaw(&self) -> Result<(), CgroupError> {
|
||||
self.open_path("freezer.state", true).and_then(|mut file| {
|
||||
file.write_all("THAWED".to_string().as_ref()).map_err(CgroupError::WriteError)
|
||||
file.write_all("THAWED".to_string().as_ref())
|
||||
.map_err(CgroupError::WriteError)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -3,28 +3,27 @@
|
||||
//! Currently, we only support the cgroupv1 hierarchy, but in the future we will add support for
|
||||
//! the Unified Hierarchy.
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::BufRead;
|
||||
use std::io::BufReader;
|
||||
use std::fs::File;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use blkio::BlkIoController;
|
||||
use cpu::CpuController;
|
||||
use cpuacct::CpuAcctController;
|
||||
use cpuset::CpuSetController;
|
||||
use devices::DevicesController;
|
||||
use freezer::FreezerController;
|
||||
use hugetlb::HugeTlbController;
|
||||
use memory::MemController;
|
||||
use net_cls::NetClsController;
|
||||
use net_prio::NetPrioController;
|
||||
use perf_event::PerfEventController;
|
||||
use pid::PidController;
|
||||
use rdma::RdmaController;
|
||||
use {Controllers, Hierarchy, Subsystem};
|
||||
use ::pid::PidController;
|
||||
use ::memory::MemController;
|
||||
use ::cpuset::CpuSetController;
|
||||
use ::cpuacct::CpuAcctController;
|
||||
use ::cpu::CpuController;
|
||||
use ::freezer::FreezerController;
|
||||
use ::devices::DevicesController;
|
||||
use ::net_cls::NetClsController;
|
||||
use ::blkio::BlkIoController;
|
||||
use ::perf_event::PerfEventController;
|
||||
use ::net_prio::NetPrioController;
|
||||
use ::hugetlb::HugeTlbController;
|
||||
use ::rdma::RdmaController;
|
||||
|
||||
use ::cgroup::Cgroup;
|
||||
|
||||
use cgroup::Cgroup;
|
||||
|
||||
/// The standard, original cgroup implementation. Often referred to as "cgroupv1".
|
||||
pub struct V1 {
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
//! This module contains the implementation of the `hugetlb` cgroup subsystem.
|
||||
//!
|
||||
//!
|
||||
//! See the Kernel's documentation for more information about this subsystem, found at:
|
||||
//! [Documentation/cgroup-v1/hugetlb.txt](https://www.kernel.org/doc/Documentation/cgroup-v1/hugetlb.txt)
|
||||
use std::path::PathBuf;
|
||||
use std::fs::File;
|
||||
use std::io::{Write, Read};
|
||||
use std::io::{Read, Write};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use {CgroupError, HugePageResources, Controllers, Controller, Resources, ControllIdentifier, Subsystem};
|
||||
use CgroupError::*;
|
||||
|
||||
use {
|
||||
CgroupError, ControllIdentifier, Controller, Controllers, HugePageResources, Resources,
|
||||
Subsystem,
|
||||
};
|
||||
|
||||
/// A controller that allows controlling the `hugetlb` subsystem of a Cgroup.
|
||||
///
|
||||
@@ -21,10 +23,18 @@ pub struct HugeTlbController {
|
||||
}
|
||||
|
||||
impl Controller for HugeTlbController {
|
||||
fn control_type(&self) -> Controllers { Controllers::HugeTlb }
|
||||
fn get_path(&self) -> &PathBuf { &self.path }
|
||||
fn get_path_mut(&mut self) -> &mut PathBuf { &mut self.path }
|
||||
fn get_base(&self) -> &PathBuf { &self.base }
|
||||
fn control_type(&self) -> Controllers {
|
||||
Controllers::HugeTlb
|
||||
}
|
||||
fn get_path(&self) -> &PathBuf {
|
||||
&self.path
|
||||
}
|
||||
fn get_path_mut(&mut self) -> &mut PathBuf {
|
||||
&mut self.path
|
||||
}
|
||||
fn get_base(&self) -> &PathBuf {
|
||||
&self.base
|
||||
}
|
||||
|
||||
fn apply(&self, res: &Resources) -> Result<(), CgroupError> {
|
||||
/* get the resources that apply to this controller */
|
||||
@@ -56,7 +66,7 @@ impl<'a> From<&'a Subsystem> for &'a HugeTlbController {
|
||||
_ => {
|
||||
assert_eq!(1, 0);
|
||||
::std::mem::uninitialized()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,8 +120,10 @@ impl HugeTlbController {
|
||||
/// Get the maximum observed usage of memory that is backed by hugepages of a certain size
|
||||
/// (`hugetlb_size`).
|
||||
pub fn max_usage_in_bytes(&self, hugetlb_size: &String) -> Result<u64, CgroupError> {
|
||||
self.open_path(&format!("hugetlb.{}.max_usage_in_bytes", hugetlb_size), false)
|
||||
.and_then(read_u64_from)
|
||||
self.open_path(
|
||||
&format!("hugetlb.{}.max_usage_in_bytes", hugetlb_size),
|
||||
false,
|
||||
).and_then(read_u64_from)
|
||||
}
|
||||
|
||||
/// Set the limit (in bytes) of how much memory can be backed by hugepages of a certain size
|
||||
@@ -119,7 +131,8 @@ impl HugeTlbController {
|
||||
pub fn set_limit_in_bytes(&self, hugetlb_size: &String, limit: u64) -> Result<(), CgroupError> {
|
||||
self.open_path(&format!("hugetlb.{}.limit_in_bytes", hugetlb_size), false)
|
||||
.and_then(|mut file| {
|
||||
file.write_all(limit.to_string().as_ref()).map_err(CgroupError::WriteError)
|
||||
file.write_all(limit.to_string().as_ref())
|
||||
.map_err(CgroupError::WriteError)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
103
src/lib.rs
103
src/lib.rs
@@ -1,35 +1,35 @@
|
||||
use std::path::PathBuf;
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader, Write};
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub mod hierarchies;
|
||||
pub mod pid;
|
||||
pub mod memory;
|
||||
pub mod cpuset;
|
||||
pub mod cpuacct;
|
||||
pub mod cpu;
|
||||
pub mod devices;
|
||||
pub mod cgroup;
|
||||
pub mod freezer;
|
||||
pub mod net_cls;
|
||||
pub mod blkio;
|
||||
pub mod perf_event;
|
||||
pub mod net_prio;
|
||||
pub mod cgroup;
|
||||
pub mod cpu;
|
||||
pub mod cpuacct;
|
||||
pub mod cpuset;
|
||||
pub mod devices;
|
||||
pub mod freezer;
|
||||
pub mod hierarchies;
|
||||
pub mod hugetlb;
|
||||
pub mod memory;
|
||||
pub mod net_cls;
|
||||
pub mod net_prio;
|
||||
pub mod perf_event;
|
||||
pub mod pid;
|
||||
pub mod rdma;
|
||||
|
||||
use pid::PidController;
|
||||
use memory::MemController;
|
||||
use cpuset::CpuSetController;
|
||||
use cpuacct::CpuAcctController;
|
||||
use cpu::CpuController;
|
||||
use freezer::FreezerController;
|
||||
use devices::DevicesController;
|
||||
use net_cls::NetClsController;
|
||||
use blkio::BlkIoController;
|
||||
use perf_event::PerfEventController;
|
||||
use net_prio::NetPrioController;
|
||||
use cpu::CpuController;
|
||||
use cpuacct::CpuAcctController;
|
||||
use cpuset::CpuSetController;
|
||||
use devices::DevicesController;
|
||||
use freezer::FreezerController;
|
||||
use hugetlb::HugeTlbController;
|
||||
use memory::MemController;
|
||||
use net_cls::NetClsController;
|
||||
use net_prio::NetPrioController;
|
||||
use perf_event::PerfEventController;
|
||||
use pid::PidController;
|
||||
use rdma::RdmaController;
|
||||
|
||||
pub use cgroup::Cgroup;
|
||||
@@ -95,22 +95,34 @@ impl PartialEq for CgroupError {
|
||||
match self {
|
||||
CgroupError::WriteError(_) => if let CgroupError::WriteError(_) = other {
|
||||
return true;
|
||||
} else { return false },
|
||||
} else {
|
||||
return false;
|
||||
},
|
||||
CgroupError::ReadError(_) => if let CgroupError::ReadError(_) = other {
|
||||
return true;
|
||||
} else { return false },
|
||||
} else {
|
||||
return false;
|
||||
},
|
||||
CgroupError::ParseError => if let CgroupError::ParseError = other {
|
||||
return true;
|
||||
} else { return false },
|
||||
} else {
|
||||
return false;
|
||||
},
|
||||
CgroupError::InvalidOperation => if let CgroupError::InvalidOperation = other {
|
||||
return true;
|
||||
} else { return false },
|
||||
} else {
|
||||
return false;
|
||||
},
|
||||
CgroupError::InvalidPath => if let CgroupError::InvalidPath = other {
|
||||
return true;
|
||||
} else { return false },
|
||||
} else {
|
||||
return false;
|
||||
},
|
||||
CgroupError::Unknown => if let CgroupError::Unknown = other {
|
||||
return true;
|
||||
} else { return false },
|
||||
} else {
|
||||
return false;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -232,23 +244,25 @@ pub trait Controller {
|
||||
/// Attach a task to this controller.
|
||||
fn add_task(&self, pid: &CgroupPid) -> Result<(), CgroupError> {
|
||||
self.open_path("tasks", true).and_then(|mut file| {
|
||||
file.write_all(pid.pid.to_string().as_ref()).map_err(CgroupError::WriteError)
|
||||
file.write_all(pid.pid.to_string().as_ref())
|
||||
.map_err(CgroupError::WriteError)
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the list of tasks that this controller has.
|
||||
fn tasks(&self) -> Vec<CgroupPid> {
|
||||
self.open_path("tasks", false).and_then(|file| {
|
||||
let bf = BufReader::new(file);
|
||||
let mut v = Vec::new();
|
||||
for line in bf.lines() {
|
||||
if let Ok(line) = line {
|
||||
let n = line.trim().parse().unwrap_or(0u64);
|
||||
v.push(n);
|
||||
self.open_path("tasks", false)
|
||||
.and_then(|file| {
|
||||
let bf = BufReader::new(file);
|
||||
let mut v = Vec::new();
|
||||
for line in bf.lines() {
|
||||
if let Ok(line) = line {
|
||||
let n = line.trim().parse().unwrap_or(0u64);
|
||||
v.push(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(v.into_iter().map(CgroupPid::from).collect())
|
||||
}).unwrap_or(vec![])
|
||||
Ok(v.into_iter().map(CgroupPid::from).collect())
|
||||
}).unwrap_or(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -479,21 +493,16 @@ pub struct CgroupPid {
|
||||
|
||||
impl From<u64> for CgroupPid {
|
||||
fn from(u: u64) -> CgroupPid {
|
||||
CgroupPid {
|
||||
pid: u,
|
||||
}
|
||||
CgroupPid { pid: u }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a std::process::Child> for CgroupPid {
|
||||
fn from(u: &std::process::Child) -> CgroupPid {
|
||||
CgroupPid {
|
||||
pid: u.id() as u64,
|
||||
}
|
||||
CgroupPid { pid: u.id() as u64 }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Subsystem {
|
||||
fn enter(self, path: &String) -> Self {
|
||||
match self {
|
||||
|
||||
389
src/memory.rs
389
src/memory.rs
@@ -1,13 +1,15 @@
|
||||
//! This module contains the implementation of the `memory` cgroup subsystem.
|
||||
//!
|
||||
//!
|
||||
//! See the Kernel's documentation for more information about this subsystem, found at:
|
||||
//! [Documentation/cgroup-v1/memory.txt](https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt)
|
||||
use std::path::PathBuf;
|
||||
use std::io::{Write, Read};
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use {CgroupError, Resources, MemoryResources, Controller, Controllers, Subsystem, ControllIdentifier};
|
||||
use CgroupError::*;
|
||||
use {
|
||||
CgroupError, ControllIdentifier, Controller, Controllers, MemoryResources, Resources, Subsystem,
|
||||
};
|
||||
|
||||
/// A controller that allows controlling the `memory` subsystem of a Cgroup.
|
||||
///
|
||||
@@ -15,7 +17,7 @@ use CgroupError::*;
|
||||
/// of the tasks in the control group. Additonally, one can also set powerful limits on their
|
||||
/// memory usage.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MemController{
|
||||
pub struct MemController {
|
||||
base: PathBuf,
|
||||
path: PathBuf,
|
||||
}
|
||||
@@ -83,55 +85,127 @@ fn parse_numa_stat(s: String) -> Result<NumaStat, CgroupError> {
|
||||
// Parse the number of nodes
|
||||
let nodes = (s.split_whitespace().collect::<Vec<_>>().len() - 8) / 8;
|
||||
let mut ls = s.lines();
|
||||
let total_line = ls.next().unwrap();
|
||||
let file_line = ls.next().unwrap();
|
||||
let anon_line = ls.next().unwrap();
|
||||
let total_line = ls.next().unwrap();
|
||||
let file_line = ls.next().unwrap();
|
||||
let anon_line = ls.next().unwrap();
|
||||
let unevict_line = ls.next().unwrap();
|
||||
let hier_total_line = ls.next().unwrap();
|
||||
let hier_file_line = ls.next().unwrap();
|
||||
let hier_anon_line = ls.next().unwrap();
|
||||
let hier_total_line = ls.next().unwrap();
|
||||
let hier_file_line = ls.next().unwrap();
|
||||
let hier_anon_line = ls.next().unwrap();
|
||||
let hier_unevict_line = ls.next().unwrap();
|
||||
|
||||
Ok(NumaStat {
|
||||
total_pages: total_line.split(|x| x == ' ' || x == '=').collect::<Vec<_>>()[1].parse::<u64>().unwrap_or(0),
|
||||
total_pages: total_line
|
||||
.split(|x| x == ' ' || x == '=')
|
||||
.collect::<Vec<_>>()[1]
|
||||
.parse::<u64>()
|
||||
.unwrap_or(0),
|
||||
total_pages_per_node: {
|
||||
let spl = &total_line.split(" ").collect::<Vec<_>>()[1..];
|
||||
spl.iter().map(|x| x.split("=").collect::<Vec<_>>()[1].parse::<u64>().unwrap_or(0)).collect()
|
||||
spl.iter()
|
||||
.map(|x| {
|
||||
x.split("=").collect::<Vec<_>>()[1]
|
||||
.parse::<u64>()
|
||||
.unwrap_or(0)
|
||||
}).collect()
|
||||
},
|
||||
file_pages: file_line.split(|x| x == ' ' || x == '=').collect::<Vec<_>>()[1].parse::<u64>().unwrap_or(0),
|
||||
file_pages: file_line
|
||||
.split(|x| x == ' ' || x == '=')
|
||||
.collect::<Vec<_>>()[1]
|
||||
.parse::<u64>()
|
||||
.unwrap_or(0),
|
||||
file_pages_per_node: {
|
||||
let spl = &file_line.split(" ").collect::<Vec<_>>()[1..];
|
||||
spl.iter().map(|x| x.split("=").collect::<Vec<_>>()[1].parse::<u64>().unwrap_or(0)).collect()
|
||||
spl.iter()
|
||||
.map(|x| {
|
||||
x.split("=").collect::<Vec<_>>()[1]
|
||||
.parse::<u64>()
|
||||
.unwrap_or(0)
|
||||
}).collect()
|
||||
},
|
||||
anon_pages: anon_line.split(|x| x == ' ' || x == '=').collect::<Vec<_>>()[1].parse::<u64>().unwrap_or(0),
|
||||
anon_pages: anon_line
|
||||
.split(|x| x == ' ' || x == '=')
|
||||
.collect::<Vec<_>>()[1]
|
||||
.parse::<u64>()
|
||||
.unwrap_or(0),
|
||||
anon_pages_per_node: {
|
||||
let spl = &anon_line.split(" ").collect::<Vec<_>>()[1..];
|
||||
spl.iter().map(|x| x.split("=").collect::<Vec<_>>()[1].parse::<u64>().unwrap_or(0)).collect()
|
||||
spl.iter()
|
||||
.map(|x| {
|
||||
x.split("=").collect::<Vec<_>>()[1]
|
||||
.parse::<u64>()
|
||||
.unwrap_or(0)
|
||||
}).collect()
|
||||
},
|
||||
unevictable_pages: unevict_line.split(|x| x == ' ' || x == '=').collect::<Vec<_>>()[1].parse::<u64>().unwrap_or(0),
|
||||
unevictable_pages: unevict_line
|
||||
.split(|x| x == ' ' || x == '=')
|
||||
.collect::<Vec<_>>()[1]
|
||||
.parse::<u64>()
|
||||
.unwrap_or(0),
|
||||
unevictable_pages_per_node: {
|
||||
let spl = &unevict_line.split(" ").collect::<Vec<_>>()[1..];
|
||||
spl.iter().map(|x| x.split("=").collect::<Vec<_>>()[1].parse::<u64>().unwrap_or(0)).collect()
|
||||
spl.iter()
|
||||
.map(|x| {
|
||||
x.split("=").collect::<Vec<_>>()[1]
|
||||
.parse::<u64>()
|
||||
.unwrap_or(0)
|
||||
}).collect()
|
||||
},
|
||||
hierarchical_total_pages: hier_total_line.split(|x| x == ' ' || x == '=').collect::<Vec<_>>()[1].parse::<u64>().unwrap_or(0),
|
||||
hierarchical_total_pages: hier_total_line
|
||||
.split(|x| x == ' ' || x == '=')
|
||||
.collect::<Vec<_>>()[1]
|
||||
.parse::<u64>()
|
||||
.unwrap_or(0),
|
||||
hierarchical_total_pages_per_node: {
|
||||
let spl = &hier_total_line.split(" ").collect::<Vec<_>>()[1..];
|
||||
spl.iter().map(|x| x.split("=").collect::<Vec<_>>()[1].parse::<u64>().unwrap_or(0)).collect()
|
||||
spl.iter()
|
||||
.map(|x| {
|
||||
x.split("=").collect::<Vec<_>>()[1]
|
||||
.parse::<u64>()
|
||||
.unwrap_or(0)
|
||||
}).collect()
|
||||
},
|
||||
hierarchical_file_pages: hier_file_line.split(|x| x == ' ' || x == '=').collect::<Vec<_>>()[1].parse::<u64>().unwrap_or(0),
|
||||
hierarchical_file_pages: hier_file_line
|
||||
.split(|x| x == ' ' || x == '=')
|
||||
.collect::<Vec<_>>()[1]
|
||||
.parse::<u64>()
|
||||
.unwrap_or(0),
|
||||
hierarchical_file_pages_per_node: {
|
||||
let spl = &hier_file_line.split(" ").collect::<Vec<_>>()[1..];
|
||||
spl.iter().map(|x| x.split("=").collect::<Vec<_>>()[1].parse::<u64>().unwrap_or(0)).collect()
|
||||
spl.iter()
|
||||
.map(|x| {
|
||||
x.split("=").collect::<Vec<_>>()[1]
|
||||
.parse::<u64>()
|
||||
.unwrap_or(0)
|
||||
}).collect()
|
||||
},
|
||||
hierarchical_anon_pages: hier_anon_line.split(|x| x == ' ' || x == '=').collect::<Vec<_>>()[1].parse::<u64>().unwrap_or(0),
|
||||
hierarchical_anon_pages: hier_anon_line
|
||||
.split(|x| x == ' ' || x == '=')
|
||||
.collect::<Vec<_>>()[1]
|
||||
.parse::<u64>()
|
||||
.unwrap_or(0),
|
||||
hierarchical_anon_pages_per_node: {
|
||||
let spl = &hier_anon_line.split(" ").collect::<Vec<_>>()[1..];
|
||||
spl.iter().map(|x| x.split("=").collect::<Vec<_>>()[1].parse::<u64>().unwrap_or(0)).collect()
|
||||
spl.iter()
|
||||
.map(|x| {
|
||||
x.split("=").collect::<Vec<_>>()[1]
|
||||
.parse::<u64>()
|
||||
.unwrap_or(0)
|
||||
}).collect()
|
||||
},
|
||||
hierarchical_unevictable_pages: hier_unevict_line.split(|x| x == ' ' || x == '=').collect::<Vec<_>>()[1].parse::<u64>().unwrap_or(0),
|
||||
hierarchical_unevictable_pages: hier_unevict_line
|
||||
.split(|x| x == ' ' || x == '=')
|
||||
.collect::<Vec<_>>()[1]
|
||||
.parse::<u64>()
|
||||
.unwrap_or(0),
|
||||
hierarchical_unevictable_pages_per_node: {
|
||||
let spl = &hier_unevict_line.split(" ").collect::<Vec<_>>()[1..];
|
||||
spl.iter().map(|x| x.split("=").collect::<Vec<_>>()[1].parse::<u64>().unwrap_or(0)).collect()
|
||||
spl.iter()
|
||||
.map(|x| {
|
||||
x.split("=").collect::<Vec<_>>()[1]
|
||||
.parse::<u64>()
|
||||
.unwrap_or(0)
|
||||
}).collect()
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -177,9 +251,10 @@ pub struct MemoryStat {
|
||||
}
|
||||
|
||||
fn parse_memory_stat(s: String) -> Result<MemoryStat, CgroupError> {
|
||||
let sp: Vec<&str> = s.split_whitespace()
|
||||
.filter(|x| x.parse::<u64>().is_ok())
|
||||
.collect();
|
||||
let sp: Vec<&str> = s
|
||||
.split_whitespace()
|
||||
.filter(|x| x.parse::<u64>().is_ok())
|
||||
.collect();
|
||||
|
||||
let mut spl = sp.iter();
|
||||
Ok(MemoryStat {
|
||||
@@ -317,10 +392,18 @@ pub struct Kmem {
|
||||
}
|
||||
|
||||
impl Controller for MemController {
|
||||
fn control_type(&self) -> Controllers { Controllers::Mem }
|
||||
fn get_path(&self) -> &PathBuf { &self.path }
|
||||
fn get_path_mut(&mut self) -> &mut PathBuf { &mut self.path }
|
||||
fn get_base(&self) -> &PathBuf { &self.base }
|
||||
fn control_type(&self) -> Controllers {
|
||||
Controllers::Mem
|
||||
}
|
||||
fn get_path(&self) -> &PathBuf {
|
||||
&self.path
|
||||
}
|
||||
fn get_path_mut(&mut self) -> &mut PathBuf {
|
||||
&mut self.path
|
||||
}
|
||||
fn get_base(&self) -> &PathBuf {
|
||||
&self.base
|
||||
}
|
||||
|
||||
fn apply(&self, res: &Resources) -> Result<(), CgroupError> {
|
||||
/* get the resources that apply to this controller */
|
||||
@@ -357,53 +440,79 @@ impl MemController {
|
||||
/// kernel Documentation and/or sources.
|
||||
pub fn memory_stat(&self) -> Memory {
|
||||
Memory {
|
||||
fail_cnt: self.open_path("memory.failcnt", false)
|
||||
.and_then(read_u64_from).unwrap_or(0),
|
||||
limit_in_bytes: self.open_path("memory.limit_in_bytes", false)
|
||||
.and_then(read_u64_from).unwrap_or(0),
|
||||
usage_in_bytes: self.open_path("memory.usage_in_bytes", false)
|
||||
.and_then(read_u64_from).unwrap_or(0),
|
||||
max_usage_in_bytes: self.open_path("memory.max_usage_in_bytes", false)
|
||||
.and_then(read_u64_from).unwrap_or(0),
|
||||
move_charge_at_immigrate: self.open_path("memory.move_charge_at_immigrate", false)
|
||||
.and_then(read_u64_from).unwrap_or(0),
|
||||
numa_stat: self.open_path("memory.numa_stat", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_numa_stat)
|
||||
.unwrap_or(NumaStat::default()),
|
||||
oom_control: self.open_path("memory.oom_control", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_oom_control)
|
||||
.unwrap_or(OomControl::default()),
|
||||
soft_limit_in_bytes: self.open_path("memory.soft_limit_in_bytes", false)
|
||||
.and_then(read_u64_from)
|
||||
.unwrap_or(0),
|
||||
stat: self.open_path("memory.stat", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_memory_stat)
|
||||
.unwrap_or(MemoryStat::default()),
|
||||
swappiness: self.open_path("memory.swappiness", false)
|
||||
.and_then(read_u64_from)
|
||||
.unwrap_or(0),
|
||||
use_hierarchy: self.open_path("memory.use_hierarchy", false)
|
||||
.and_then(read_u64_from)
|
||||
.unwrap_or(0)
|
||||
fail_cnt: self
|
||||
.open_path("memory.failcnt", false)
|
||||
.and_then(read_u64_from)
|
||||
.unwrap_or(0),
|
||||
limit_in_bytes: self
|
||||
.open_path("memory.limit_in_bytes", false)
|
||||
.and_then(read_u64_from)
|
||||
.unwrap_or(0),
|
||||
usage_in_bytes: self
|
||||
.open_path("memory.usage_in_bytes", false)
|
||||
.and_then(read_u64_from)
|
||||
.unwrap_or(0),
|
||||
max_usage_in_bytes: self
|
||||
.open_path("memory.max_usage_in_bytes", false)
|
||||
.and_then(read_u64_from)
|
||||
.unwrap_or(0),
|
||||
move_charge_at_immigrate: self
|
||||
.open_path("memory.move_charge_at_immigrate", false)
|
||||
.and_then(read_u64_from)
|
||||
.unwrap_or(0),
|
||||
numa_stat: self
|
||||
.open_path("memory.numa_stat", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_numa_stat)
|
||||
.unwrap_or(NumaStat::default()),
|
||||
oom_control: self
|
||||
.open_path("memory.oom_control", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_oom_control)
|
||||
.unwrap_or(OomControl::default()),
|
||||
soft_limit_in_bytes: self
|
||||
.open_path("memory.soft_limit_in_bytes", false)
|
||||
.and_then(read_u64_from)
|
||||
.unwrap_or(0),
|
||||
stat: self
|
||||
.open_path("memory.stat", false)
|
||||
.and_then(read_string_from)
|
||||
.and_then(parse_memory_stat)
|
||||
.unwrap_or(MemoryStat::default()),
|
||||
swappiness: self
|
||||
.open_path("memory.swappiness", false)
|
||||
.and_then(read_u64_from)
|
||||
.unwrap_or(0),
|
||||
use_hierarchy: self
|
||||
.open_path("memory.use_hierarchy", false)
|
||||
.and_then(read_u64_from)
|
||||
.unwrap_or(0),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gathers information about the kernel memory usage of the control group's tasks.
|
||||
pub fn kmem_stat(&self) -> Kmem {
|
||||
Kmem {
|
||||
fail_cnt: self.open_path("memory.kmem.failcnt", false)
|
||||
.and_then(read_u64_from).unwrap_or(0),
|
||||
limit_in_bytes: self.open_path("memory.kmem.limit_in_bytes", false)
|
||||
.and_then(read_u64_from).unwrap_or(0),
|
||||
usage_in_bytes: self.open_path("memory.kmem.usage_in_bytes", false)
|
||||
.and_then(read_u64_from).unwrap_or(0),
|
||||
max_usage_in_bytes: self.open_path("memory.kmem.max_usage_in_bytes", false)
|
||||
.and_then(read_u64_from).unwrap_or(0),
|
||||
slabinfo: self.open_path("memory.kmem.slabinfo", false)
|
||||
.and_then(read_string_from).unwrap_or("".to_string()),
|
||||
fail_cnt: self
|
||||
.open_path("memory.kmem.failcnt", false)
|
||||
.and_then(read_u64_from)
|
||||
.unwrap_or(0),
|
||||
limit_in_bytes: self
|
||||
.open_path("memory.kmem.limit_in_bytes", false)
|
||||
.and_then(read_u64_from)
|
||||
.unwrap_or(0),
|
||||
usage_in_bytes: self
|
||||
.open_path("memory.kmem.usage_in_bytes", false)
|
||||
.and_then(read_u64_from)
|
||||
.unwrap_or(0),
|
||||
max_usage_in_bytes: self
|
||||
.open_path("memory.kmem.max_usage_in_bytes", false)
|
||||
.and_then(read_u64_from)
|
||||
.unwrap_or(0),
|
||||
slabinfo: self
|
||||
.open_path("memory.kmem.slabinfo", false)
|
||||
.and_then(read_string_from)
|
||||
.unwrap_or("".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -411,14 +520,22 @@ impl MemController {
|
||||
/// TCP-related.
|
||||
pub fn kmem_tcp_stat(&self) -> Tcp {
|
||||
Tcp {
|
||||
fail_cnt: self.open_path("memory.kmem.tcp.failcnt", false)
|
||||
.and_then(read_u64_from).unwrap_or(0),
|
||||
limit_in_bytes: self.open_path("memory.kmem.tcp.limit_in_bytes", false)
|
||||
.and_then(read_u64_from).unwrap_or(0),
|
||||
usage_in_bytes: self.open_path("memory.kmem.tcp.usage_in_bytes", false)
|
||||
.and_then(read_u64_from).unwrap_or(0),
|
||||
max_usage_in_bytes: self.open_path("memory.kmem.tcp.max_usage_in_bytes", false)
|
||||
.and_then(read_u64_from).unwrap_or(0),
|
||||
fail_cnt: self
|
||||
.open_path("memory.kmem.tcp.failcnt", false)
|
||||
.and_then(read_u64_from)
|
||||
.unwrap_or(0),
|
||||
limit_in_bytes: self
|
||||
.open_path("memory.kmem.tcp.limit_in_bytes", false)
|
||||
.and_then(read_u64_from)
|
||||
.unwrap_or(0),
|
||||
usage_in_bytes: self
|
||||
.open_path("memory.kmem.tcp.usage_in_bytes", false)
|
||||
.and_then(read_u64_from)
|
||||
.unwrap_or(0),
|
||||
max_usage_in_bytes: self
|
||||
.open_path("memory.kmem.tcp.max_usage_in_bytes", false)
|
||||
.and_then(read_u64_from)
|
||||
.unwrap_or(0),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,65 +543,83 @@ impl MemController {
|
||||
/// (if any).
|
||||
pub fn memswap(&self) -> MemSwap {
|
||||
MemSwap {
|
||||
fail_cnt: self.open_path("memory.memsw.failcnt", false)
|
||||
.and_then(read_u64_from).unwrap_or(0),
|
||||
limit_in_bytes: self.open_path("memory.memsw.limit_in_bytes", false)
|
||||
.and_then(read_u64_from).unwrap_or(0),
|
||||
usage_in_bytes: self.open_path("memory.memsw.usage_in_bytes", false)
|
||||
.and_then(read_u64_from).unwrap_or(0),
|
||||
max_usage_in_bytes: self.open_path("memory.memsw.max_usage_in_bytes", false)
|
||||
.and_then(read_u64_from).unwrap_or(0),
|
||||
fail_cnt: self
|
||||
.open_path("memory.memsw.failcnt", false)
|
||||
.and_then(read_u64_from)
|
||||
.unwrap_or(0),
|
||||
limit_in_bytes: self
|
||||
.open_path("memory.memsw.limit_in_bytes", false)
|
||||
.and_then(read_u64_from)
|
||||
.unwrap_or(0),
|
||||
usage_in_bytes: self
|
||||
.open_path("memory.memsw.usage_in_bytes", false)
|
||||
.and_then(read_u64_from)
|
||||
.unwrap_or(0),
|
||||
max_usage_in_bytes: self
|
||||
.open_path("memory.memsw.max_usage_in_bytes", false)
|
||||
.and_then(read_u64_from)
|
||||
.unwrap_or(0),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the memory usage limit of the control group, in bytes.
|
||||
pub fn set_limit(&self, limit: u64) -> Result<(), CgroupError> {
|
||||
self.open_path("memory.limit_in_bytes", true).and_then(|mut file| {
|
||||
file.write_all(limit.to_string().as_ref()).map_err(CgroupError::WriteError)
|
||||
})
|
||||
self.open_path("memory.limit_in_bytes", true)
|
||||
.and_then(|mut file| {
|
||||
file.write_all(limit.to_string().as_ref())
|
||||
.map_err(CgroupError::WriteError)
|
||||
})
|
||||
}
|
||||
|
||||
/// Set the kernel memory limit of the control group, in bytes.
|
||||
pub fn set_kmem_limit(&self, limit: u64) -> Result<(), CgroupError> {
|
||||
self.open_path("memory.kmem.limit_in_bytes", true).and_then(|mut file| {
|
||||
file.write_all(limit.to_string().as_ref()).map_err(CgroupError::WriteError)
|
||||
})
|
||||
self.open_path("memory.kmem.limit_in_bytes", true)
|
||||
.and_then(|mut file| {
|
||||
file.write_all(limit.to_string().as_ref())
|
||||
.map_err(CgroupError::WriteError)
|
||||
})
|
||||
}
|
||||
|
||||
/// Set the memory+swap limit of the control group, in bytes.
|
||||
pub fn set_memswap_limit(&self, limit: u64) -> Result<(), CgroupError> {
|
||||
self.open_path("memory.memsw.limit_in_bytes", true).and_then(|mut file| {
|
||||
file.write_all(limit.to_string().as_ref()).map_err(CgroupError::WriteError)
|
||||
})
|
||||
self.open_path("memory.memsw.limit_in_bytes", true)
|
||||
.and_then(|mut file| {
|
||||
file.write_all(limit.to_string().as_ref())
|
||||
.map_err(CgroupError::WriteError)
|
||||
})
|
||||
}
|
||||
|
||||
/// Set how much kernel memory can be used for TCP-related buffers by the control group.
|
||||
pub fn set_tcp_limit(&self, limit: u64) -> Result<(), CgroupError> {
|
||||
self.open_path("memory.kmem.tcp.limit_in_bytes", true).and_then(|mut file| {
|
||||
file.write_all(limit.to_string().as_ref()).map_err(CgroupError::WriteError)
|
||||
})
|
||||
self.open_path("memory.kmem.tcp.limit_in_bytes", true)
|
||||
.and_then(|mut file| {
|
||||
file.write_all(limit.to_string().as_ref())
|
||||
.map_err(CgroupError::WriteError)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/// Set the soft limit of the control group, in bytes.
|
||||
///
|
||||
/// This limit is enforced when the system is nearing OOM conditions. Contrast this with the
|
||||
/// hard limit, which is _always_ enforced.
|
||||
pub fn set_soft_limit(&self, limit: u64) -> Result<(), CgroupError> {
|
||||
self.open_path("memory.soft_limit_in_bytes", true).and_then(|mut file| {
|
||||
file.write_all(limit.to_string().as_ref()).map_err(CgroupError::WriteError)
|
||||
})
|
||||
self.open_path("memory.soft_limit_in_bytes", true)
|
||||
.and_then(|mut file| {
|
||||
file.write_all(limit.to_string().as_ref())
|
||||
.map_err(CgroupError::WriteError)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/// Set how likely the kernel is to swap out parts of the address space used by the control
|
||||
/// group.
|
||||
///
|
||||
/// Note that a value of zero does not imply that the process will not be swapped out.
|
||||
pub fn set_swappiness(&self, swp: u64) -> Result<(), CgroupError> {
|
||||
self.open_path("memory.swappiness", true).and_then(|mut file| {
|
||||
file.write_all(swp.to_string().as_ref()).map_err(CgroupError::WriteError)
|
||||
})
|
||||
self.open_path("memory.swappiness", true)
|
||||
.and_then(|mut file| {
|
||||
file.write_all(swp.to_string().as_ref())
|
||||
.map_err(CgroupError::WriteError)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -502,7 +637,7 @@ impl<'a> From<&'a Subsystem> for &'a MemController {
|
||||
_ => {
|
||||
assert_eq!(1, 0);
|
||||
::std::mem::uninitialized()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -526,7 +661,9 @@ fn read_string_from(mut file: File) -> Result<String, CgroupError> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use memory::{MemoryStat, parse_memory_stat, NumaStat, parse_oom_control, OomControl, parse_numa_stat};
|
||||
use memory::{
|
||||
parse_memory_stat, parse_numa_stat, parse_oom_control, MemoryStat, NumaStat, OomControl,
|
||||
};
|
||||
const good_value: &str = "\
|
||||
total=51189 N0=51189 N1=123
|
||||
file=50175 N0=50175 N1=123
|
||||
@@ -585,7 +722,8 @@ total_unevictable 81920
|
||||
|
||||
#[test]
|
||||
fn test_parse_numa_stat() {
|
||||
assert_eq!(parse_numa_stat(good_value.to_string()),
|
||||
assert_eq!(
|
||||
parse_numa_stat(good_value.to_string()),
|
||||
Ok(NumaStat {
|
||||
total_pages: 51189,
|
||||
total_pages_per_node: vec![51189, 123],
|
||||
@@ -604,22 +742,26 @@ total_unevictable 81920
|
||||
hierarchical_anon_pages_per_node: vec![770402, 123],
|
||||
hierarchical_unevictable_pages: 20,
|
||||
hierarchical_unevictable_pages_per_node: vec![20, 123],
|
||||
}));
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_oom_control() {
|
||||
assert_eq!(parse_oom_control(good_oomcontrol_val.to_string()),
|
||||
Ok(OomControl {
|
||||
oom_kill_disable: false,
|
||||
under_oom: true,
|
||||
oom_kill: 1337,
|
||||
}));
|
||||
assert_eq!(
|
||||
parse_oom_control(good_oomcontrol_val.to_string()),
|
||||
Ok(OomControl {
|
||||
oom_kill_disable: false,
|
||||
under_oom: true,
|
||||
oom_kill: 1337,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_memory_stat() {
|
||||
assert_eq!(parse_memory_stat(good_memorystat_val.to_string()),
|
||||
assert_eq!(
|
||||
parse_memory_stat(good_memorystat_val.to_string()),
|
||||
Ok(MemoryStat {
|
||||
cache: 178880512,
|
||||
rss: 4206592,
|
||||
@@ -657,6 +799,7 @@ total_unevictable 81920
|
||||
total_inactive_file: 1272135680,
|
||||
total_active_file: 2338816000,
|
||||
total_unevictable: 81920,
|
||||
}));
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
//! This module contains the implementation of the `net_cls` cgroup subsystem.
|
||||
//!
|
||||
//!
|
||||
//! See the Kernel's documentation for more information about this subsystem, found at:
|
||||
//! [Documentation/cgroup-v1/net_cls.txt](https://www.kernel.org/doc/Documentation/cgroup-v1/net_cls.txt)
|
||||
use std::path::PathBuf;
|
||||
use std::io::{Read, Write};
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use {CgroupError, NetworkResources, Controllers, Controller, Resources, ControllIdentifier, Subsystem};
|
||||
use CgroupError::*;
|
||||
use {
|
||||
CgroupError, ControllIdentifier, Controller, Controllers, NetworkResources, Resources,
|
||||
Subsystem,
|
||||
};
|
||||
|
||||
/// A controller that allows controlling the `net_cls` subsystem of a Cgroup.
|
||||
///
|
||||
@@ -21,10 +24,18 @@ pub struct NetClsController {
|
||||
}
|
||||
|
||||
impl Controller for NetClsController {
|
||||
fn control_type(&self) -> Controllers { Controllers::NetCls }
|
||||
fn get_path(&self) -> &PathBuf { &self.path }
|
||||
fn get_path_mut(&mut self) -> &mut PathBuf { &mut self.path }
|
||||
fn get_base(&self) -> &PathBuf { &self.base }
|
||||
fn control_type(&self) -> Controllers {
|
||||
Controllers::NetCls
|
||||
}
|
||||
fn get_path(&self) -> &PathBuf {
|
||||
&self.path
|
||||
}
|
||||
fn get_path_mut(&mut self) -> &mut PathBuf {
|
||||
&mut self.path
|
||||
}
|
||||
fn get_base(&self) -> &PathBuf {
|
||||
&self.base
|
||||
}
|
||||
|
||||
fn apply(&self, res: &Resources) -> Result<(), CgroupError> {
|
||||
/* get the resources that apply to this controller */
|
||||
@@ -54,7 +65,7 @@ impl<'a> From<&'a Subsystem> for &'a NetClsController {
|
||||
_ => {
|
||||
assert_eq!(1, 0);
|
||||
::std::mem::uninitialized()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,19 +89,19 @@ impl NetClsController {
|
||||
path: root,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Set the network class id of the outgoing packets of the control group's tasks.
|
||||
pub fn set_class(&self, class: u64) -> Result<(), CgroupError> {
|
||||
self.open_path("net_cls.classid", true).and_then(|mut file| {
|
||||
let s = format!("{:#08X}", class);
|
||||
file.write_all(s.as_ref()).map_err(CgroupError::WriteError)
|
||||
})
|
||||
self.open_path("net_cls.classid", true)
|
||||
.and_then(|mut file| {
|
||||
let s = format!("{:#08X}", class);
|
||||
file.write_all(s.as_ref()).map_err(CgroupError::WriteError)
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the network class id of the outgoing packets of the control group's tasks.
|
||||
pub fn get_class(&self) -> Result<u64, CgroupError> {
|
||||
self.open_path("net_cls.classid", false).and_then(|file| {
|
||||
read_u64_from(file)
|
||||
})
|
||||
self.open_path("net_cls.classid", false)
|
||||
.and_then(|file| read_u64_from(file))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
//! This module contains the implementation of the `net_prio` cgroup subsystem.
|
||||
//!
|
||||
//!
|
||||
//! See the Kernel's documentation for more information about this subsystem, found at:
|
||||
//! [Documentation/cgroup-v1/net_prio.txt](https://www.kernel.org/doc/Documentation/cgroup-v1/net_prio.txt)
|
||||
use std::path::PathBuf;
|
||||
use std::io::{BufReader, BufRead, Write, Read};
|
||||
use std::fs::File;
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader, Read, Write};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use {CgroupError, NetworkResources, Controllers, Controller, Resources, ControllIdentifier, Subsystem};
|
||||
use CgroupError::*;
|
||||
use {
|
||||
CgroupError, ControllIdentifier, Controller, Controllers, NetworkResources, Resources,
|
||||
Subsystem,
|
||||
};
|
||||
|
||||
/// A controller that allows controlling the `net_prio` subsystem of a Cgroup.
|
||||
///
|
||||
@@ -22,10 +25,18 @@ pub struct NetPrioController {
|
||||
}
|
||||
|
||||
impl Controller for NetPrioController {
|
||||
fn control_type(&self) -> Controllers { Controllers::NetPrio }
|
||||
fn get_path(&self) -> &PathBuf { &self.path }
|
||||
fn get_path_mut(&mut self) -> &mut PathBuf { &mut self.path }
|
||||
fn get_base(&self) -> &PathBuf { &self.base }
|
||||
fn control_type(&self) -> Controllers {
|
||||
Controllers::NetPrio
|
||||
}
|
||||
fn get_path(&self) -> &PathBuf {
|
||||
&self.path
|
||||
}
|
||||
fn get_path_mut(&mut self) -> &mut PathBuf {
|
||||
&mut self.path
|
||||
}
|
||||
fn get_base(&self) -> &PathBuf {
|
||||
&self.base
|
||||
}
|
||||
|
||||
fn apply(&self, res: &Resources) -> Result<(), CgroupError> {
|
||||
/* get the resources that apply to this controller */
|
||||
@@ -55,7 +66,7 @@ impl<'a> From<&'a Subsystem> for &'a NetPrioController {
|
||||
_ => {
|
||||
assert_eq!(1, 0);
|
||||
::std::mem::uninitialized()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -89,38 +100,41 @@ impl NetPrioController {
|
||||
|
||||
/// A map of priorities for each network interface.
|
||||
pub fn ifpriomap(&self) -> Result<HashMap<String, u64>, CgroupError> {
|
||||
self.open_path("net_prio.ifpriomap", false) .and_then(|file| {
|
||||
let bf = BufReader::new(file);
|
||||
bf.lines().fold(Ok(HashMap::new()), |acc, line| {
|
||||
if acc.is_err() {
|
||||
acc
|
||||
} else {
|
||||
let mut acc = acc.unwrap();
|
||||
let l = line.unwrap();
|
||||
let mut sp = l.split_whitespace();
|
||||
let ifname = sp.nth(0);
|
||||
let ifprio = sp.nth(1);
|
||||
if ifname.is_none() || ifprio.is_none() {
|
||||
Err(CgroupError::ParseError)
|
||||
self.open_path("net_prio.ifpriomap", false)
|
||||
.and_then(|file| {
|
||||
let bf = BufReader::new(file);
|
||||
bf.lines().fold(Ok(HashMap::new()), |acc, line| {
|
||||
if acc.is_err() {
|
||||
acc
|
||||
} else {
|
||||
let ifname = ifname.unwrap();
|
||||
let ifprio = ifprio.unwrap().trim().parse();
|
||||
if ifprio.is_err() {
|
||||
let mut acc = acc.unwrap();
|
||||
let l = line.unwrap();
|
||||
let mut sp = l.split_whitespace();
|
||||
let ifname = sp.nth(0);
|
||||
let ifprio = sp.nth(1);
|
||||
if ifname.is_none() || ifprio.is_none() {
|
||||
Err(CgroupError::ParseError)
|
||||
} else {
|
||||
acc.insert(ifname.to_string(), ifprio.unwrap());
|
||||
Ok(acc)
|
||||
let ifname = ifname.unwrap();
|
||||
let ifprio = ifprio.unwrap().trim().parse();
|
||||
if ifprio.is_err() {
|
||||
Err(CgroupError::ParseError)
|
||||
} else {
|
||||
acc.insert(ifname.to_string(), ifprio.unwrap());
|
||||
Ok(acc)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Set the priority of the network traffic on `eif` to be `prio`.
|
||||
pub fn set_if_prio(&self, eif: &String, prio: u64) -> Result<(), CgroupError> {
|
||||
self.open_path("net_prio.ifpriomap", true).and_then(|mut file| {
|
||||
file.write_all(format!("{} {}", eif, prio).as_ref()).map_err(CgroupError::WriteError)
|
||||
})
|
||||
self.open_path("net_prio.ifpriomap", true)
|
||||
.and_then(|mut file| {
|
||||
file.write_all(format!("{} {}", eif, prio).as_ref())
|
||||
.map_err(CgroupError::WriteError)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
//! This module contains the implementation of the `perf_event` cgroup subsystem.
|
||||
//!
|
||||
//!
|
||||
//! See the Kernel's documentation for more information about this subsystem, found at:
|
||||
//! [tools/perf/Documentation/perf-record.txt](https://raw.githubusercontent.com/torvalds/linux/master/tools/perf/Documentation/perf-record.txt)
|
||||
use std::path::PathBuf;
|
||||
|
||||
use {CgroupError, Controllers, Controller, Resources, ControllIdentifier, Subsystem};
|
||||
use {CgroupError, ControllIdentifier, Controller, Controllers, Resources, Subsystem};
|
||||
|
||||
/// A controller that allows controlling the `perf_event` subsystem of a Cgroup.
|
||||
///
|
||||
@@ -17,10 +17,18 @@ pub struct PerfEventController {
|
||||
}
|
||||
|
||||
impl Controller for PerfEventController {
|
||||
fn control_type(&self) -> Controllers { Controllers::PerfEvent }
|
||||
fn get_path(&self) -> &PathBuf { &self.path }
|
||||
fn get_path_mut(&mut self) -> &mut PathBuf { &mut self.path }
|
||||
fn get_base(&self) -> &PathBuf { &self.base }
|
||||
fn control_type(&self) -> Controllers {
|
||||
Controllers::PerfEvent
|
||||
}
|
||||
fn get_path(&self) -> &PathBuf {
|
||||
&self.path
|
||||
}
|
||||
fn get_path_mut(&mut self) -> &mut PathBuf {
|
||||
&mut self.path
|
||||
}
|
||||
fn get_base(&self) -> &PathBuf {
|
||||
&self.base
|
||||
}
|
||||
|
||||
fn apply(&self, _res: &Resources) -> Result<(), CgroupError> {
|
||||
Ok(())
|
||||
@@ -41,7 +49,7 @@ impl<'a> From<&'a Subsystem> for &'a PerfEventController {
|
||||
_ => {
|
||||
assert_eq!(1, 0);
|
||||
::std::mem::uninitialized()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
47
src/pid.rs
47
src/pid.rs
@@ -1,13 +1,15 @@
|
||||
//! This module contains the implementation of the `pids` cgroup subsystem.
|
||||
//!
|
||||
//!
|
||||
//! See the Kernel's documentation for more information about this subsystem, found at:
|
||||
//! [Documentation/cgroups-v1/pids.txt](https://www.kernel.org/doc/Documentation/cgroup-v1/pids.txt)
|
||||
use std::path::PathBuf;
|
||||
use std::io::{Write, Read};
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use {CgroupError, Resources, PidResources, Controller, ControllIdentifier, Subsystem, Controllers};
|
||||
use CgroupError::*;
|
||||
use {
|
||||
CgroupError, ControllIdentifier, Controller, Controllers, PidResources, Resources, Subsystem,
|
||||
};
|
||||
|
||||
/// A controller that allows controlling the `pids` subsystem of a Cgroup.
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -33,10 +35,18 @@ impl Default for PidMax {
|
||||
}
|
||||
|
||||
impl Controller for PidController {
|
||||
fn control_type(&self) -> Controllers { Controllers::Pids }
|
||||
fn get_path(&self) -> &PathBuf { &self.path }
|
||||
fn get_path_mut(&mut self) -> &mut PathBuf { &mut self.path }
|
||||
fn get_base(&self) -> &PathBuf { &self.base }
|
||||
fn control_type(&self) -> Controllers {
|
||||
Controllers::Pids
|
||||
}
|
||||
fn get_path(&self) -> &PathBuf {
|
||||
&self.path
|
||||
}
|
||||
fn get_path_mut(&mut self) -> &mut PathBuf {
|
||||
&mut self.path
|
||||
}
|
||||
fn get_base(&self) -> &PathBuf {
|
||||
&self.base
|
||||
}
|
||||
|
||||
fn apply(&self, res: &Resources) -> Result<(), CgroupError> {
|
||||
/* get the resources that apply to this controller */
|
||||
@@ -45,7 +55,7 @@ impl Controller for PidController {
|
||||
if pidres.update_values {
|
||||
/* apply pid_max */
|
||||
let _ = self.set_pid_max(pidres.maximum_number_of_processes);
|
||||
|
||||
|
||||
/* now, verify */
|
||||
if self.get_pid_max() == Ok(pidres.maximum_number_of_processes) {
|
||||
return Ok(());
|
||||
@@ -78,7 +88,7 @@ impl<'a> From<&'a Subsystem> for &'a PidController {
|
||||
_ => {
|
||||
assert_eq!(1, 0);
|
||||
::std::mem::uninitialized()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -109,14 +119,12 @@ impl PidController {
|
||||
self.open_path("pids.events", false).and_then(|mut file| {
|
||||
let mut string = String::new();
|
||||
match file.read_to_string(&mut string) {
|
||||
Ok(_) => {
|
||||
match string.split_whitespace().nth(1) {
|
||||
Some(elem) => match elem.parse() {
|
||||
Ok(val) => Ok(val),
|
||||
Err(_) => Err(CgroupError::ParseError),
|
||||
},
|
||||
None => Err(CgroupError::ParseError),
|
||||
}
|
||||
Ok(_) => match string.split_whitespace().nth(1) {
|
||||
Some(elem) => match elem.parse() {
|
||||
Ok(val) => Ok(val),
|
||||
Err(_) => Err(CgroupError::ParseError),
|
||||
},
|
||||
None => Err(CgroupError::ParseError),
|
||||
},
|
||||
Err(e) => Err(CgroupError::ReadError(e)),
|
||||
}
|
||||
@@ -125,7 +133,8 @@ impl PidController {
|
||||
|
||||
/// The number of processes currently.
|
||||
pub fn get_pid_current(&self) -> Result<u64, CgroupError> {
|
||||
self.open_path("pids.current", false).and_then(read_u64_from)
|
||||
self.open_path("pids.current", false)
|
||||
.and_then(read_u64_from)
|
||||
}
|
||||
|
||||
/// The maximum number of processes that can exist at one time in the control group.
|
||||
|
||||
29
src/rdma.rs
29
src/rdma.rs
@@ -1,12 +1,12 @@
|
||||
//! This module contains the implementation of the `rdma` cgroup subsystem.
|
||||
//!
|
||||
//!
|
||||
//! See the Kernel's documentation for more information about this subsystem, found at:
|
||||
//! [Documentation/cgroup-v1/rdma.txt](https://www.kernel.org/doc/Documentation/cgroup-v1/rdma.txt)
|
||||
use std::path::PathBuf;
|
||||
use std::io::{Write, Read};
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use {CgroupError, Controllers, Controller, Resources, ControllIdentifier, Subsystem};
|
||||
use {CgroupError, ControllIdentifier, Controller, Controllers, Resources, Subsystem};
|
||||
|
||||
/// A controller that allows controlling the `rdma` subsystem of a Cgroup.
|
||||
///
|
||||
@@ -19,10 +19,18 @@ pub struct RdmaController {
|
||||
}
|
||||
|
||||
impl Controller for RdmaController {
|
||||
fn control_type(&self) -> Controllers { Controllers::Rdma }
|
||||
fn get_path(&self) -> &PathBuf { &self.path }
|
||||
fn get_path_mut(&mut self) -> &mut PathBuf { &mut self.path }
|
||||
fn get_base(&self) -> &PathBuf { &self.base }
|
||||
fn control_type(&self) -> Controllers {
|
||||
Controllers::Rdma
|
||||
}
|
||||
fn get_path(&self) -> &PathBuf {
|
||||
&self.path
|
||||
}
|
||||
fn get_path_mut(&mut self) -> &mut PathBuf {
|
||||
&mut self.path
|
||||
}
|
||||
fn get_base(&self) -> &PathBuf {
|
||||
&self.base
|
||||
}
|
||||
|
||||
fn apply(&self, _res: &Resources) -> Result<(), CgroupError> {
|
||||
Ok(())
|
||||
@@ -43,7 +51,7 @@ impl<'a> From<&'a Subsystem> for &'a RdmaController {
|
||||
_ => {
|
||||
assert_eq!(1, 0);
|
||||
::std::mem::uninitialized()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,7 +85,8 @@ impl RdmaController {
|
||||
/// Set a maximum usage for each RDMA/IB resource.
|
||||
pub fn set_max(&self, max: &String) -> Result<(), CgroupError> {
|
||||
self.open_path("rdma.max", true).and_then(|mut file| {
|
||||
file.write_all(max.as_ref()).map_err(CgroupError::WriteError)
|
||||
file.write_all(max.as_ref())
|
||||
.map_err(CgroupError::WriteError)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
extern crate cgroups;
|
||||
use cgroups::{Cgroup, CgroupPid};
|
||||
|
||||
extern crate nix;
|
||||
extern crate libc;
|
||||
extern crate nix;
|
||||
|
||||
#[test]
|
||||
fn test_tasks_iterator() {
|
||||
@@ -17,7 +17,7 @@ fn test_tasks_iterator() {
|
||||
// Verify that the task is indeed in the control group
|
||||
assert_eq!(tasks.next(), Some(CgroupPid::from(pid)));
|
||||
assert_eq!(tasks.next(), None);
|
||||
|
||||
|
||||
// Now, try removing it.
|
||||
cg.remove_task(CgroupPid::from(pid));
|
||||
tasks = cg.tasks().into_iter();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
extern crate cgroups;
|
||||
|
||||
use cgroups::{Cgroup, CgroupError};
|
||||
use cgroups::cpuset::CpuSetController;
|
||||
use cgroups::{Cgroup, CgroupError};
|
||||
|
||||
#[test]
|
||||
fn test_cpuset_memory_pressure_root_cg() {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//! Integration tests about the devices subsystem
|
||||
|
||||
extern crate cgroups;
|
||||
use cgroups::devices::{DevicePermissions, DeviceType, DevicesController};
|
||||
use cgroups::{Cgroup, DeviceResource};
|
||||
use cgroups::devices::{DevicesController, DevicePermissions, DeviceType};
|
||||
|
||||
#[test]
|
||||
fn test_devices_parsing() {
|
||||
@@ -12,7 +12,16 @@ fn test_devices_parsing() {
|
||||
let devices: &DevicesController = cg.controller_of().unwrap();
|
||||
|
||||
// Deny access to all devices first
|
||||
devices.deny_device(DeviceType::All, -1, -1, &vec![DevicePermissions::Read, DevicePermissions::Write, DevicePermissions::MkNod]);
|
||||
devices.deny_device(
|
||||
DeviceType::All,
|
||||
-1,
|
||||
-1,
|
||||
&vec![
|
||||
DevicePermissions::Read,
|
||||
DevicePermissions::Write,
|
||||
DevicePermissions::MkNod,
|
||||
],
|
||||
);
|
||||
// Acquire the list of allowed devices after we denied all
|
||||
let allowed_devices = devices.allowed_devices();
|
||||
// Verify that there are no devices that we can access.
|
||||
@@ -25,13 +34,16 @@ fn test_devices_parsing() {
|
||||
assert!(allowed_devices.is_ok());
|
||||
let allowed_devices = allowed_devices.unwrap();
|
||||
assert_eq!(allowed_devices.len(), 1);
|
||||
assert_eq!(allowed_devices[0], DeviceResource {
|
||||
allow: true,
|
||||
devtype: DeviceType::Char,
|
||||
major: 1,
|
||||
minor: 3,
|
||||
access: vec![DevicePermissions::MkNod],
|
||||
});
|
||||
assert_eq!(
|
||||
allowed_devices[0],
|
||||
DeviceResource {
|
||||
allow: true,
|
||||
devtype: DeviceType::Char,
|
||||
major: 1,
|
||||
minor: 3,
|
||||
access: vec![DevicePermissions::MkNod],
|
||||
}
|
||||
);
|
||||
|
||||
// Now deny, this device explicitly.
|
||||
devices.deny_device(DeviceType::Char, 1, 3, &DevicePermissions::all());
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
//! Integration tests about the pids subsystem
|
||||
extern crate cgroups;
|
||||
use cgroups::{CgroupError, CgroupPid, Cgroup, Resources, PidResources};
|
||||
use cgroups::pid::{PidController, PidMax};
|
||||
use cgroups::Controller;
|
||||
use cgroups::{Cgroup, CgroupError, CgroupPid, PidResources, Resources};
|
||||
|
||||
extern crate nix;
|
||||
use nix::unistd::{Pid, fork, ForkResult};
|
||||
use nix::sys::wait::{waitpid, WaitStatus};
|
||||
use nix::unistd::{fork, ForkResult, Pid};
|
||||
|
||||
extern crate libc;
|
||||
use libc::pid_t;
|
||||
@@ -86,16 +86,14 @@ fn test_pid_events_is_not_zero() {
|
||||
let events = pids.get_pid_events();
|
||||
assert!(events.is_ok());
|
||||
assert_eq!(events.unwrap(), before + 1);
|
||||
},
|
||||
Ok(ForkResult::Child) => {
|
||||
loop {
|
||||
let pids_max = pids.get_pid_max();
|
||||
if pids_max.is_ok() && pids_max.unwrap() == PidMax::Value(1) {
|
||||
if let Err(_) = fork() {
|
||||
unsafe { libc::exit(0) };
|
||||
} else {
|
||||
unsafe { libc::exit(1) };
|
||||
}
|
||||
}
|
||||
Ok(ForkResult::Child) => loop {
|
||||
let pids_max = pids.get_pid_max();
|
||||
if pids_max.is_ok() && pids_max.unwrap() == PidMax::Value(1) {
|
||||
if let Err(_) = fork() {
|
||||
unsafe { libc::exit(0) };
|
||||
} else {
|
||||
unsafe { libc::exit(1) };
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//! Integration test about setting resources using `apply()`
|
||||
extern crate cgroups;
|
||||
|
||||
use cgroups::{Cgroup, Resources, PidResources};
|
||||
use cgroups::pid::{PidController, PidMax};
|
||||
use cgroups::{Cgroup, PidResources, Resources};
|
||||
|
||||
#[test]
|
||||
fn pid_resources() {
|
||||
|
||||
Reference in New Issue
Block a user