mirror of
https://github.com/kata-containers/cgroups-rs.git
synced 2026-08-05 02:13:23 +00:00
@@ -5,7 +5,7 @@ repository = "https://github.com/levex/cgroups-rs"
|
||||
keywords = ["linux", "cgroup", "containers", "isolation"]
|
||||
categories = ["os", "api-bindings", "os::unix-apis"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
version = "0.1.1-alpha.0"
|
||||
version = "0.2.0"
|
||||
authors = ["Levente Kurusa <lkurusa@acm.org>", "Sam Wilson <tecywiz121@hotmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
@@ -14,6 +14,7 @@ log = "0.4"
|
||||
regex = "1.1"
|
||||
nix = "0.18.0"
|
||||
libc = "0.2"
|
||||
procinfo = "0.4.2"
|
||||
|
||||
[dev-dependencies]
|
||||
libc = "0.2.76"
|
||||
|
||||
23
README.md
23
README.md
@@ -9,24 +9,29 @@ is planned for the Unified hierarchy.
|
||||
## Create a control group using the builder pattern
|
||||
|
||||
``` rust
|
||||
// Acquire a handle for the V1 cgroup hierarchy.
|
||||
let hier = ::hierarchies::V1::new();
|
||||
|
||||
|
||||
use cgroups::*;
|
||||
use cgroups::cgroup_builder::*;
|
||||
|
||||
// Acquire a handle for the cgroup hierarchy.
|
||||
let hier = cgroups::hierarchies::auto();
|
||||
|
||||
// Use the builder pattern (see the documentation to create the control group)
|
||||
//
|
||||
// This creates a control group named "example" in the V1 hierarchy.
|
||||
let cg: Cgroup = CgroupBuilder::new("example", &v1)
|
||||
.cpu()
|
||||
.shares(85)
|
||||
.done()
|
||||
.build();
|
||||
let cg: Cgroup = CgroupBuilder::new("example")
|
||||
.cpu()
|
||||
.shares(85)
|
||||
.done()
|
||||
.build(hier);
|
||||
|
||||
// Now `cg` is a control group that gets 85% of the CPU time in relative to
|
||||
// other control groups.
|
||||
|
||||
// Get a handle to the CPU controller.
|
||||
let cpus: &CpuController = cg.controller_of().unwrap();
|
||||
cpus.add_task(1234u64);
|
||||
let cpus: &cgroups::cpu::CpuController = cg.controller_of().unwrap();
|
||||
cpus.add_task(&CgroupPid::from(1234u64));
|
||||
|
||||
// [...]
|
||||
|
||||
|
||||
59
src/blkio.rs
59
src/blkio.rs
@@ -344,39 +344,36 @@ impl ControllerInternal for BlkIoController {
|
||||
// get the resources that apply to this controller
|
||||
let res: &BlkIoResources = &res.blkio;
|
||||
|
||||
if res.update_values {
|
||||
if let Some(weight) = res.weight {
|
||||
let _ = self.set_weight(weight as u64);
|
||||
}
|
||||
if let Some(leaf_weight) = res.leaf_weight {
|
||||
let _ = self.set_leaf_weight(leaf_weight as u64);
|
||||
}
|
||||
if let Some(weight) = res.weight {
|
||||
let _ = self.set_weight(weight as u64);
|
||||
}
|
||||
if let Some(leaf_weight) = res.leaf_weight {
|
||||
let _ = self.set_leaf_weight(leaf_weight as u64);
|
||||
}
|
||||
|
||||
for dev in &res.weight_device {
|
||||
if let Some(weight) = dev.weight {
|
||||
let _ = self.set_weight_for_device(dev.major, dev.minor, weight as u64);
|
||||
}
|
||||
if let Some(leaf_weight) = dev.leaf_weight {
|
||||
let _ =
|
||||
self.set_leaf_weight_for_device(dev.major, dev.minor, leaf_weight as u64);
|
||||
}
|
||||
for dev in &res.weight_device {
|
||||
if let Some(weight) = dev.weight {
|
||||
let _ = self.set_weight_for_device(dev.major, dev.minor, weight as u64);
|
||||
}
|
||||
if let Some(leaf_weight) = dev.leaf_weight {
|
||||
let _ = self.set_leaf_weight_for_device(dev.major, dev.minor, leaf_weight as u64);
|
||||
}
|
||||
}
|
||||
|
||||
for dev in &res.throttle_read_bps_device {
|
||||
let _ = self.throttle_read_bps_for_device(dev.major, dev.minor, dev.rate);
|
||||
}
|
||||
for dev in &res.throttle_read_bps_device {
|
||||
let _ = self.throttle_read_bps_for_device(dev.major, dev.minor, dev.rate);
|
||||
}
|
||||
|
||||
for dev in &res.throttle_write_bps_device {
|
||||
let _ = self.throttle_write_bps_for_device(dev.major, dev.minor, dev.rate);
|
||||
}
|
||||
for dev in &res.throttle_write_bps_device {
|
||||
let _ = self.throttle_write_bps_for_device(dev.major, dev.minor, dev.rate);
|
||||
}
|
||||
|
||||
for dev in &res.throttle_read_iops_device {
|
||||
let _ = self.throttle_read_iops_for_device(dev.major, dev.minor, dev.rate);
|
||||
}
|
||||
for dev in &res.throttle_read_iops_device {
|
||||
let _ = self.throttle_read_iops_for_device(dev.major, dev.minor, dev.rate);
|
||||
}
|
||||
|
||||
for dev in &res.throttle_write_iops_device {
|
||||
let _ = self.throttle_write_iops_for_device(dev.major, dev.minor, dev.rate);
|
||||
}
|
||||
for dev in &res.throttle_write_iops_device {
|
||||
let _ = self.throttle_write_iops_for_device(dev.major, dev.minor, dev.rate);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -424,12 +421,8 @@ fn read_u64_from(mut file: File) -> Result<u64> {
|
||||
}
|
||||
|
||||
impl BlkIoController {
|
||||
/// Constructs a new `BlkIoController` with `oroot` serving as the root of the control group.
|
||||
pub fn new(oroot: PathBuf, v2: bool) -> Self {
|
||||
let mut root = oroot;
|
||||
if !v2 {
|
||||
root.push(Self::controller_type().to_string());
|
||||
}
|
||||
/// Constructs a new `BlkIoController` with `root` serving as the root of the control group.
|
||||
pub fn new(root: PathBuf, v2: bool) -> Self {
|
||||
Self {
|
||||
base: root.clone(),
|
||||
path: root,
|
||||
|
||||
148
src/cgroup.rs
148
src/cgroup.rs
@@ -9,8 +9,6 @@
|
||||
use crate::error::ErrorKind::*;
|
||||
use crate::error::*;
|
||||
|
||||
use crate::libc_rmdir;
|
||||
|
||||
use crate::{CgroupPid, ControllIdentifier, Controller, Hierarchy, Resources, Subsystem};
|
||||
|
||||
use std::collections::HashMap;
|
||||
@@ -30,16 +28,37 @@ use std::path::{Path, PathBuf};
|
||||
/// > specialized behaviour.
|
||||
///
|
||||
/// This crate is an attempt at providing a Rust-native way of managing these cgroups.
|
||||
pub struct Cgroup<'b> {
|
||||
#[derive(Debug)]
|
||||
pub struct Cgroup {
|
||||
/// The list of subsystems that control this cgroup
|
||||
subsystems: Vec<Subsystem>,
|
||||
|
||||
/// The hierarchy.
|
||||
hier: Box<&'b dyn Hierarchy>,
|
||||
hier: Box<dyn Hierarchy>,
|
||||
path: String,
|
||||
}
|
||||
|
||||
impl<'b> Cgroup<'b> {
|
||||
impl Clone for Cgroup {
|
||||
fn clone(&self) -> Self {
|
||||
Cgroup {
|
||||
subsystems: self.subsystems.clone(),
|
||||
path: self.path.clone(),
|
||||
hier: crate::hierarchies::auto(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Cgroup {
|
||||
fn default() -> Self {
|
||||
Cgroup {
|
||||
subsystems: Vec::new(),
|
||||
hier: crate::hierarchies::auto(),
|
||||
path: "".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Cgroup {
|
||||
/// Create this control group.
|
||||
fn create(&self) {
|
||||
if self.hier.v2() {
|
||||
@@ -58,56 +77,66 @@ impl<'b> Cgroup<'b> {
|
||||
/// Create a new control group in the hierarchy `hier`, with name `path`.
|
||||
///
|
||||
/// Returns a handle to the control group that can be used to manipulate it.
|
||||
pub fn new<P: AsRef<Path>>(hier: Box<dyn Hierarchy>, path: P) -> Cgroup {
|
||||
let cg = Cgroup::load(hier, path);
|
||||
cg.create();
|
||||
cg
|
||||
}
|
||||
|
||||
/// Create a new control group in the hierarchy `hier`, with name `path` and `relative_paths`
|
||||
///
|
||||
/// Note that if the handle goes out of scope and is dropped, the control group is _not_
|
||||
/// destroyed.
|
||||
pub fn new<P: AsRef<Path>>(hier: Box<&'b dyn Hierarchy>, path: P) -> Cgroup<'b> {
|
||||
let relative_paths = get_cgroups_relative_paths().unwrap();
|
||||
Cgroup::new_with_relative_paths(hier, path, relative_paths)
|
||||
/// Returns a handle to the control group that can be used to manipulate it.
|
||||
///
|
||||
/// Note that this method is only meaningful for cgroup v1, call it is equivalent to call `new` in the v2 mode
|
||||
pub fn new_with_relative_paths<P: AsRef<Path>>(
|
||||
hier: Box<dyn Hierarchy>,
|
||||
path: P,
|
||||
relative_paths: HashMap<String, String>,
|
||||
) -> Cgroup {
|
||||
let cg = Cgroup::load_with_relative_paths(hier, path, relative_paths);
|
||||
cg.create();
|
||||
cg
|
||||
}
|
||||
|
||||
/// Create a handle for a control group in the hierarchy `hier`, with name `path`.
|
||||
///
|
||||
/// Returns a handle to the control group (that possibly does not exist until `create()` has
|
||||
/// been called on the cgroup.
|
||||
///
|
||||
/// Note that if the handle goes out of scope and is dropped, the control group is _not_
|
||||
/// destroyed.
|
||||
pub fn load<P: AsRef<Path>>(hier: Box<&'b dyn Hierarchy>, path: P) -> Cgroup<'b> {
|
||||
let relative_paths = get_cgroups_relative_paths().unwrap();
|
||||
Cgroup::load_with_relative_paths(hier, path, relative_paths)
|
||||
}
|
||||
pub fn load<P: AsRef<Path>>(hier: Box<dyn Hierarchy>, path: P) -> Cgroup {
|
||||
let path = path.as_ref();
|
||||
let mut subsystems = hier.subsystems();
|
||||
if path.as_os_str() != "" {
|
||||
subsystems = subsystems
|
||||
.into_iter()
|
||||
.map(|x| x.enter(path))
|
||||
.collect::<Vec<_>>();
|
||||
}
|
||||
|
||||
let cg = Cgroup {
|
||||
path: path.to_str().unwrap().to_string(),
|
||||
subsystems: subsystems,
|
||||
hier,
|
||||
};
|
||||
|
||||
/// Create a new control group in the hierarchy `hier`, with name `path`.
|
||||
/// and relative paths from `/proc/self/cgroup`
|
||||
///
|
||||
/// Returns a handle to the control group that can be used to manipulate it.
|
||||
///
|
||||
/// Note that if the handle goes out of scope and is dropped, the control group is _not_
|
||||
/// destroyed.
|
||||
pub fn new_with_relative_paths<P: AsRef<Path>>(
|
||||
hier: Box<&'b dyn Hierarchy>,
|
||||
path: P,
|
||||
relative_paths: HashMap<String, String>,
|
||||
) -> Cgroup<'b> {
|
||||
let cg = Cgroup::load_with_relative_paths(hier, path, relative_paths);
|
||||
cg.create();
|
||||
cg
|
||||
}
|
||||
|
||||
/// Create a handle for a control group in the hierarchy `hier`, with name `path`,
|
||||
/// and relative paths from `/proc/self/cgroup`
|
||||
/// Create a handle for a control group in the hierarchy `hier`, with name `path` and `relative_paths`
|
||||
///
|
||||
/// Returns a handle to the control group (that possibly does not exist until `create()` has
|
||||
/// been called on the cgroup.
|
||||
///
|
||||
/// Note that if the handle goes out of scope and is dropped, the control group is _not_
|
||||
/// destroyed.
|
||||
/// Note that this method is only meaningful for cgroup v1, call it is equivalent to call `load` in the v2 mode
|
||||
pub fn load_with_relative_paths<P: AsRef<Path>>(
|
||||
hier: Box<&'b dyn Hierarchy>,
|
||||
hier: Box<dyn Hierarchy>,
|
||||
path: P,
|
||||
relative_paths: HashMap<String, String>,
|
||||
) -> Cgroup<'b> {
|
||||
) -> Cgroup {
|
||||
// relative_paths only valid for cgroup v1
|
||||
if hier.v2() {
|
||||
return Self::load(hier, path);
|
||||
}
|
||||
|
||||
let path = path.as_ref();
|
||||
let mut subsystems = hier.subsystems();
|
||||
if path.as_os_str() != "" {
|
||||
@@ -130,7 +159,7 @@ impl<'b> Cgroup<'b> {
|
||||
|
||||
let cg = Cgroup {
|
||||
subsystems: subsystems,
|
||||
hier: hier,
|
||||
hier,
|
||||
path: path.to_str().unwrap().to_string(),
|
||||
};
|
||||
|
||||
@@ -148,17 +177,17 @@ impl<'b> Cgroup<'b> {
|
||||
/// system call will fail if there are any descendants. Thus, one should check whether it was
|
||||
/// actually removed, and remove the descendants first if not. In the future, this behavior
|
||||
/// will change.
|
||||
pub fn delete(self) {
|
||||
pub fn delete(&self) -> Result<()> {
|
||||
if self.v2() {
|
||||
if self.path != "" {
|
||||
let mut p = self.hier.root().clone();
|
||||
p.push(self.path);
|
||||
libc_rmdir(p.to_str().unwrap());
|
||||
p.push(self.path.clone());
|
||||
return fs::remove_dir(p).map_err(|e| Error::with_cause(RemoveFailed, e));
|
||||
}
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.subsystems.into_iter().for_each(|sub| match sub {
|
||||
self.subsystems.iter().try_for_each(|sub| match sub {
|
||||
Subsystem::Pid(pidc) => pidc.delete(),
|
||||
Subsystem::Mem(c) => c.delete(),
|
||||
Subsystem::CpuSet(c) => c.delete(),
|
||||
@@ -173,7 +202,7 @@ impl<'b> Cgroup<'b> {
|
||||
Subsystem::HugeTlb(c) => c.delete(),
|
||||
Subsystem::Rdma(c) => c.delete(),
|
||||
Subsystem::Systemd(c) => c.delete(),
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
/// Apply a set of resource limits to the control group.
|
||||
@@ -233,6 +262,29 @@ impl<'b> Cgroup<'b> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Attach a task to the control group by thread group id.
|
||||
pub fn add_task_by_tgid(&self, pid: CgroupPid) -> Result<()> {
|
||||
self.subsystems()
|
||||
.iter()
|
||||
.try_for_each(|sub| sub.to_controller().add_task_by_tgid(&pid))
|
||||
}
|
||||
|
||||
/// Set notify_on_release to the control group.
|
||||
pub fn set_notify_on_release(&self, enable: bool) -> Result<()> {
|
||||
self.subsystems()
|
||||
.iter()
|
||||
.try_for_each(|sub| sub.to_controller().set_notify_on_release(enable))
|
||||
}
|
||||
|
||||
/// Set release_agent
|
||||
pub fn set_release_agent(&self, path: &str) -> Result<()> {
|
||||
self.hier
|
||||
.root_control_group()
|
||||
.subsystems()
|
||||
.iter()
|
||||
.try_for_each(|sub| sub.to_controller().set_release_agent(path))
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
@@ -324,13 +376,7 @@ pub fn get_cgroups_relative_paths() -> Result<HashMap<String, String>> {
|
||||
|
||||
let keys: Vec<&str> = fl[1].split(',').collect();
|
||||
for key in &keys {
|
||||
// this is a workaround, cgroup file are using `name=systemd`,
|
||||
// but if file system the name is `systemd`
|
||||
if *key == "name=systemd" {
|
||||
m.insert("systemd".to_string(), fl[2].to_string());
|
||||
} else {
|
||||
m.insert(key.to_string(), fl[2].to_string());
|
||||
}
|
||||
m.insert(key.to_string(), fl[2].to_string());
|
||||
}
|
||||
}
|
||||
Ok(m)
|
||||
|
||||
@@ -20,8 +20,7 @@
|
||||
//! # use cgroups::devices::*;
|
||||
//! # use cgroups::cgroup_builder::*;
|
||||
//! let h = cgroups::hierarchies::auto();
|
||||
//! let h = Box::new(&*h);
|
||||
//! let cgroup: Cgroup = CgroupBuilder::new("hello", h)
|
||||
//! let cgroup: Cgroup = CgroupBuilder::new("hello")
|
||||
//! .memory()
|
||||
//! .kernel_memory_limit(1024 * 1024)
|
||||
//! .memory_hard_limit(1024 * 1024)
|
||||
@@ -47,8 +46,8 @@
|
||||
//! .limit("2G".to_string(), 2 * 1024 * 1024 * 1024)
|
||||
//! .done()
|
||||
//! .blkio()
|
||||
//! .weight(Some(123))
|
||||
//! .leaf_weight(Some(99))
|
||||
//! .weight(123)
|
||||
//! .leaf_weight(99)
|
||||
//! .weight_device(6, 1, Some(100), Some(55))
|
||||
//! .weight_device(6, 1, Some(100), Some(55))
|
||||
//! .throttle_iops()
|
||||
@@ -58,7 +57,7 @@
|
||||
//! .read(6, 1, 10)
|
||||
//! .write(11, 1, 100)
|
||||
//! .done()
|
||||
//! .build();
|
||||
//! .build(h);
|
||||
//! ```
|
||||
|
||||
use crate::{
|
||||
@@ -70,67 +69,64 @@ macro_rules! gen_setter {
|
||||
($res:ident, $cont:ident, $func:ident, $name:ident, $ty:ty) => {
|
||||
/// See the similarly named function in the respective controller.
|
||||
pub fn $name(mut self, $name: $ty) -> Self {
|
||||
self.cgroup.resources.$res.update_values = true;
|
||||
self.cgroup.resources.$res.$name = $name;
|
||||
self.cgroup.resources.$res.$name = Some($name);
|
||||
self
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// A control group builder instance
|
||||
pub struct CgroupBuilder<'a> {
|
||||
pub struct CgroupBuilder {
|
||||
name: String,
|
||||
hierarchy: Box<&'a dyn Hierarchy>,
|
||||
/// Internal, unsupported field: use the associated builders instead.
|
||||
resources: Resources,
|
||||
}
|
||||
|
||||
impl<'a> CgroupBuilder<'a> {
|
||||
impl CgroupBuilder {
|
||||
/// Start building a control group with the supplied hierarchy and name pair.
|
||||
///
|
||||
/// Note that this does not actually create the control group until `build()` is called.
|
||||
pub fn new(name: &'a str, hierarchy: Box<&'a dyn Hierarchy>) -> CgroupBuilder<'a> {
|
||||
pub fn new(name: &str) -> CgroupBuilder {
|
||||
CgroupBuilder {
|
||||
name: name.to_owned(),
|
||||
hierarchy: hierarchy,
|
||||
resources: Resources::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds the memory resources of the control group.
|
||||
pub fn memory(self) -> MemoryResourceBuilder<'a> {
|
||||
pub fn memory(self) -> MemoryResourceBuilder {
|
||||
MemoryResourceBuilder { cgroup: self }
|
||||
}
|
||||
|
||||
/// Builds the pid resources of the control group.
|
||||
pub fn pid(self) -> PidResourceBuilder<'a> {
|
||||
pub fn pid(self) -> PidResourceBuilder {
|
||||
PidResourceBuilder { cgroup: self }
|
||||
}
|
||||
|
||||
/// Builds the cpu resources of the control group.
|
||||
pub fn cpu(self) -> CpuResourceBuilder<'a> {
|
||||
pub fn cpu(self) -> CpuResourceBuilder {
|
||||
CpuResourceBuilder { cgroup: self }
|
||||
}
|
||||
|
||||
/// Builds the devices resources of the control group, disallowing or
|
||||
/// allowing access to certain devices in the system.
|
||||
pub fn devices(self) -> DeviceResourceBuilder<'a> {
|
||||
pub fn devices(self) -> DeviceResourceBuilder {
|
||||
DeviceResourceBuilder { cgroup: self }
|
||||
}
|
||||
|
||||
/// Builds the network resources of the control group, setting class id, or
|
||||
/// various priorities on networking interfaces.
|
||||
pub fn network(self) -> NetworkResourceBuilder<'a> {
|
||||
pub fn network(self) -> NetworkResourceBuilder {
|
||||
NetworkResourceBuilder { cgroup: self }
|
||||
}
|
||||
|
||||
/// Builds the hugepage/hugetlb resources available to the control group.
|
||||
pub fn hugepages(self) -> HugepagesResourceBuilder<'a> {
|
||||
pub fn hugepages(self) -> HugepagesResourceBuilder {
|
||||
HugepagesResourceBuilder { cgroup: self }
|
||||
}
|
||||
|
||||
/// Builds the block I/O resources available for the control group.
|
||||
pub fn blkio(self) -> BlkIoResourcesBuilder<'a> {
|
||||
pub fn blkio(self) -> BlkIoResourcesBuilder {
|
||||
BlkIoResourcesBuilder {
|
||||
cgroup: self,
|
||||
throttling_iops: false,
|
||||
@@ -138,19 +134,19 @@ impl<'a> CgroupBuilder<'a> {
|
||||
}
|
||||
|
||||
/// Finalize the control group, consuming the builder and creating the control group.
|
||||
pub fn build(self) -> Cgroup<'a> {
|
||||
let cg = Cgroup::new(self.hierarchy, self.name);
|
||||
pub fn build(self, hier: Box<dyn Hierarchy>) -> Cgroup {
|
||||
let cg = Cgroup::new(hier, self.name);
|
||||
let _ret = cg.apply(&self.resources);
|
||||
cg
|
||||
}
|
||||
}
|
||||
|
||||
/// A builder that configures the memory controller of a control group.
|
||||
pub struct MemoryResourceBuilder<'a> {
|
||||
cgroup: CgroupBuilder<'a>,
|
||||
pub struct MemoryResourceBuilder {
|
||||
cgroup: CgroupBuilder,
|
||||
}
|
||||
|
||||
impl<'a> MemoryResourceBuilder<'a> {
|
||||
impl MemoryResourceBuilder {
|
||||
gen_setter!(
|
||||
memory,
|
||||
MemController,
|
||||
@@ -183,17 +179,17 @@ impl<'a> MemoryResourceBuilder<'a> {
|
||||
gen_setter!(memory, MemController, set_swappiness, swappiness, u64);
|
||||
|
||||
/// Finish the construction of the memory resources of a control group.
|
||||
pub fn done(self) -> CgroupBuilder<'a> {
|
||||
pub fn done(self) -> CgroupBuilder {
|
||||
self.cgroup
|
||||
}
|
||||
}
|
||||
|
||||
/// A builder that configures the pid controller of a control group.
|
||||
pub struct PidResourceBuilder<'a> {
|
||||
cgroup: CgroupBuilder<'a>,
|
||||
pub struct PidResourceBuilder {
|
||||
cgroup: CgroupBuilder,
|
||||
}
|
||||
|
||||
impl<'a> PidResourceBuilder<'a> {
|
||||
impl PidResourceBuilder {
|
||||
gen_setter!(
|
||||
pid,
|
||||
PidController,
|
||||
@@ -203,19 +199,18 @@ impl<'a> PidResourceBuilder<'a> {
|
||||
);
|
||||
|
||||
/// Finish the construction of the pid resources of a control group.
|
||||
pub fn done(self) -> CgroupBuilder<'a> {
|
||||
pub fn done(self) -> CgroupBuilder {
|
||||
self.cgroup
|
||||
}
|
||||
}
|
||||
|
||||
/// A builder that configures the cpuset & cpu controllers of a control group.
|
||||
pub struct CpuResourceBuilder<'a> {
|
||||
cgroup: CgroupBuilder<'a>,
|
||||
pub struct CpuResourceBuilder {
|
||||
cgroup: CgroupBuilder,
|
||||
}
|
||||
|
||||
impl<'a> CpuResourceBuilder<'a> {
|
||||
// FIXME this should all changed to options.
|
||||
gen_setter!(cpu, CpuSetController, set_cpus, cpus, Option<String>);
|
||||
impl CpuResourceBuilder {
|
||||
gen_setter!(cpu, CpuSetController, set_cpus, cpus, String);
|
||||
gen_setter!(cpu, CpuSetController, set_mems, mems, String);
|
||||
gen_setter!(cpu, CpuController, set_shares, shares, u64);
|
||||
gen_setter!(cpu, CpuController, set_cfs_quota, quota, i64);
|
||||
@@ -224,17 +219,17 @@ impl<'a> CpuResourceBuilder<'a> {
|
||||
gen_setter!(cpu, CpuController, set_rt_period, realtime_period, u64);
|
||||
|
||||
/// Finish the construction of the cpu resources of a control group.
|
||||
pub fn done(self) -> CgroupBuilder<'a> {
|
||||
pub fn done(self) -> CgroupBuilder {
|
||||
self.cgroup
|
||||
}
|
||||
}
|
||||
|
||||
/// A builder that configures the devices controller of a control group.
|
||||
pub struct DeviceResourceBuilder<'a> {
|
||||
cgroup: CgroupBuilder<'a>,
|
||||
pub struct DeviceResourceBuilder {
|
||||
cgroup: CgroupBuilder,
|
||||
}
|
||||
|
||||
impl<'a> DeviceResourceBuilder<'a> {
|
||||
impl DeviceResourceBuilder {
|
||||
/// Restrict (or allow) a device to the tasks inside the control group.
|
||||
pub fn device(
|
||||
mut self,
|
||||
@@ -243,8 +238,7 @@ impl<'a> DeviceResourceBuilder<'a> {
|
||||
devtype: crate::devices::DeviceType,
|
||||
allow: bool,
|
||||
access: Vec<crate::devices::DevicePermissions>,
|
||||
) -> DeviceResourceBuilder<'a> {
|
||||
self.cgroup.resources.devices.update_values = true;
|
||||
) -> DeviceResourceBuilder {
|
||||
self.cgroup.resources.devices.devices.push(DeviceResource {
|
||||
major,
|
||||
minor,
|
||||
@@ -256,23 +250,22 @@ impl<'a> DeviceResourceBuilder<'a> {
|
||||
}
|
||||
|
||||
/// Finish the construction of the devices resources of a control group.
|
||||
pub fn done(self) -> CgroupBuilder<'a> {
|
||||
pub fn done(self) -> CgroupBuilder {
|
||||
self.cgroup
|
||||
}
|
||||
}
|
||||
|
||||
/// A builder that configures the net_cls & net_prio controllers of a control group.
|
||||
pub struct NetworkResourceBuilder<'a> {
|
||||
cgroup: CgroupBuilder<'a>,
|
||||
pub struct NetworkResourceBuilder {
|
||||
cgroup: CgroupBuilder,
|
||||
}
|
||||
|
||||
impl<'a> NetworkResourceBuilder<'a> {
|
||||
impl NetworkResourceBuilder {
|
||||
gen_setter!(network, NetclsController, set_class, class_id, u64);
|
||||
|
||||
/// Set the priority of the tasks when operating on a networking device defined by `name` to be
|
||||
/// `priority`.
|
||||
pub fn priority(mut self, name: String, priority: u64) -> NetworkResourceBuilder<'a> {
|
||||
self.cgroup.resources.network.update_values = true;
|
||||
pub fn priority(mut self, name: String, priority: u64) -> NetworkResourceBuilder {
|
||||
self.cgroup
|
||||
.resources
|
||||
.network
|
||||
@@ -282,20 +275,19 @@ impl<'a> NetworkResourceBuilder<'a> {
|
||||
}
|
||||
|
||||
/// Finish the construction of the network resources of a control group.
|
||||
pub fn done(self) -> CgroupBuilder<'a> {
|
||||
pub fn done(self) -> CgroupBuilder {
|
||||
self.cgroup
|
||||
}
|
||||
}
|
||||
|
||||
/// A builder that configures the hugepages controller of a control group.
|
||||
pub struct HugepagesResourceBuilder<'a> {
|
||||
cgroup: CgroupBuilder<'a>,
|
||||
pub struct HugepagesResourceBuilder {
|
||||
cgroup: CgroupBuilder,
|
||||
}
|
||||
|
||||
impl<'a> HugepagesResourceBuilder<'a> {
|
||||
impl HugepagesResourceBuilder {
|
||||
/// Limit the usage of certain hugepages (determined by `size`) to be at most `limit` bytes.
|
||||
pub fn limit(mut self, size: String, limit: u64) -> HugepagesResourceBuilder<'a> {
|
||||
self.cgroup.resources.hugepages.update_values = true;
|
||||
pub fn limit(mut self, size: String, limit: u64) -> HugepagesResourceBuilder {
|
||||
self.cgroup
|
||||
.resources
|
||||
.hugepages
|
||||
@@ -305,26 +297,20 @@ impl<'a> HugepagesResourceBuilder<'a> {
|
||||
}
|
||||
|
||||
/// Finish the construction of the network resources of a control group.
|
||||
pub fn done(self) -> CgroupBuilder<'a> {
|
||||
pub fn done(self) -> CgroupBuilder {
|
||||
self.cgroup
|
||||
}
|
||||
}
|
||||
|
||||
/// A builder that configures the blkio controller of a control group.
|
||||
pub struct BlkIoResourcesBuilder<'a> {
|
||||
cgroup: CgroupBuilder<'a>,
|
||||
pub struct BlkIoResourcesBuilder {
|
||||
cgroup: CgroupBuilder,
|
||||
throttling_iops: bool,
|
||||
}
|
||||
|
||||
impl<'a> BlkIoResourcesBuilder<'a> {
|
||||
gen_setter!(blkio, BlkIoController, set_weight, weight, Option<u16>);
|
||||
gen_setter!(
|
||||
blkio,
|
||||
BlkIoController,
|
||||
set_leaf_weight,
|
||||
leaf_weight,
|
||||
Option<u16>
|
||||
);
|
||||
impl BlkIoResourcesBuilder {
|
||||
gen_setter!(blkio, BlkIoController, set_weight, weight, u16);
|
||||
gen_setter!(blkio, BlkIoController, set_leaf_weight, leaf_weight, u16);
|
||||
|
||||
/// Set the weight of a certain device.
|
||||
pub fn weight_device(
|
||||
@@ -333,8 +319,7 @@ impl<'a> BlkIoResourcesBuilder<'a> {
|
||||
minor: u64,
|
||||
weight: Option<u16>,
|
||||
leaf_weight: Option<u16>,
|
||||
) -> BlkIoResourcesBuilder<'a> {
|
||||
self.cgroup.resources.blkio.update_values = true;
|
||||
) -> BlkIoResourcesBuilder {
|
||||
self.cgroup
|
||||
.resources
|
||||
.blkio
|
||||
@@ -349,20 +334,19 @@ impl<'a> BlkIoResourcesBuilder<'a> {
|
||||
}
|
||||
|
||||
/// Start configuring the I/O operations per second metric.
|
||||
pub fn throttle_iops(mut self) -> BlkIoResourcesBuilder<'a> {
|
||||
pub fn throttle_iops(mut self) -> BlkIoResourcesBuilder {
|
||||
self.throttling_iops = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Start configuring the bytes per second metric.
|
||||
pub fn throttle_bps(mut self) -> BlkIoResourcesBuilder<'a> {
|
||||
pub fn throttle_bps(mut self) -> BlkIoResourcesBuilder {
|
||||
self.throttling_iops = false;
|
||||
self
|
||||
}
|
||||
|
||||
/// Limit the read rate of the current metric for a certain device.
|
||||
pub fn read(mut self, major: u64, minor: u64, rate: u64) -> BlkIoResourcesBuilder<'a> {
|
||||
self.cgroup.resources.blkio.update_values = true;
|
||||
pub fn read(mut self, major: u64, minor: u64, rate: u64) -> BlkIoResourcesBuilder {
|
||||
let throttle = BlkIoDeviceThrottleResource { major, minor, rate };
|
||||
if self.throttling_iops {
|
||||
self.cgroup
|
||||
@@ -381,8 +365,7 @@ impl<'a> BlkIoResourcesBuilder<'a> {
|
||||
}
|
||||
|
||||
/// Limit the write rate of the current metric for a certain device.
|
||||
pub fn write(mut self, major: u64, minor: u64, rate: u64) -> BlkIoResourcesBuilder<'a> {
|
||||
self.cgroup.resources.blkio.update_values = true;
|
||||
pub fn write(mut self, major: u64, minor: u64, rate: u64) -> BlkIoResourcesBuilder {
|
||||
let throttle = BlkIoDeviceThrottleResource { major, minor, rate };
|
||||
if self.throttling_iops {
|
||||
self.cgroup
|
||||
@@ -401,7 +384,7 @@ impl<'a> BlkIoResourcesBuilder<'a> {
|
||||
}
|
||||
|
||||
/// Finish the construction of the blkio resources of a control group.
|
||||
pub fn done(self) -> CgroupBuilder<'a> {
|
||||
pub fn done(self) -> CgroupBuilder {
|
||||
self.cgroup
|
||||
}
|
||||
}
|
||||
|
||||
37
src/cpu.rs
37
src/cpu.rs
@@ -18,8 +18,8 @@ use crate::error::*;
|
||||
use crate::{parse_max_value, read_i64_from};
|
||||
|
||||
use crate::{
|
||||
ControllIdentifier, ControllerInternal, Controllers, CpuResources, MaxValue, Resources,
|
||||
Subsystem,
|
||||
ControllIdentifier, ControllerInternal, Controllers, CpuResources, CustomizedAttribute,
|
||||
MaxValue, Resources, Subsystem,
|
||||
};
|
||||
|
||||
/// A controller that allows controlling the `cpu` subsystem of a Cgroup.
|
||||
@@ -75,24 +75,15 @@ impl ControllerInternal for CpuController {
|
||||
// get the resources that apply to this controller
|
||||
let res: &CpuResources = &res.cpu;
|
||||
|
||||
if res.update_values {
|
||||
let _ = self.set_shares(res.shares);
|
||||
if self.shares()? != res.shares as u64 {
|
||||
return Err(Error::new(ErrorKind::Other));
|
||||
}
|
||||
update_and_test!(self, set_shares, res.shares, shares);
|
||||
update_and_test!(self, set_cfs_period, res.period, cfs_period);
|
||||
update_and_test!(self, set_cfs_quota, res.quota, cfs_quota);
|
||||
|
||||
let _ = self.set_cfs_period(res.period);
|
||||
if self.cfs_period()? != res.period as u64 {
|
||||
return Err(Error::new(ErrorKind::Other));
|
||||
}
|
||||
res.attrs.iter().for_each(|(k, v)| {
|
||||
let _ = self.set(k, v);
|
||||
});
|
||||
|
||||
let _ = self.set_cfs_quota(res.quota);
|
||||
if self.cfs_quota()? != res.quota {
|
||||
return Err(Error::new(ErrorKind::Other));
|
||||
}
|
||||
|
||||
// TODO: rt properties (CONFIG_RT_GROUP_SCHED) are not yet supported
|
||||
}
|
||||
// TODO: rt properties (CONFIG_RT_GROUP_SCHED) are not yet supported
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -131,12 +122,8 @@ fn read_u64_from(mut file: File) -> Result<u64> {
|
||||
}
|
||||
|
||||
impl CpuController {
|
||||
/// Contructs a new `CpuController` with `oroot` serving as the root of the control group.
|
||||
pub fn new(oroot: PathBuf, v2: bool) -> Self {
|
||||
let mut root = oroot;
|
||||
if !v2 {
|
||||
root.push(Self::controller_type().to_string());
|
||||
}
|
||||
/// Contructs a new `CpuController` with `root` serving as the root of the control group.
|
||||
pub fn new(root: PathBuf, v2: bool) -> Self {
|
||||
Self {
|
||||
base: root.clone(),
|
||||
path: root,
|
||||
@@ -306,6 +293,8 @@ impl CpuController {
|
||||
}
|
||||
}
|
||||
|
||||
impl CustomizedAttribute for CpuController {}
|
||||
|
||||
fn parse_cfs_quota_and_period(mut file: File) -> Result<CFSQuotaAndPeriod> {
|
||||
let mut content = String::new();
|
||||
file.read_to_string(&mut content)
|
||||
|
||||
@@ -118,10 +118,8 @@ fn read_string_from(mut file: File) -> Result<String> {
|
||||
}
|
||||
|
||||
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;
|
||||
root.push(Self::controller_type().to_string());
|
||||
/// Contructs a new `CpuAcctController` with `root` serving as the root of the control group.
|
||||
pub fn new(root: PathBuf) -> Self {
|
||||
Self {
|
||||
base: root.clone(),
|
||||
path: root,
|
||||
|
||||
@@ -110,12 +110,8 @@ impl ControllerInternal for CpuSetController {
|
||||
// get the resources that apply to this controller
|
||||
let res: &CpuResources = &res.cpu;
|
||||
|
||||
if res.update_values {
|
||||
if res.cpus.is_some() {
|
||||
let _ = self.set_cpus(res.cpus.as_ref().unwrap().as_str());
|
||||
}
|
||||
let _ = self.set_mems(&res.mems);
|
||||
}
|
||||
update!(self, set_cpus, res.cpus.as_ref());
|
||||
update!(self, set_mems, res.mems.as_ref());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -265,12 +261,8 @@ fn parse_range(s: String) -> Result<Vec<(u64, u64)>> {
|
||||
}
|
||||
|
||||
impl CpuSetController {
|
||||
/// Contructs a new `CpuSetController` with `oroot` serving as the root of the control group.
|
||||
pub fn new(oroot: PathBuf, v2: bool) -> Self {
|
||||
let mut root = oroot;
|
||||
if !v2 {
|
||||
root.push(Self::controller_type().to_string());
|
||||
}
|
||||
/// Contructs a new `CpuSetController` with `root` serving as the root of the control group.
|
||||
pub fn new(root: PathBuf, v2: bool) -> Self {
|
||||
Self {
|
||||
base: root.clone(),
|
||||
path: root,
|
||||
|
||||
@@ -155,13 +155,11 @@ impl ControllerInternal for DevicesController {
|
||||
// get the resources that apply to this controller
|
||||
let res: &DeviceResources = &res.devices;
|
||||
|
||||
if res.update_values {
|
||||
for i in &res.devices {
|
||||
if i.allow {
|
||||
let _ = self.allow_device(i.devtype, i.major, i.minor, &i.access);
|
||||
} else {
|
||||
let _ = self.deny_device(i.devtype, i.major, i.minor, &i.access);
|
||||
}
|
||||
for i in &res.devices {
|
||||
if i.allow {
|
||||
let _ = self.allow_device(i.devtype, i.major, i.minor, &i.access);
|
||||
} else {
|
||||
let _ = self.deny_device(i.devtype, i.major, i.minor, &i.access);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,10 +189,8 @@ impl<'a> From<&'a Subsystem> for &'a DevicesController {
|
||||
}
|
||||
|
||||
impl DevicesController {
|
||||
/// Constructs a new `DevicesController` with `oroot` serving as the root of the control group.
|
||||
pub fn new(oroot: PathBuf) -> Self {
|
||||
let mut root = oroot;
|
||||
root.push(Self::controller_type().to_string());
|
||||
/// Constructs a new `DevicesController` with `root` serving as the root of the control group.
|
||||
pub fn new(root: PathBuf) -> Self {
|
||||
Self {
|
||||
base: root.clone(),
|
||||
path: root,
|
||||
|
||||
10
src/error.rs
10
src/error.rs
@@ -19,6 +19,9 @@ pub enum ErrorKind {
|
||||
/// An error occured while trying to read from a control group file.
|
||||
ReadFailed,
|
||||
|
||||
/// An error occured while trying to remove a control group.
|
||||
RemoveFailed,
|
||||
|
||||
/// An error occured while trying to parse a value from a control group file.
|
||||
///
|
||||
/// In the future, there will be some information attached to this field.
|
||||
@@ -55,6 +58,7 @@ impl fmt::Display for Error {
|
||||
ErrorKind::Common(s) => s.clone(),
|
||||
ErrorKind::WriteFailed => "unable to write to a control group file".to_string(),
|
||||
ErrorKind::ReadFailed => "unable to read a control group file".to_string(),
|
||||
ErrorKind::RemoveFailed => "unable to remove a control group".to_string(),
|
||||
ErrorKind::ParseError => "unable to parse control group file".to_string(),
|
||||
ErrorKind::InvalidOperation => "the requested operation is invalid".to_string(),
|
||||
ErrorKind::InvalidPath => "the given path is invalid".to_string(),
|
||||
@@ -62,7 +66,11 @@ impl fmt::Display for Error {
|
||||
ErrorKind::Other => "an unknown error".to_string(),
|
||||
};
|
||||
|
||||
write!(f, "{}", msg)
|
||||
if let Some(cause) = &self.cause {
|
||||
write!(f, "{} caused by: {:?}", msg, cause)
|
||||
} else {
|
||||
write!(f, "{}", msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -82,12 +82,8 @@ impl<'a> From<&'a Subsystem> for &'a FreezerController {
|
||||
}
|
||||
|
||||
impl FreezerController {
|
||||
/// Contructs a new `FreezerController` with `oroot` serving as the root of the control group.
|
||||
pub fn new(oroot: PathBuf, v2: bool) -> Self {
|
||||
let mut root = oroot;
|
||||
if !v2 {
|
||||
root.push(Self::controller_type().to_string());
|
||||
}
|
||||
/// Contructs a new `FreezerController` with `root` serving as the root of the control group.
|
||||
pub fn new(root: PathBuf, v2: bool) -> Self {
|
||||
Self {
|
||||
base: root.clone(),
|
||||
path: root,
|
||||
|
||||
@@ -9,12 +9,9 @@
|
||||
//! Currently, we only support the cgroupv1 hierarchy, but in the future we will add support for
|
||||
//! the Unified Hierarchy.
|
||||
|
||||
use std::fs::{self, File};
|
||||
use std::io::BufRead;
|
||||
use std::io::BufReader;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use log::*;
|
||||
use procinfo::pid::{mountinfo_self, Mountinfo};
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::blkio::BlkIoController;
|
||||
use crate::cpu::CpuController;
|
||||
@@ -35,10 +32,12 @@ use crate::{Controllers, Hierarchy, Subsystem};
|
||||
use crate::cgroup::Cgroup;
|
||||
|
||||
/// The standard, original cgroup implementation. Often referred to as "cgroupv1".
|
||||
#[derive(Debug)]
|
||||
pub struct V1 {
|
||||
mount_point: String,
|
||||
mountinfo: Vec<Mountinfo>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct V2 {
|
||||
root: String,
|
||||
}
|
||||
@@ -50,80 +49,71 @@ impl Hierarchy for V1 {
|
||||
|
||||
fn subsystems(&self) -> Vec<Subsystem> {
|
||||
let mut subs = vec![];
|
||||
if self.check_support(Controllers::Pids) {
|
||||
subs.push(Subsystem::Pid(PidController::new(self.root(), false)));
|
||||
|
||||
// The cgroup writeback feature requires cooperation between memcgs and blkcgs
|
||||
// To avoid exceptions, we should add_task for blkcg before memcg(push BlkIo before Mem)
|
||||
// For more Information: https://www.alibabacloud.com/help/doc-detail/155509.htm
|
||||
if let Some(root) = self.get_mount_point(Controllers::BlkIo) {
|
||||
subs.push(Subsystem::BlkIo(BlkIoController::new(root, false)));
|
||||
}
|
||||
if self.check_support(Controllers::Mem) {
|
||||
subs.push(Subsystem::Mem(MemController::new(self.root(), false)));
|
||||
if let Some(root) = self.get_mount_point(Controllers::Mem) {
|
||||
subs.push(Subsystem::Mem(MemController::new(root, false)));
|
||||
}
|
||||
if self.check_support(Controllers::CpuSet) {
|
||||
subs.push(Subsystem::CpuSet(CpuSetController::new(self.root(), false)));
|
||||
if let Some(root) = self.get_mount_point(Controllers::Pids) {
|
||||
subs.push(Subsystem::Pid(PidController::new(root, false)));
|
||||
}
|
||||
if self.check_support(Controllers::CpuAcct) {
|
||||
subs.push(Subsystem::CpuAcct(CpuAcctController::new(self.root())));
|
||||
if let Some(root) = self.get_mount_point(Controllers::CpuSet) {
|
||||
subs.push(Subsystem::CpuSet(CpuSetController::new(root, false)));
|
||||
}
|
||||
if self.check_support(Controllers::Cpu) {
|
||||
subs.push(Subsystem::Cpu(CpuController::new(self.root(), false)));
|
||||
if let Some(root) = self.get_mount_point(Controllers::CpuAcct) {
|
||||
subs.push(Subsystem::CpuAcct(CpuAcctController::new(root)));
|
||||
}
|
||||
if self.check_support(Controllers::Devices) {
|
||||
subs.push(Subsystem::Devices(DevicesController::new(self.root())));
|
||||
if let Some(root) = self.get_mount_point(Controllers::Cpu) {
|
||||
subs.push(Subsystem::Cpu(CpuController::new(root, false)));
|
||||
}
|
||||
if self.check_support(Controllers::Freezer) {
|
||||
subs.push(Subsystem::Freezer(FreezerController::new(
|
||||
self.root(),
|
||||
false,
|
||||
)));
|
||||
if let Some(root) = self.get_mount_point(Controllers::Devices) {
|
||||
subs.push(Subsystem::Devices(DevicesController::new(root)));
|
||||
}
|
||||
if self.check_support(Controllers::NetCls) {
|
||||
subs.push(Subsystem::NetCls(NetClsController::new(self.root())));
|
||||
if let Some(root) = self.get_mount_point(Controllers::Freezer) {
|
||||
subs.push(Subsystem::Freezer(FreezerController::new(root, false)));
|
||||
}
|
||||
if self.check_support(Controllers::BlkIo) {
|
||||
subs.push(Subsystem::BlkIo(BlkIoController::new(self.root(), false)));
|
||||
if let Some(root) = self.get_mount_point(Controllers::NetCls) {
|
||||
subs.push(Subsystem::NetCls(NetClsController::new(root)));
|
||||
}
|
||||
if self.check_support(Controllers::PerfEvent) {
|
||||
subs.push(Subsystem::PerfEvent(PerfEventController::new(self.root())));
|
||||
if let Some(root) = self.get_mount_point(Controllers::PerfEvent) {
|
||||
subs.push(Subsystem::PerfEvent(PerfEventController::new(root)));
|
||||
}
|
||||
if self.check_support(Controllers::NetPrio) {
|
||||
subs.push(Subsystem::NetPrio(NetPrioController::new(self.root())));
|
||||
if let Some(root) = self.get_mount_point(Controllers::NetPrio) {
|
||||
subs.push(Subsystem::NetPrio(NetPrioController::new(root)));
|
||||
}
|
||||
if self.check_support(Controllers::HugeTlb) {
|
||||
subs.push(Subsystem::HugeTlb(HugeTlbController::new(
|
||||
self.root(),
|
||||
false,
|
||||
)));
|
||||
if let Some(root) = self.get_mount_point(Controllers::HugeTlb) {
|
||||
subs.push(Subsystem::HugeTlb(HugeTlbController::new(root, false)));
|
||||
}
|
||||
if self.check_support(Controllers::Rdma) {
|
||||
subs.push(Subsystem::Rdma(RdmaController::new(self.root())));
|
||||
if let Some(root) = self.get_mount_point(Controllers::Rdma) {
|
||||
subs.push(Subsystem::Rdma(RdmaController::new(root)));
|
||||
}
|
||||
if self.check_support(Controllers::Systemd) {
|
||||
subs.push(Subsystem::Systemd(SystemdController::new(
|
||||
self.root(),
|
||||
false,
|
||||
)));
|
||||
if let Some(root) = self.get_mount_point(Controllers::Systemd) {
|
||||
subs.push(Subsystem::Systemd(SystemdController::new(root, false)));
|
||||
}
|
||||
|
||||
subs
|
||||
}
|
||||
|
||||
fn root_control_group(&self) -> Cgroup {
|
||||
let b: &dyn Hierarchy = self as &dyn Hierarchy;
|
||||
Cgroup::load(Box::new(&*b), "".to_string())
|
||||
}
|
||||
|
||||
fn check_support(&self, sub: Controllers) -> bool {
|
||||
let root = self.root().read_dir().unwrap();
|
||||
for entry in root {
|
||||
if let Ok(entry) = entry {
|
||||
if entry.file_name().into_string().unwrap() == sub.to_string() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
Cgroup::load(auto(), "".to_string())
|
||||
}
|
||||
|
||||
fn root(&self) -> PathBuf {
|
||||
PathBuf::from(self.mount_point.clone())
|
||||
self.mountinfo
|
||||
.iter()
|
||||
.find_map(|m| {
|
||||
if m.fs_type.0 == "cgroup" {
|
||||
return Some(m.mount_point.parent().unwrap());
|
||||
}
|
||||
None
|
||||
})
|
||||
.unwrap()
|
||||
.to_path_buf()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,12 +171,7 @@ impl Hierarchy for V2 {
|
||||
}
|
||||
|
||||
fn root_control_group(&self) -> Cgroup {
|
||||
let b: &dyn Hierarchy = self as &dyn Hierarchy;
|
||||
Cgroup::load(Box::new(&*b), "".to_string())
|
||||
}
|
||||
|
||||
fn check_support(&self, _sub: Controllers) -> bool {
|
||||
return false;
|
||||
Cgroup::load(auto(), "".to_string())
|
||||
}
|
||||
|
||||
fn root(&self) -> PathBuf {
|
||||
@@ -198,11 +183,19 @@ impl V1 {
|
||||
/// Finds where control groups are mounted to and returns a hierarchy in which control groups
|
||||
/// can be created.
|
||||
pub fn new() -> V1 {
|
||||
let mount_point = find_v1_mount().unwrap();
|
||||
V1 {
|
||||
mount_point: mount_point,
|
||||
mountinfo: mountinfo_self().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_mount_point(&self, controller: Controllers) -> Option<PathBuf> {
|
||||
self.mountinfo.iter().find_map(|m| {
|
||||
if m.fs_type.0 == "cgroup" && m.super_opts.contains(&controller.to_string()) {
|
||||
return Some(m.mount_point.clone());
|
||||
}
|
||||
None
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl V2 {
|
||||
@@ -221,7 +214,7 @@ pub const UNIFIED_MOUNTPOINT: &'static str = "/sys/fs/cgroup";
|
||||
pub fn is_cgroup2_unified_mode() -> bool {
|
||||
use nix::sys::statfs;
|
||||
|
||||
let path = Path::new(UNIFIED_MOUNTPOINT);
|
||||
let path = std::path::Path::new(UNIFIED_MOUNTPOINT);
|
||||
let fs_stat = statfs::statfs(path);
|
||||
if fs_stat.is_err() {
|
||||
return false;
|
||||
@@ -260,40 +253,3 @@ pub fn auto() -> Box<dyn Hierarchy> {
|
||||
Box::new(V1::new())
|
||||
}
|
||||
}
|
||||
|
||||
fn find_v1_mount() -> Option<String> {
|
||||
// Open mountinfo so we can get a parseable mount list
|
||||
let mountinfo_path = Path::new("/proc/self/mountinfo");
|
||||
|
||||
// If /proc isn't mounted, or something else happens, then bail out
|
||||
if mountinfo_path.exists() == false {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mountinfo_file = File::open(mountinfo_path).unwrap();
|
||||
let mountinfo_reader = BufReader::new(&mountinfo_file);
|
||||
for _line in mountinfo_reader.lines() {
|
||||
let line = _line.unwrap();
|
||||
let mut fields = line.split_whitespace();
|
||||
let index = line.find(" - ").unwrap();
|
||||
let more_fields = line[index + 3..].split_whitespace().collect::<Vec<_>>();
|
||||
if more_fields.len() == 0 {
|
||||
continue;
|
||||
}
|
||||
if more_fields[0] == "cgroup" {
|
||||
if more_fields.len() < 3 {
|
||||
continue;
|
||||
}
|
||||
let cgroups_mount = fields.nth(4).unwrap();
|
||||
if let Some(parent) = std::path::Path::new(cgroups_mount).parent() {
|
||||
if let Some(path) = parent.as_os_str().to_str() {
|
||||
debug!("found cgroups {:?} from {:?}", path, cgroups_mount);
|
||||
return Some(path.to_string());
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
@@ -54,14 +54,13 @@ impl ControllerInternal for HugeTlbController {
|
||||
// get the resources that apply to this controller
|
||||
let res: &HugePageResources = &res.hugepages;
|
||||
|
||||
if res.update_values {
|
||||
for i in &res.limits {
|
||||
let _ = self.set_limit_in_bytes(&i.size, i.limit);
|
||||
if self.limit_in_bytes(&i.size)? != i.limit {
|
||||
return Err(Error::new(Other));
|
||||
}
|
||||
for i in &res.limits {
|
||||
let _ = self.set_limit_in_bytes(&i.size, i.limit);
|
||||
if self.limit_in_bytes(&i.size)? != i.limit {
|
||||
return Err(Error::new(Other));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -99,12 +98,8 @@ fn read_u64_from(mut file: File) -> Result<u64> {
|
||||
}
|
||||
|
||||
impl HugeTlbController {
|
||||
/// Constructs a new `HugeTlbController` with `oroot` serving as the root of the control group.
|
||||
pub fn new(oroot: PathBuf, v2: bool) -> Self {
|
||||
let mut root = oroot;
|
||||
if !v2 {
|
||||
root.push(Self::controller_type().to_string());
|
||||
}
|
||||
/// Constructs a new `HugeTlbController` with `root` serving as the root of the control group.
|
||||
pub fn new(root: PathBuf, v2: bool) -> Self {
|
||||
let sizes = get_hugepage_sizes().unwrap();
|
||||
Self {
|
||||
base: root.clone(),
|
||||
|
||||
169
src/lib.rs
169
src/lib.rs
@@ -7,10 +7,29 @@
|
||||
use log::*;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::fs::{self, File};
|
||||
use std::io::{BufRead, BufReader, Read, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
macro_rules! update_and_test {
|
||||
($self: ident, $set_func:ident, $value:expr, $get_func:ident) => {
|
||||
if let Some(v) = $value {
|
||||
$self.$set_func(v)?;
|
||||
if $self.$get_func()? != v {
|
||||
return Err(Error::new(Other));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! update {
|
||||
($self: ident, $set_func:ident, $value:expr) => {
|
||||
if let Some(v) = $value {
|
||||
let _ = $self.$set_func(v);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub mod blkio;
|
||||
pub mod cgroup;
|
||||
pub mod cgroup_builder;
|
||||
@@ -48,10 +67,11 @@ use crate::pid::PidController;
|
||||
use crate::rdma::RdmaController;
|
||||
use crate::systemd::SystemdController;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use crate::cgroup::Cgroup;
|
||||
|
||||
/// Contains all the subsystems that are available in this crate.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Subsystem {
|
||||
/// Controller for the `Pid` subsystem, see `PidController` for more information.
|
||||
Pid(PidController),
|
||||
@@ -84,7 +104,7 @@ pub enum Subsystem {
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
#[derive(Eq, PartialEq, Debug, Clone)]
|
||||
pub enum Controllers {
|
||||
Pids,
|
||||
Mem,
|
||||
@@ -118,7 +138,7 @@ impl Controllers {
|
||||
Controllers::NetPrio => return "net_prio".to_string(),
|
||||
Controllers::HugeTlb => return "hugetlb".to_string(),
|
||||
Controllers::Rdma => return "rdma".to_string(),
|
||||
Controllers::Systemd => return "systemd".to_string(),
|
||||
Controllers::Systemd => return "name=systemd".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,9 +209,28 @@ mod sealed {
|
||||
std::path::Path::new(p).exists()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CustomizedAttribute: ControllerInternal {
|
||||
fn set(&self, key: &str, value: &str) -> Result<()> {
|
||||
self.open_path(key, true).and_then(|mut file| {
|
||||
file.write_all(value.as_ref())
|
||||
.map_err(|e| Error::with_cause(WriteFailed, e))
|
||||
})
|
||||
}
|
||||
|
||||
fn get(&self, key: &str) -> Result<String> {
|
||||
self.open_path(key, false).and_then(|mut file: File| {
|
||||
let mut string = String::new();
|
||||
match file.read_to_string(&mut string) {
|
||||
Ok(_) => Ok(string.trim().to_owned()),
|
||||
Err(e) => Err(Error::with_cause(ReadFailed, e)),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) use crate::sealed::ControllerInternal;
|
||||
pub(crate) use crate::sealed::{ControllerInternal, CustomizedAttribute};
|
||||
|
||||
/// A Controller is a subsystem attached to the control group.
|
||||
///
|
||||
@@ -213,12 +252,21 @@ pub trait Controller {
|
||||
/// Does this controller already exist?
|
||||
fn exists(&self) -> bool;
|
||||
|
||||
/// Set notify_on_release
|
||||
fn set_notify_on_release(&self, enable: bool) -> Result<()>;
|
||||
|
||||
/// Set release_agent
|
||||
fn set_release_agent(&self, path: &str) -> Result<()>;
|
||||
|
||||
/// Delete the controller.
|
||||
fn delete(&self);
|
||||
fn delete(&self) -> Result<()>;
|
||||
|
||||
/// Attach a task to this controller.
|
||||
fn add_task(&self, pid: &CgroupPid) -> Result<()>;
|
||||
|
||||
/// Attach a task to this controller.
|
||||
fn add_task_by_tgid(&self, pid: &CgroupPid) -> Result<()>;
|
||||
|
||||
/// Get the list of tasks that this controller has.
|
||||
fn tasks(&self) -> Vec<CgroupPid>;
|
||||
|
||||
@@ -250,20 +298,38 @@ where
|
||||
|
||||
match ::std::fs::create_dir_all(self.get_path()) {
|
||||
Ok(_) => self.post_create(),
|
||||
Err(e) => warn!("error create_dir {:?}", e),
|
||||
Err(e) => warn!("error create_dir: {:?} error: {:?}", self.get_path(), e),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set notify_on_release
|
||||
fn set_notify_on_release(&self, enable: bool) -> Result<()> {
|
||||
self.open_path("notify_on_release", true)
|
||||
.and_then(|mut file| {
|
||||
write!(file, "{}", enable as i32)
|
||||
.map_err(|e| Error::with_cause(ErrorKind::WriteFailed, e))
|
||||
})
|
||||
}
|
||||
|
||||
/// Set release_agent
|
||||
fn set_release_agent(&self, path: &str) -> Result<()> {
|
||||
self.open_path("release_agent", true).and_then(|mut file| {
|
||||
file.write_all(path.as_bytes())
|
||||
.map_err(|e| Error::with_cause(ErrorKind::WriteFailed, e))
|
||||
})
|
||||
}
|
||||
/// Does this controller already exist?
|
||||
fn exists(&self) -> bool {
|
||||
self.get_path().exists()
|
||||
}
|
||||
|
||||
/// Delete the controller.
|
||||
fn delete(&self) {
|
||||
if self.get_path().exists() {
|
||||
libc_rmdir(self.get_path().to_str().unwrap());
|
||||
fn delete(&self) -> Result<()> {
|
||||
if !self.get_path().exists() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
fs::remove_dir(self.get_path()).map_err(|e| Error::with_cause(ErrorKind::RemoveFailed, e))
|
||||
}
|
||||
|
||||
/// Attach a task to this controller.
|
||||
@@ -278,6 +344,14 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
/// Attach a task to this controller by thread group id.
|
||||
fn add_task_by_tgid(&self, pid: &CgroupPid) -> Result<()> {
|
||||
self.open_path("cgroup.procs", true).and_then(|mut file| {
|
||||
file.write_all(pid.pid.to_string().as_ref())
|
||||
.map_err(|e| Error::with_cause(ErrorKind::WriteFailed, e))
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the list of tasks that this controller has.
|
||||
fn tasks(&self) -> Vec<CgroupPid> {
|
||||
let mut file = "tasks";
|
||||
@@ -311,7 +385,7 @@ pub trait ControllIdentifier {
|
||||
|
||||
/// Control group hierarchy (right now, only V1 is supported, but in the future Unified will be
|
||||
/// implemented as well).
|
||||
pub trait Hierarchy {
|
||||
pub trait Hierarchy: std::fmt::Debug + Send {
|
||||
/// Returns what subsystems are supported by the hierarchy.
|
||||
fn subsystems(&self) -> Vec<Subsystem>;
|
||||
|
||||
@@ -322,75 +396,79 @@ pub trait Hierarchy {
|
||||
fn root_control_group(&self) -> Cgroup;
|
||||
|
||||
fn v2(&self) -> bool;
|
||||
|
||||
/// Checks whether a certain subsystem is supported in the hierarchy.
|
||||
///
|
||||
/// This is an internal function and should not be used.
|
||||
#[doc(hidden)]
|
||||
fn check_support(&self, sub: Controllers) -> bool;
|
||||
}
|
||||
|
||||
/// Resource limits for the memory subsystem.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Default)]
|
||||
pub struct MemoryResources {
|
||||
/// Whether values should be applied to the controller.
|
||||
pub update_values: bool,
|
||||
/// How much memory (in bytes) can the kernel consume.
|
||||
pub kernel_memory_limit: i64,
|
||||
pub kernel_memory_limit: Option<i64>,
|
||||
/// Upper limit of memory usage of the control group's tasks.
|
||||
pub memory_hard_limit: i64,
|
||||
pub memory_hard_limit: Option<i64>,
|
||||
/// How much memory the tasks in the control group can use when the system is under memory
|
||||
/// pressure.
|
||||
pub memory_soft_limit: i64,
|
||||
pub memory_soft_limit: Option<i64>,
|
||||
/// How much of the kernel's memory (in bytes) can be used for TCP-related buffers.
|
||||
pub kernel_tcp_memory_limit: i64,
|
||||
pub kernel_tcp_memory_limit: Option<i64>,
|
||||
/// How much memory and swap together can the tasks in the control group use.
|
||||
pub memory_swap_limit: i64,
|
||||
pub memory_swap_limit: Option<i64>,
|
||||
/// Controls the tendency of the kernel to swap out parts of the address space of the tasks to
|
||||
/// disk. Lower value implies less likely.
|
||||
///
|
||||
/// Note, however, that a value of zero does not mean the process is never swapped out. Use the
|
||||
/// traditional `mlock(2)` system call for that purpose.
|
||||
pub swappiness: u64,
|
||||
pub swappiness: Option<u64>,
|
||||
/// Customized key-value attributes
|
||||
///
|
||||
/// # Usage:
|
||||
/// ```
|
||||
/// let resource = &mut cgroups::Resources::default();
|
||||
/// resource.memory.attrs.insert("memory.numa_balancing", "true".to_string());
|
||||
/// // apply here
|
||||
pub attrs: std::collections::HashMap<&'static str, String>,
|
||||
}
|
||||
|
||||
/// Resources limits on the number of processes.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Default)]
|
||||
pub struct PidResources {
|
||||
/// Whether values should be applied to the controller.
|
||||
pub update_values: bool,
|
||||
/// The maximum number of processes that can exist in the control group.
|
||||
///
|
||||
/// Note that attaching processes to the control group will still succeed _even_ if the limit
|
||||
/// would be violated, however forks/clones inside the control group will have with `EAGAIN` if
|
||||
/// they would violate the limit set here.
|
||||
pub maximum_number_of_processes: MaxValue,
|
||||
pub maximum_number_of_processes: Option<MaxValue>,
|
||||
}
|
||||
|
||||
/// Resources limits about how the tasks can use the CPU.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Default)]
|
||||
pub struct CpuResources {
|
||||
/// Whether values should be applied to the controller.
|
||||
pub update_values: bool,
|
||||
// cpuset
|
||||
/// A comma-separated list of CPU IDs where the task in the control group can run. Dashes
|
||||
/// between numbers indicate ranges.
|
||||
pub cpus: Option<String>,
|
||||
/// Same syntax as the `cpus` field of this structure, but applies to memory nodes instead of
|
||||
/// processors.
|
||||
pub mems: String,
|
||||
pub mems: Option<String>,
|
||||
// cpu
|
||||
/// Weight of how much of the total CPU time should this control group get. Note that this is
|
||||
/// hierarchical, so this is weighted against the siblings of this control group.
|
||||
pub shares: u64,
|
||||
pub shares: Option<u64>,
|
||||
/// In one `period`, how much can the tasks run in nanoseconds.
|
||||
pub quota: i64,
|
||||
pub quota: Option<i64>,
|
||||
/// Period of time in nanoseconds.
|
||||
pub period: u64,
|
||||
pub period: Option<u64>,
|
||||
/// This is currently a no-operation.
|
||||
pub realtime_runtime: i64,
|
||||
pub realtime_runtime: Option<i64>,
|
||||
/// This is currently a no-operation.
|
||||
pub realtime_period: u64,
|
||||
pub realtime_period: Option<u64>,
|
||||
/// Customized key-value attributes
|
||||
/// # Usage:
|
||||
/// ```
|
||||
/// let resource = &mut cgroups::Resources::default();
|
||||
/// resource.cpu.attrs.insert("cpu.cfs_init_buffer_us", "10".to_string());
|
||||
/// // apply here
|
||||
/// ```
|
||||
pub attrs: std::collections::HashMap<&'static str, String>,
|
||||
}
|
||||
|
||||
/// A device resource that can be allowed or denied access to.
|
||||
@@ -411,8 +489,6 @@ pub struct DeviceResource {
|
||||
/// Limit the usage of devices for the control group's tasks.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Default)]
|
||||
pub struct DeviceResources {
|
||||
/// Whether values should be applied to the controller.
|
||||
pub update_values: bool,
|
||||
/// For each device in the list, the limits in the structure are applied.
|
||||
pub devices: Vec<DeviceResource>,
|
||||
}
|
||||
@@ -430,12 +506,10 @@ pub struct NetworkPriority {
|
||||
/// control group.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Default)]
|
||||
pub struct NetworkResources {
|
||||
/// Whether values should be applied to the controller.
|
||||
pub update_values: bool,
|
||||
/// The networking class identifier to attach to the packets.
|
||||
///
|
||||
/// This can then later be used in iptables and such to have special rules.
|
||||
pub class_id: u64,
|
||||
pub class_id: Option<u64>,
|
||||
/// Priority of the egress traffic for each interface.
|
||||
pub priorities: Vec<NetworkPriority>,
|
||||
}
|
||||
@@ -453,8 +527,6 @@ pub struct HugePageResource {
|
||||
/// Provides the ability to set consumption limit on each type of hugepages.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Default)]
|
||||
pub struct HugePageResources {
|
||||
/// Whether values should be applied to the controller.
|
||||
pub update_values: bool,
|
||||
/// Set a limit of consumption for each hugepages type.
|
||||
pub limits: Vec<HugePageResource>,
|
||||
}
|
||||
@@ -486,8 +558,6 @@ pub struct BlkIoDeviceThrottleResource {
|
||||
/// General block I/O resource limits.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Default)]
|
||||
pub struct BlkIoResources {
|
||||
/// Whether values should be applied to the controller.
|
||||
pub update_values: bool,
|
||||
/// The weight of the control group against descendant nodes.
|
||||
pub weight: Option<u16>,
|
||||
/// The weight of the control group against sibling nodes.
|
||||
@@ -762,15 +832,8 @@ pub fn nested_keyed_to_hashmap(mut file: File) -> Result<HashMap<String, HashMap
|
||||
Ok(h)
|
||||
}
|
||||
|
||||
/// fs::remove_dir_all or fs::remove_dir can't work with cgroup directory sometimes.
|
||||
/// with error: `Os { code: 1, kind: PermissionDenied, message: "Operation not permitted" }`
|
||||
pub fn libc_rmdir(p: &str) {
|
||||
// with int return value
|
||||
let _ = unsafe { libc::rmdir(p.as_ptr() as *const libc::c_char) };
|
||||
}
|
||||
|
||||
/// read and parse an i64 data
|
||||
pub fn read_i64_from(mut file: File) -> Result<i64> {
|
||||
fn read_i64_from(mut file: File) -> Result<i64> {
|
||||
let mut string = String::new();
|
||||
match file.read_to_string(&mut string) {
|
||||
Ok(_) => string
|
||||
|
||||
@@ -17,12 +17,13 @@ use std::sync::mpsc::Receiver;
|
||||
use crate::error::ErrorKind::*;
|
||||
use crate::error::*;
|
||||
use crate::events;
|
||||
use crate::read_i64_from;
|
||||
|
||||
use crate::flat_keyed_to_hashmap;
|
||||
|
||||
use crate::{
|
||||
ControllIdentifier, ControllerInternal, Controllers, MaxValue, MemoryResources, Resources,
|
||||
Subsystem,
|
||||
ControllIdentifier, ControllerInternal, Controllers, CustomizedAttribute, MaxValue,
|
||||
MemoryResources, Resources, Subsystem,
|
||||
};
|
||||
|
||||
/// A controller that allows controlling the `memory` subsystem of a Cgroup.
|
||||
@@ -453,26 +454,20 @@ impl ControllerInternal for MemController {
|
||||
// get the resources that apply to this controller
|
||||
let memres: &MemoryResources = &res.memory;
|
||||
|
||||
if memres.update_values {
|
||||
let _ = self.set_limit(memres.memory_hard_limit);
|
||||
let _ = self.set_soft_limit(memres.memory_soft_limit);
|
||||
let _ = self.set_kmem_limit(memres.kernel_memory_limit);
|
||||
let _ = self.set_memswap_limit(memres.memory_swap_limit);
|
||||
let _ = self.set_tcp_limit(memres.kernel_tcp_memory_limit);
|
||||
let _ = self.set_swappiness(memres.swappiness);
|
||||
}
|
||||
update!(self, set_limit, memres.memory_hard_limit);
|
||||
update!(self, set_soft_limit, memres.memory_soft_limit);
|
||||
update!(self, set_kmem_limit, memres.kernel_memory_limit);
|
||||
update!(self, set_memswap_limit, memres.memory_swap_limit);
|
||||
update!(self, set_tcp_limit, memres.kernel_tcp_memory_limit);
|
||||
update!(self, set_swappiness, memres.swappiness);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl MemController {
|
||||
/// Contructs a new `MemController` with `oroot` serving as the root of the control group.
|
||||
pub fn new(oroot: PathBuf, v2: bool) -> Self {
|
||||
let mut root = oroot;
|
||||
if !v2 {
|
||||
root.push(Self::controller_type().to_string());
|
||||
}
|
||||
/// Contructs a new `MemController` with `root` serving as the root of the control group.
|
||||
pub fn new(root: PathBuf, v2: bool) -> Self {
|
||||
Self {
|
||||
base: root.clone(),
|
||||
path: root,
|
||||
@@ -708,6 +703,11 @@ impl MemController {
|
||||
|
||||
/// Reset the kernel memory fail counter
|
||||
pub fn reset_kmem_fail_count(&self) -> Result<()> {
|
||||
// Ignore kmem because there is no kmem in cgroup v2
|
||||
if self.v2 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.open_path("memory.kmem.failcnt", true)
|
||||
.and_then(|mut file| {
|
||||
file.write_all("0".to_string().as_ref())
|
||||
@@ -717,6 +717,11 @@ impl MemController {
|
||||
|
||||
/// Reset the TCP related fail counter
|
||||
pub fn reset_tcp_fail_count(&self) -> Result<()> {
|
||||
// Ignore kmem because there is no kmem in cgroup v2
|
||||
if self.v2 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.open_path("memory.kmem.tcp.failcnt", true)
|
||||
.and_then(|mut file| {
|
||||
file.write_all("0".to_string().as_ref())
|
||||
@@ -756,6 +761,11 @@ impl MemController {
|
||||
|
||||
/// Set the kernel memory limit of the control group, in bytes.
|
||||
pub fn set_kmem_limit(&self, limit: i64) -> Result<()> {
|
||||
// Ignore kmem because there is no kmem in cgroup v2
|
||||
if self.v2 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.open_path("memory.kmem.limit_in_bytes", true)
|
||||
.and_then(|mut file| {
|
||||
file.write_all(limit.to_string().as_ref())
|
||||
@@ -777,6 +787,11 @@ impl MemController {
|
||||
|
||||
/// Set how much kernel memory can be used for TCP-related buffers by the control group.
|
||||
pub fn set_tcp_limit(&self, limit: i64) -> Result<()> {
|
||||
// Ignore kmem because there is no kmem in cgroup v2
|
||||
if self.v2 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.open_path("memory.kmem.tcp.limit_in_bytes", true)
|
||||
.and_then(|mut file| {
|
||||
file.write_all(limit.to_string().as_ref())
|
||||
@@ -804,11 +819,15 @@ impl MemController {
|
||||
///
|
||||
/// 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<()> {
|
||||
self.open_path("memory.swappiness", true)
|
||||
.and_then(|mut file| {
|
||||
file.write_all(swp.to_string().as_ref())
|
||||
.map_err(|e| Error::with_cause(WriteFailed, e))
|
||||
})
|
||||
let mut file = "memory.swappiness";
|
||||
if self.v2 {
|
||||
file = "memory.swap.max"
|
||||
}
|
||||
|
||||
self.open_path(file, true).and_then(|mut file| {
|
||||
file.write_all(swp.to_string().as_ref())
|
||||
.map_err(|e| Error::with_cause(WriteFailed, e))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn disable_oom_killer(&self) -> Result<()> {
|
||||
@@ -834,6 +853,8 @@ impl ControllIdentifier for MemController {
|
||||
}
|
||||
}
|
||||
|
||||
impl CustomizedAttribute for MemController {}
|
||||
|
||||
impl<'a> From<&'a Subsystem> for &'a MemController {
|
||||
fn from(sub: &'a Subsystem) -> &'a MemController {
|
||||
unsafe {
|
||||
@@ -860,17 +881,6 @@ fn read_u64_from(mut file: File) -> Result<u64> {
|
||||
}
|
||||
}
|
||||
|
||||
fn read_i64_from(mut file: File) -> Result<i64> {
|
||||
let mut string = String::new();
|
||||
match file.read_to_string(&mut string) {
|
||||
Ok(_) => string
|
||||
.trim()
|
||||
.parse()
|
||||
.map_err(|e| Error::with_cause(ParseError, e)),
|
||||
Err(e) => Err(Error::with_cause(ReadFailed, e)),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_string_from(mut file: File) -> Result<String> {
|
||||
let mut string = String::new();
|
||||
match file.read_to_string(&mut string) {
|
||||
@@ -884,8 +894,6 @@ mod tests {
|
||||
use crate::memory::{
|
||||
parse_memory_stat, parse_numa_stat, parse_oom_control, MemoryStat, NumaStat, OomControl,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
|
||||
static GOOD_VALUE: &str = "\
|
||||
total=51189 N0=51189 N1=123
|
||||
file=50175 N0=50175 N1=123
|
||||
|
||||
@@ -47,12 +47,8 @@ impl ControllerInternal for NetClsController {
|
||||
// get the resources that apply to this controller
|
||||
let res: &NetworkResources = &res.network;
|
||||
|
||||
if res.update_values {
|
||||
let _ = self.set_class(res.class_id);
|
||||
if self.get_class()? != res.class_id {
|
||||
return Err(Error::new(Other));
|
||||
}
|
||||
}
|
||||
update_and_test!(self, set_class, res.class_id, get_class);
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
@@ -90,10 +86,8 @@ fn read_u64_from(mut file: File) -> Result<u64> {
|
||||
}
|
||||
|
||||
impl NetClsController {
|
||||
/// Constructs a new `NetClsController` with `oroot` serving as the root of the control group.
|
||||
pub fn new(oroot: PathBuf) -> Self {
|
||||
let mut root = oroot;
|
||||
root.push(Self::controller_type().to_string());
|
||||
/// Constructs a new `NetClsController` with `root` serving as the root of the control group.
|
||||
pub fn new(root: PathBuf) -> Self {
|
||||
Self {
|
||||
base: root.clone(),
|
||||
path: root,
|
||||
|
||||
@@ -48,10 +48,8 @@ impl ControllerInternal for NetPrioController {
|
||||
// get the resources that apply to this controller
|
||||
let res: &NetworkResources = &res.network;
|
||||
|
||||
if res.update_values {
|
||||
for i in &res.priorities {
|
||||
let _ = self.set_if_prio(&i.name, i.priority);
|
||||
}
|
||||
for i in &res.priorities {
|
||||
let _ = self.set_if_prio(&i.name, i.priority);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -91,10 +89,8 @@ fn read_u64_from(mut file: File) -> Result<u64> {
|
||||
}
|
||||
|
||||
impl NetPrioController {
|
||||
/// Constructs a new `NetPrioController` with `oroot` serving as the root of the control group.
|
||||
pub fn new(oroot: PathBuf) -> Self {
|
||||
let mut root = oroot;
|
||||
root.push(Self::controller_type().to_string());
|
||||
/// Constructs a new `NetPrioController` with `root` serving as the root of the control group.
|
||||
pub fn new(root: PathBuf) -> Self {
|
||||
Self {
|
||||
base: root.clone(),
|
||||
path: root,
|
||||
|
||||
@@ -64,10 +64,8 @@ impl<'a> From<&'a Subsystem> for &'a PerfEventController {
|
||||
}
|
||||
|
||||
impl PerfEventController {
|
||||
/// Constructs a new `PerfEventController` with `oroot` serving as the root of the control group.
|
||||
pub fn new(oroot: PathBuf) -> Self {
|
||||
let mut root = oroot;
|
||||
root.push(Self::controller_type().to_string());
|
||||
/// Constructs a new `PerfEventController` with `root` serving as the root of the control group.
|
||||
pub fn new(root: PathBuf) -> Self {
|
||||
Self {
|
||||
base: root.clone(),
|
||||
path: root,
|
||||
|
||||
26
src/pid.rs
26
src/pid.rs
@@ -50,17 +50,13 @@ impl ControllerInternal for PidController {
|
||||
// get the resources that apply to this controller
|
||||
let pidres: &PidResources = &res.pid;
|
||||
|
||||
if pidres.update_values {
|
||||
// apply pid_max
|
||||
let _ = self.set_pid_max(pidres.maximum_number_of_processes);
|
||||
|
||||
// now, verify
|
||||
if self.get_pid_max()? == pidres.maximum_number_of_processes {
|
||||
return Ok(());
|
||||
} else {
|
||||
return Err(Error::new(Other));
|
||||
}
|
||||
}
|
||||
// apply pid_max
|
||||
update_and_test!(
|
||||
self,
|
||||
set_pid_max,
|
||||
pidres.maximum_number_of_processes,
|
||||
get_pid_max
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -105,13 +101,9 @@ fn read_u64_from(mut file: File) -> Result<u64> {
|
||||
}
|
||||
|
||||
impl PidController {
|
||||
/// Constructors a new `PidController` instance, with `oroot` serving as the controller's root
|
||||
/// Constructors a new `PidController` instance, with `root` serving as the controller's root
|
||||
/// directory.
|
||||
pub fn new(oroot: PathBuf, v2: bool) -> Self {
|
||||
let mut root = oroot;
|
||||
if !v2 {
|
||||
root.push(Self::controller_type().to_string());
|
||||
}
|
||||
pub fn new(root: PathBuf, v2: bool) -> Self {
|
||||
Self {
|
||||
base: root.clone(),
|
||||
path: root,
|
||||
|
||||
@@ -75,10 +75,8 @@ fn read_string_from(mut file: File) -> Result<String> {
|
||||
}
|
||||
|
||||
impl RdmaController {
|
||||
/// Constructs a new `RdmaController` with `oroot` serving as the root of the control group.
|
||||
pub fn new(oroot: PathBuf) -> Self {
|
||||
let mut root = oroot;
|
||||
root.push(Self::controller_type().to_string());
|
||||
/// Constructs a new `RdmaController` with `root` serving as the root of the control group.
|
||||
pub fn new(root: PathBuf) -> Self {
|
||||
Self {
|
||||
base: root.clone(),
|
||||
path: root,
|
||||
|
||||
@@ -61,12 +61,8 @@ impl<'a> From<&'a Subsystem> for &'a SystemdController {
|
||||
}
|
||||
|
||||
impl SystemdController {
|
||||
/// Constructs a new `SystemdController` with `oroot` serving as the root of the control group.
|
||||
pub fn new(oroot: PathBuf, v2: bool) -> Self {
|
||||
let mut root = oroot;
|
||||
if !v2 {
|
||||
root.push(Self::controller_type().to_string());
|
||||
}
|
||||
/// Constructs a new `SystemdController` with `root` serving as the root of the control group.
|
||||
pub fn new(root: PathBuf, v2: bool) -> Self {
|
||||
Self {
|
||||
base: root.clone(),
|
||||
path: root,
|
||||
|
||||
@@ -18,12 +18,11 @@ use cgroups::*;
|
||||
#[test]
|
||||
pub fn test_cpu_res_build() {
|
||||
let h = cgroups::hierarchies::auto();
|
||||
let h = Box::new(&*h);
|
||||
let cg: Cgroup = CgroupBuilder::new("test_cpu_res_build", h)
|
||||
let cg: Cgroup = CgroupBuilder::new("test_cpu_res_build")
|
||||
.cpu()
|
||||
.shares(85)
|
||||
.done()
|
||||
.build();
|
||||
.build(h);
|
||||
|
||||
{
|
||||
let cpu: &CpuController = cg.controller_of().unwrap();
|
||||
@@ -31,20 +30,19 @@ pub fn test_cpu_res_build() {
|
||||
assert_eq!(cpu.shares().unwrap(), 85);
|
||||
}
|
||||
|
||||
cg.delete();
|
||||
cg.delete().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_memory_res_build() {
|
||||
let h = cgroups::hierarchies::auto();
|
||||
let h = Box::new(&*h);
|
||||
let cg: Cgroup = CgroupBuilder::new("test_memory_res_build", h)
|
||||
let cg: Cgroup = CgroupBuilder::new("test_memory_res_build")
|
||||
.memory()
|
||||
.kernel_memory_limit(128 * 1024 * 1024)
|
||||
.swappiness(70)
|
||||
.memory_hard_limit(1024 * 1024 * 1024)
|
||||
.done()
|
||||
.build();
|
||||
.build(h);
|
||||
|
||||
{
|
||||
let c: &MemController = cg.controller_of().unwrap();
|
||||
@@ -55,18 +53,17 @@ pub fn test_memory_res_build() {
|
||||
assert_eq!(c.memory_stat().limit_in_bytes, 1024 * 1024 * 1024);
|
||||
}
|
||||
|
||||
cg.delete();
|
||||
cg.delete().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_pid_res_build() {
|
||||
let h = cgroups::hierarchies::auto();
|
||||
let h = Box::new(&*h);
|
||||
let cg: Cgroup = CgroupBuilder::new("test_pid_res_build", h)
|
||||
let cg: Cgroup = CgroupBuilder::new("test_pid_res_build")
|
||||
.pid()
|
||||
.maximum_number_of_processes(MaxValue::Value(123))
|
||||
.done()
|
||||
.build();
|
||||
.build(h);
|
||||
|
||||
{
|
||||
let c: &PidController = cg.controller_of().unwrap();
|
||||
@@ -74,19 +71,18 @@ pub fn test_pid_res_build() {
|
||||
assert_eq!(c.get_pid_max().unwrap(), MaxValue::Value(123));
|
||||
}
|
||||
|
||||
cg.delete();
|
||||
cg.delete().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // ignore this test for now, not sure why my kernel doesn't like it
|
||||
pub fn test_devices_res_build() {
|
||||
let h = cgroups::hierarchies::auto();
|
||||
let h = Box::new(&*h);
|
||||
let cg: Cgroup = CgroupBuilder::new("test_devices_res_build", h)
|
||||
let cg: Cgroup = CgroupBuilder::new("test_devices_res_build")
|
||||
.devices()
|
||||
.device(1, 6, DeviceType::Char, true, vec![DevicePermissions::Read])
|
||||
.done()
|
||||
.build();
|
||||
.build(h);
|
||||
|
||||
{
|
||||
let c: &DevicesController = cg.controller_of().unwrap();
|
||||
@@ -102,7 +98,7 @@ pub fn test_devices_res_build() {
|
||||
}]
|
||||
);
|
||||
}
|
||||
cg.delete();
|
||||
cg.delete().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -112,19 +108,18 @@ pub fn test_network_res_build() {
|
||||
// FIXME add cases for v2
|
||||
return;
|
||||
}
|
||||
let h = Box::new(&*h);
|
||||
let cg: Cgroup = CgroupBuilder::new("test_network_res_build", h)
|
||||
let cg: Cgroup = CgroupBuilder::new("test_network_res_build")
|
||||
.network()
|
||||
.class_id(1337)
|
||||
.done()
|
||||
.build();
|
||||
.build(h);
|
||||
|
||||
{
|
||||
let c: &NetClsController = cg.controller_of().unwrap();
|
||||
assert!(c.get_class().is_ok());
|
||||
assert_eq!(c.get_class().unwrap(), 1337);
|
||||
}
|
||||
cg.delete();
|
||||
cg.delete().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -134,12 +129,11 @@ pub fn test_hugepages_res_build() {
|
||||
// FIXME add cases for v2
|
||||
return;
|
||||
}
|
||||
let h = Box::new(&*h);
|
||||
let cg: Cgroup = CgroupBuilder::new("test_hugepages_res_build", h)
|
||||
let cg: Cgroup = CgroupBuilder::new("test_hugepages_res_build")
|
||||
.hugepages()
|
||||
.limit("2MB".to_string(), 4 * 2 * 1024 * 1024)
|
||||
.done()
|
||||
.build();
|
||||
.build(h);
|
||||
|
||||
{
|
||||
let c: &HugeTlbController = cg.controller_of().unwrap();
|
||||
@@ -149,23 +143,22 @@ pub fn test_hugepages_res_build() {
|
||||
4 * 2 * 1024 * 1024
|
||||
);
|
||||
}
|
||||
cg.delete();
|
||||
cg.delete().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // high version kernel not support `blkio.weight`
|
||||
pub fn test_blkio_res_build() {
|
||||
let h = cgroups::hierarchies::auto();
|
||||
let h = Box::new(&*h);
|
||||
let cg: Cgroup = CgroupBuilder::new("test_blkio_res_build", h)
|
||||
let cg: Cgroup = CgroupBuilder::new("test_blkio_res_build")
|
||||
.blkio()
|
||||
.weight(Some(100))
|
||||
.weight(100)
|
||||
.done()
|
||||
.build();
|
||||
.build(h);
|
||||
|
||||
{
|
||||
let c: &BlkIoController = cg.controller_of().unwrap();
|
||||
assert_eq!(c.blkio().weight, 100);
|
||||
}
|
||||
cg.delete();
|
||||
cg.delete().unwrap();
|
||||
}
|
||||
|
||||
@@ -5,15 +5,13 @@
|
||||
//
|
||||
|
||||
//! Simple unit tests about the control groups system.
|
||||
use cgroups::memory::{MemController, SetMemory};
|
||||
use cgroups::memory::MemController;
|
||||
use cgroups::Controller;
|
||||
use cgroups::{Cgroup, CgroupPid, Hierarchy, Subsystem};
|
||||
use std::collections::HashMap;
|
||||
use cgroups::{Cgroup, CgroupPid, Subsystem};
|
||||
|
||||
#[test]
|
||||
fn test_tasks_iterator() {
|
||||
let h = cgroups::hierarchies::auto();
|
||||
let h = Box::new(&*h);
|
||||
let pid = libc::pid_t::from(nix::unistd::getpid()) as u64;
|
||||
let cg = Cgroup::new(h, String::from("test_tasks_iterator"));
|
||||
{
|
||||
@@ -35,7 +33,7 @@ fn test_tasks_iterator() {
|
||||
// Verify that it was indeed removed.
|
||||
assert_eq!(tasks.next(), None);
|
||||
}
|
||||
cg.delete();
|
||||
cg.delete().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -45,13 +43,9 @@ fn test_cgroup_with_relative_paths() {
|
||||
}
|
||||
let h = cgroups::hierarchies::auto();
|
||||
let cgroup_root = h.root();
|
||||
let h = Box::new(&*h);
|
||||
let mut relative_paths = HashMap::new();
|
||||
let mem_relative_path = "/mmm/abc/def";
|
||||
relative_paths.insert("memory".to_string(), mem_relative_path.to_string());
|
||||
let cgroup_name = "test_cgroup_with_relative_paths";
|
||||
|
||||
let cg = Cgroup::new_with_relative_paths(h, String::from(cgroup_name), relative_paths);
|
||||
let cg = Cgroup::load(h, String::from(cgroup_name));
|
||||
{
|
||||
let subsystems = cg.subsystems();
|
||||
subsystems.into_iter().for_each(|sub| match sub {
|
||||
@@ -74,18 +68,13 @@ fn test_cgroup_with_relative_paths() {
|
||||
// cgroup_path = cgroup_root + relative_path + cgroup_name
|
||||
assert_eq!(
|
||||
cgroup_path,
|
||||
format!(
|
||||
"{}/memory{}/{}",
|
||||
cgroup_root.to_str().unwrap(),
|
||||
mem_relative_path,
|
||||
cgroup_name
|
||||
)
|
||||
format!("{}/memory/{}", cgroup_root.to_str().unwrap(), cgroup_name)
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
});
|
||||
}
|
||||
cg.delete();
|
||||
cg.delete().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -94,15 +83,14 @@ fn test_cgroup_v2() {
|
||||
return;
|
||||
}
|
||||
let h = cgroups::hierarchies::auto();
|
||||
let h = Box::new(&*h);
|
||||
let cg = Cgroup::new_with_relative_paths(h, String::from("test_v2"), HashMap::new());
|
||||
let cg = Cgroup::new(h, String::from("test_v2"));
|
||||
|
||||
let mem_controller: &MemController = cg.controller_of().unwrap();
|
||||
let (mem, swp, rev) = (4 * 1024 * 1000, 2 * 1024 * 1000, 1024 * 1000);
|
||||
|
||||
let _ = mem_controller.set_limit(mem);
|
||||
let _ = mem_controller.set_memswap_limit(swp);
|
||||
let _ = mem_controller.set_soft_limit(rev);
|
||||
mem_controller.set_limit(mem).unwrap();
|
||||
mem_controller.set_memswap_limit(swp).unwrap();
|
||||
mem_controller.set_soft_limit(rev).unwrap();
|
||||
|
||||
let memory_stat = mem_controller.memory_stat();
|
||||
println!("memory_stat {:?}", memory_stat);
|
||||
@@ -113,5 +101,5 @@ fn test_cgroup_v2() {
|
||||
println!("memswap {:?}", memswap);
|
||||
assert_eq!(swp, memswap.limit_in_bytes);
|
||||
|
||||
cg.delete();
|
||||
cg.delete().unwrap();
|
||||
}
|
||||
|
||||
20
tests/cpu.rs
20
tests/cpu.rs
@@ -5,15 +5,11 @@
|
||||
|
||||
//! Simple unit tests about the CPU control groups system.
|
||||
use cgroups::cpu::CpuController;
|
||||
use cgroups::error::ErrorKind;
|
||||
use cgroups::{Cgroup, CgroupPid, CpuResources, Hierarchy, Resources};
|
||||
|
||||
use std::fs;
|
||||
use cgroups::Cgroup;
|
||||
|
||||
#[test]
|
||||
fn test_cfs_quota_and_periods() {
|
||||
let h = cgroups::hierarchies::auto();
|
||||
let h = Box::new(&*h);
|
||||
let cg = Cgroup::new(h, String::from("test_cfs_quota_and_periods"));
|
||||
|
||||
let cpu_controller: &CpuController = cg.controller_of().unwrap();
|
||||
@@ -27,7 +23,7 @@ fn test_cfs_quota_and_periods() {
|
||||
assert_eq!(100000, current_peroid);
|
||||
|
||||
// case 1 set quota
|
||||
let r = cpu_controller.set_cfs_quota(2000);
|
||||
let _ = cpu_controller.set_cfs_quota(2000);
|
||||
|
||||
let current_quota = cpu_controller.cfs_quota().unwrap();
|
||||
let current_peroid = cpu_controller.cfs_period().unwrap();
|
||||
@@ -35,14 +31,16 @@ fn test_cfs_quota_and_periods() {
|
||||
assert_eq!(100000, current_peroid);
|
||||
|
||||
// case 2 set period
|
||||
cpu_controller.set_cfs_period(1000000);
|
||||
cpu_controller.set_cfs_period(1000000).unwrap();
|
||||
let current_quota = cpu_controller.cfs_quota().unwrap();
|
||||
let current_peroid = cpu_controller.cfs_period().unwrap();
|
||||
assert_eq!(2000, current_quota);
|
||||
assert_eq!(1000000, current_peroid);
|
||||
|
||||
// case 3 set both quota and period
|
||||
cpu_controller.set_cfs_quota_and_period(Some(5000), Some(100000));
|
||||
cpu_controller
|
||||
.set_cfs_quota_and_period(Some(5000), Some(100000))
|
||||
.unwrap();
|
||||
|
||||
let current_quota = cpu_controller.cfs_quota().unwrap();
|
||||
let current_peroid = cpu_controller.cfs_period().unwrap();
|
||||
@@ -50,12 +48,14 @@ fn test_cfs_quota_and_periods() {
|
||||
assert_eq!(100000, current_peroid);
|
||||
|
||||
// case 4 set both quota and period, set quota to -1
|
||||
cpu_controller.set_cfs_quota_and_period(Some(-1), None);
|
||||
cpu_controller
|
||||
.set_cfs_quota_and_period(Some(-1), None)
|
||||
.unwrap();
|
||||
|
||||
let current_quota = cpu_controller.cfs_quota().unwrap();
|
||||
let current_peroid = cpu_controller.cfs_period().unwrap();
|
||||
assert_eq!(-1, current_quota);
|
||||
assert_eq!(100000, current_peroid);
|
||||
|
||||
cg.delete();
|
||||
cg.delete().unwrap();
|
||||
}
|
||||
|
||||
@@ -6,14 +6,13 @@
|
||||
|
||||
use cgroups::cpuset::CpuSetController;
|
||||
use cgroups::error::ErrorKind;
|
||||
use cgroups::{Cgroup, CgroupPid, CpuResources, Hierarchy, Resources};
|
||||
use cgroups::{Cgroup, CgroupPid};
|
||||
|
||||
use std::fs;
|
||||
|
||||
#[test]
|
||||
fn test_cpuset_memory_pressure_root_cg() {
|
||||
let h = cgroups::hierarchies::auto();
|
||||
let h = Box::new(&*h);
|
||||
let cg = Cgroup::new(h, String::from("test_cpuset_memory_pressure_root_cg"));
|
||||
{
|
||||
let cpuset: &CpuSetController = cg.controller_of().unwrap();
|
||||
@@ -22,13 +21,12 @@ fn test_cpuset_memory_pressure_root_cg() {
|
||||
let res = cpuset.set_enable_memory_pressure(true);
|
||||
assert_eq!(res.unwrap_err().kind(), &ErrorKind::InvalidOperation);
|
||||
}
|
||||
cg.delete();
|
||||
cg.delete().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cpuset_set_cpus() {
|
||||
let h = cgroups::hierarchies::auto();
|
||||
let h = Box::new(&*h);
|
||||
let cg = Cgroup::new(h, String::from("test_cpuset_set_cpus"));
|
||||
{
|
||||
let cpuset: &CpuSetController = cg.controller_of().unwrap();
|
||||
@@ -61,13 +59,12 @@ fn test_cpuset_set_cpus() {
|
||||
assert_eq!(format!("{}-{}", set.cpus[0].0, set.cpus[0].1), cpus);
|
||||
}
|
||||
}
|
||||
cg.delete();
|
||||
cg.delete().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cpuset_set_cpus_add_task() {
|
||||
let h = cgroups::hierarchies::auto();
|
||||
let h = Box::new(&*h);
|
||||
let cg = Cgroup::new(h, String::from("test_cpuset_set_cpus_add_task/sub-dir"));
|
||||
|
||||
let cpuset: &CpuSetController = cg.controller_of().unwrap();
|
||||
@@ -92,5 +89,5 @@ fn test_cpuset_set_cpus_add_task() {
|
||||
println!("tasks after deleted: {:?}", tasks);
|
||||
assert_eq!(0, tasks.len());
|
||||
|
||||
cg.delete();
|
||||
cg.delete().unwrap();
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
//! Integration tests about the devices subsystem
|
||||
|
||||
use cgroups::devices::{DevicePermissions, DeviceType, DevicesController};
|
||||
use cgroups::{Cgroup, DeviceResource, Hierarchy};
|
||||
use cgroups::{Cgroup, DeviceResource};
|
||||
|
||||
#[test]
|
||||
fn test_devices_parsing() {
|
||||
@@ -17,22 +17,23 @@ fn test_devices_parsing() {
|
||||
}
|
||||
|
||||
let h = cgroups::hierarchies::auto();
|
||||
let h = Box::new(&*h);
|
||||
let cg = Cgroup::new(h, String::from("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,
|
||||
],
|
||||
)
|
||||
.unwrap();
|
||||
// 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.
|
||||
@@ -40,7 +41,9 @@ fn test_devices_parsing() {
|
||||
assert_eq!(allowed_devices.unwrap(), Vec::new());
|
||||
|
||||
// Now add mknod access to /dev/null device
|
||||
devices.allow_device(DeviceType::Char, 1, 3, &vec![DevicePermissions::MkNod]);
|
||||
devices
|
||||
.allow_device(DeviceType::Char, 1, 3, &vec![DevicePermissions::MkNod])
|
||||
.unwrap();
|
||||
let allowed_devices = devices.allowed_devices();
|
||||
assert!(allowed_devices.is_ok());
|
||||
let allowed_devices = allowed_devices.unwrap();
|
||||
@@ -57,12 +60,14 @@ fn test_devices_parsing() {
|
||||
);
|
||||
|
||||
// Now deny, this device explicitly.
|
||||
devices.deny_device(DeviceType::Char, 1, 3, &DevicePermissions::all());
|
||||
devices
|
||||
.deny_device(DeviceType::Char, 1, 3, &DevicePermissions::all())
|
||||
.unwrap();
|
||||
// Finally, check that.
|
||||
let allowed_devices = devices.allowed_devices();
|
||||
// Verify that there are no devices that we can access.
|
||||
assert!(allowed_devices.is_ok());
|
||||
assert_eq!(allowed_devices.unwrap(), Vec::new());
|
||||
}
|
||||
cg.delete();
|
||||
cg.delete().unwrap();
|
||||
}
|
||||
|
||||
@@ -4,12 +4,9 @@
|
||||
//
|
||||
|
||||
//! Integration tests about the hugetlb subsystem
|
||||
use cgroups::hugetlb::{self, HugeTlbController};
|
||||
use cgroups::Controller;
|
||||
use cgroups::{Cgroup, Hierarchy};
|
||||
|
||||
use cgroups::error::ErrorKind::*;
|
||||
use cgroups::error::*;
|
||||
use cgroups::hugetlb::{self, HugeTlbController};
|
||||
use cgroups::Cgroup;
|
||||
use std::fs;
|
||||
|
||||
#[test]
|
||||
@@ -20,11 +17,10 @@ fn test_hugetlb_sizes() {
|
||||
}
|
||||
|
||||
let h = cgroups::hierarchies::auto();
|
||||
let h = Box::new(&*h);
|
||||
let cg = Cgroup::new(h, String::from("test_hugetlb_sizes"));
|
||||
{
|
||||
let hugetlb_controller: &HugeTlbController = cg.controller_of().unwrap();
|
||||
let sizes = hugetlb_controller.get_sizes();
|
||||
let _ = hugetlb_controller.get_sizes();
|
||||
|
||||
// test sizes count
|
||||
let sizes = hugetlb_controller.get_sizes();
|
||||
@@ -40,7 +36,7 @@ fn test_hugetlb_sizes() {
|
||||
assert_no_error(hugetlb_controller.max_usage_in_bytes(&size));
|
||||
}
|
||||
}
|
||||
cg.delete();
|
||||
cg.delete().unwrap();
|
||||
}
|
||||
|
||||
fn assert_no_error(r: Result<u64>) {
|
||||
|
||||
@@ -11,7 +11,6 @@ use cgroups::{Cgroup, MaxValue};
|
||||
#[test]
|
||||
fn test_disable_oom_killer() {
|
||||
let h = cgroups::hierarchies::auto();
|
||||
let h = Box::new(&*h);
|
||||
let cg = Cgroup::new(h, String::from("test_disable_oom_killer"));
|
||||
{
|
||||
let mem_controller: &MemController = cg.controller_of().unwrap();
|
||||
@@ -31,7 +30,7 @@ fn test_disable_oom_killer() {
|
||||
assert_eq!(m.oom_control.oom_kill_disable, true);
|
||||
}
|
||||
}
|
||||
cg.delete();
|
||||
cg.delete().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -41,7 +40,6 @@ fn set_mem_v2() {
|
||||
return;
|
||||
}
|
||||
|
||||
let h = Box::new(&*h);
|
||||
let cg = Cgroup::new(h, String::from("set_mem_v2"));
|
||||
{
|
||||
let mem_controller: &MemController = cg.controller_of().unwrap();
|
||||
@@ -89,5 +87,5 @@ fn set_mem_v2() {
|
||||
assert_eq!(m.high, Some(MaxValue::Max));
|
||||
}
|
||||
|
||||
cg.delete();
|
||||
cg.delete().unwrap();
|
||||
}
|
||||
|
||||
@@ -7,47 +7,42 @@
|
||||
//! Integration tests about the pids subsystem
|
||||
use cgroups::pid::PidController;
|
||||
use cgroups::Controller;
|
||||
use cgroups::{Cgroup, CgroupPid, Hierarchy, MaxValue, PidResources, Resources};
|
||||
use cgroups::{Cgroup, MaxValue};
|
||||
|
||||
use nix::sys::wait::{waitpid, WaitStatus};
|
||||
use nix::unistd::{fork, ForkResult, Pid};
|
||||
use nix::unistd::{fork, ForkResult};
|
||||
|
||||
use libc::pid_t;
|
||||
|
||||
use std::thread;
|
||||
|
||||
#[test]
|
||||
fn create_and_delete_cgroup() {
|
||||
let h = cgroups::hierarchies::auto();
|
||||
let h = Box::new(&*h);
|
||||
let cg = Cgroup::new(h, String::from("create_and_delete_cgroup"));
|
||||
{
|
||||
let pidcontroller: &PidController = cg.controller_of().unwrap();
|
||||
pidcontroller.set_pid_max(MaxValue::Value(1337));
|
||||
pidcontroller.set_pid_max(MaxValue::Value(1337)).unwrap();
|
||||
let max = pidcontroller.get_pid_max();
|
||||
assert!(max.is_ok());
|
||||
assert_eq!(max.unwrap(), MaxValue::Value(1337));
|
||||
}
|
||||
cg.delete();
|
||||
cg.delete().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pids_current_is_zero() {
|
||||
let h = cgroups::hierarchies::auto();
|
||||
let h = Box::new(&*h);
|
||||
let cg = Cgroup::new(h, String::from("test_pids_current_is_zero"));
|
||||
{
|
||||
let pidcontroller: &PidController = cg.controller_of().unwrap();
|
||||
let current = pidcontroller.get_pid_current();
|
||||
assert_eq!(current.unwrap(), 0);
|
||||
}
|
||||
cg.delete();
|
||||
cg.delete().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pids_events_is_zero() {
|
||||
let h = cgroups::hierarchies::auto();
|
||||
let h = Box::new(&*h);
|
||||
let cg = Cgroup::new(h, String::from("test_pids_events_is_zero"));
|
||||
{
|
||||
let pidcontroller: &PidController = cg.controller_of().unwrap();
|
||||
@@ -55,13 +50,12 @@ fn test_pids_events_is_zero() {
|
||||
assert!(events.is_ok());
|
||||
assert_eq!(events.unwrap(), 0);
|
||||
}
|
||||
cg.delete();
|
||||
cg.delete().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pid_events_is_not_zero() {
|
||||
let h = cgroups::hierarchies::auto();
|
||||
let h = Box::new(&*h);
|
||||
let cg = Cgroup::new(h, String::from("test_pid_events_is_not_zero"));
|
||||
{
|
||||
let pids: &PidController = cg.controller_of().unwrap();
|
||||
@@ -105,5 +99,5 @@ fn test_pid_events_is_not_zero() {
|
||||
Err(_) => panic!("failed to fork"),
|
||||
}
|
||||
}
|
||||
cg.delete();
|
||||
cg.delete().unwrap();
|
||||
}
|
||||
|
||||
@@ -6,22 +6,20 @@
|
||||
|
||||
//! Integration test about setting resources using `apply()`
|
||||
use cgroups::pid::PidController;
|
||||
use cgroups::{Cgroup, Hierarchy, MaxValue, PidResources, Resources};
|
||||
use cgroups::{Cgroup, MaxValue, PidResources, Resources};
|
||||
|
||||
#[test]
|
||||
fn pid_resources() {
|
||||
let h = cgroups::hierarchies::auto();
|
||||
let h = Box::new(&*h);
|
||||
let cg = Cgroup::new(h, String::from("pid_resources"));
|
||||
{
|
||||
let res = Resources {
|
||||
pid: PidResources {
|
||||
update_values: true,
|
||||
maximum_number_of_processes: MaxValue::Value(512),
|
||||
maximum_number_of_processes: Some(MaxValue::Value(512)),
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
cg.apply(&res);
|
||||
cg.apply(&res).unwrap();
|
||||
|
||||
// verify
|
||||
let pidcontroller: &PidController = cg.controller_of().unwrap();
|
||||
@@ -29,5 +27,5 @@ fn pid_resources() {
|
||||
assert_eq!(pid_max.is_ok(), true);
|
||||
assert_eq!(pid_max.unwrap(), MaxValue::Value(512));
|
||||
}
|
||||
cg.delete();
|
||||
cg.delete().unwrap();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user