Compare commits

..

36 Commits

Author SHA1 Message Date
Fupan Li
8f6a7e0a31 Merge pull request #19 from Tim-Zhang/0.2.0
Ready to release 0.2.0
2020-12-21 11:21:07 +08:00
Tim Zhang
9baa065226 release: v0.2.0
Bump vertion to 0.2.0

Signed-off-by: Tim Zhang <tim@hyper.sh>
2020-12-17 17:56:46 +08:00
Tim Zhang
e160df0751 Make read_i64_from private and merge read_str_from to its caller
Also remove duplicated read_i64_from.

Signed-off-by: Tim Zhang <tim@hyper.sh>
2020-12-17 17:56:46 +08:00
Tim Zhang
e1e05d3a1c Make new_with_relative_paths=new and load_with_relative_paths=new in v2
Because the relative_paths is only valid for cgroup v1, the v2 use
unified hierarchy.

Signed-off-by: Tim Zhang <tim@hyper.sh>
2020-12-17 17:56:46 +08:00
Qingyuan Hou
a89f4a062e Support set notify_on_release & release_agent
Support set notify_on_release & release_agent

Signed-off-by: Qingyuan Hou <qingyuan.hou@linux.alibaba.com>
2020-12-17 17:56:46 +08:00
Tim Zhang
61a0957a65 Fix set_swappiness in cgroup v2
The file should be memory.swap.max in cgroup v2.

Signed-off-by: Tim Zhang <tim@hyper.sh>
2020-12-17 17:56:46 +08:00
Tim Zhang
059204589c Ignore kmem in cgroup v2
Because there is no kmem in cgroup v2.

Signed-off-by: Tim Zhang <tim@hyper.sh>
2020-12-17 17:56:46 +08:00
Tim Zhang
c254fffbe0 Update readme
Update readme

Signed-off-by: Tim Zhang <tim@hyper.sh>
2020-12-17 17:56:46 +08:00
Tim Zhang
438d774866 Fix test
Fix test

Signed-off-by: Tim Zhang <tim@hyper.sh>
2020-12-17 17:56:46 +08:00
Tim Zhang
42ee1bafbd Make Cgroup can be stored in struct
- Change type of hier to remove lifetimes
- impl Clone, Default, Debug for Cgroup

Signed-off-by: Tim Zhang <tim@hyper.sh>
2020-12-17 17:56:46 +08:00
Tim Zhang
b6bb5ae947 docs: Hide Re-exports
Make `pub use crate::cgroup::Cgroup` display as struct in docs.

Signed-off-by: Tim Zhang <tim@hyper.sh>
2020-12-17 17:56:46 +08:00
Tim Zhang
d2882b1d85 Print cause when println!("{}")
> Print like following:
unable to write to a control group file caused by: Os { code: 22, kind: InvalidInput, message: "Invalid argument" }) }

Signed-off-by: Tim Zhang <tim@hyper.sh>
2020-12-17 17:56:46 +08:00
Tim Zhang
abcb5ed031 Add more logs for create_dir error in controller.create
We need know the path name which failed to create.

Signed-off-by: Tim Zhang <tim@hyper.sh>
2020-12-17 17:56:46 +08:00
Tim Zhang
1f188be405 Detect subsystems and get root from /proc/self/mountinfo
Delete check_support and stop detecting subsystems by finding in the
root folders because the detecting method is not accurate.

Signed-off-by: Tim Zhang <tim@hyper.sh>
2020-12-17 17:56:46 +08:00
Tim Zhang
fbd7164c29 Fix warnings in tests
Remove following warnings
- unused import
- unused Result

Signed-off-by: Tim Zhang <tim@hyper.sh>
2020-12-17 17:56:46 +08:00
Tim Zhang
f34225411e Remove Box wrap of Cgroup.hire
It's unnecessary.

Signed-off-by: Tim Zhang <tim@hyper.sh>
2020-12-17 17:56:46 +08:00
Tim Zhang
cd998f3f9b Do not place cgroup under relative path read from cgroup by default
Add new_with_corresponding_relative_paths to do the original action.

Signed-off-by: Tim Zhang <tim@hyper.sh>
2020-12-17 17:56:46 +08:00
Tim Zhang
1ac76b69ba Make function find_v1_mount pub
The function find_v1_mount is useful for customized impl for Hierarchy.

Signed-off-by: Tim Zhang <tim@hyper.sh>
2020-12-17 17:56:46 +08:00
Tim Zhang
121f78d8e8 Expose deletion error
So that users can retry or do some aftercare.

Fixes: #18

Signed-off-by: Tim Zhang <tim@hyper.sh>
2020-12-17 17:56:46 +08:00
Tim Zhang
0f76570677 Avoid exception caused by cgroup writeback feature
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.ht

Signed-off-by: Tim Zhang <tim@hyper.sh>
2020-12-17 17:56:46 +08:00
Tim Zhang
10650e2b16 Update tests to adapt new type of fields in resource
The type has been changed to Option type.

Signed-off-by: Tim Zhang <tim@hyper.sh>
2020-12-17 17:56:46 +08:00
Tim Zhang
567cdb43b3 Use Option as resource fields, remove the update switch: update_values
Use idiomatic Option::None to represent optional fields.
This enables updates where not all fields need to be specified.

Signed-off-by: Tim Zhang <tim@hyper.sh>
2020-12-17 17:56:41 +08:00
Tim Zhang
0c18b0855e Support customized attributes for CpuController and MemController
Customized attributes are useful for customized kernels.

Usage:
	let resource = &mut cgroups::Resources::default();
	resource.cpu.attrs.insert("cpu.cfs_init_buffer_us", "10".to_string());
	// apply here

Signed-off-by: Tim Zhang <tim@hyper.sh>
2020-12-17 16:45:41 +08:00
Yang, Wei
ca610bb57e add add_task_by_tgid
Add task by writing thread group id to cgroup.procs.

Signed-off-by: Yang, Wei <wei.yang1@linux.alibaba.com>
Signed-off-by: Tim Zhang <tim@hyper.sh>
2020-12-17 15:57:22 +08:00
Fupan Li
af6fc63bed Merge pull request #17 from Tim-Zhang/minor-updates
Minor updates
2020-10-30 10:15:05 +08:00
Tim Zhang
6f9e89572e MemController: add method reset_max_usage
We need a method to reset the max memory usage recorded.

Signed-off-by: Tim Zhang <tim@hyper.sh>
2020-10-29 19:35:39 +08:00
Tim Zhang
42eb32765b Make methods to_controller and controller_name of Subsystem public
We need these functions to detect whether subsystem path exists.

Signed-off-by: Tim Zhang <tim@hyper.sh>
2020-10-29 19:32:55 +08:00
Tim Zhang
efb98108fc Fix warnings
Got:
cargo build --target x86_64-unknown-linux-musl
   Compiling cgroups v0.1.1-alpha.0 (/home/tim/project/cgroups-rs)
warning: unused import: `nix::sys::statfs`
   --> src/hierarchies.rs:220:5
    |
220 | use nix::sys::statfs;
    |     ^^^^^^^^^^^^^^^^
    |
    = note: `#[warn(unused_imports)]` on by default

warning: 1 warning emitted

Signed-off-by: Tim Zhang <tim@hyper.sh>
2020-10-29 19:31:55 +08:00
James O. D. Hunt
c310b30c52 Merge pull request #14 from liubin/enhancement/13-fix-warnings
fix build warnings
2020-10-01 17:28:16 +01:00
bin liu
152af17f8f fix build warnings
And in Travis CI, treat warnings as error
to keep from merging codes with warning.

Fixes: #13

Signed-off-by: bin liu <bin@hyper.sh>
2020-09-23 16:19:51 +08:00
Bin Liu
d18b3bac2f Merge pull request #16 from liubin/enhancement/15-add-workflow-checks
add basic GitHub workflow for basic checks
2020-09-23 16:17:33 +08:00
bin liu
414fa281cc add basic github workflow for basic checks
Basic checks includes:

- Must have commit title/body
- Must have DCO(SoC)
- Title/Body line length are limited 75/72
- Add wip/do-not-merge to label PRs that could be merged

Fixes: #15

Signed-off-by: bin liu <bin@hyper.sh>
2020-09-23 16:15:08 +08:00
Peng Tao
f0eac7859a Merge pull request #12 from liubin/fix/11-add-trivas
update Travis configs
2020-09-23 14:01:37 +08:00
bin liu
db40fe8cd3 update Travis configs
Fixes: #11

Signed-off-by: bin liu <bin@hyper.sh>
2020-09-22 20:39:18 +08:00
Fupan Li
8717524f2c Merge pull request #10 from Kvasscn/fix-type-i8-compile-error-in-aarch64
lib: fix compiling error for type i8 in aarch64 environment
2020-09-22 10:36:14 +08:00
zhanghj
cd7e737149 lib: fix compiling error for type i8 in aarch64 environment
c_char is equal to i8 on x86, but it is equal to u8 on arm arch,
so we need to use type c_char instead of i8 to avoid compiling error.

Fixes: #9

Signed-off-by: zhanghj <zhanghj.lc@inspur.com>
2020-09-21 05:40:57 -04:00
34 changed files with 724 additions and 634 deletions

21
.github/workflows/PR-wip-checks.yaml vendored Normal file
View File

@@ -0,0 +1,21 @@
name: Pull request WIP checks
on:
pull_request:
types:
- opened
- synchronize
- reopened
- edited
- labeled
- unlabeled
jobs:
pr_wip_check:
runs-on: ubuntu-latest
name: WIP Check
steps:
- name: WIP Check
uses: tim-actions/wip-check@1c2a1ca6c110026b3e2297bb2ef39e1747b5a755
with:
labels: '["do-not-merge", "wip", "rfc"]'
keywords: '["WIP", "wip", "RFC", "rfc", "dnm", "DNM", "do-not-merge"]'

View File

@@ -0,0 +1,53 @@
name: Commit Message Check
on:
pull_request:
types:
- opened
- reopened
- synchronize
env:
error_msg: |+
See the document below for help on formatting commits for the project.
https://github.com/kata-containers/community/blob/master/CONTRIBUTING.md#patch-forma
jobs:
commit-message-check:
runs-on: ubuntu-latest
name: Commit Message Check
steps:
- name: Get PR Commits
id: 'get-pr-commits'
uses: tim-actions/get-pr-commits@v1.0.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: DCO Check
uses: tim-actions/dco@2fd0504dc0d27b33f542867c300c60840c6dcb20
with:
commits: ${{ steps.get-pr-commits.outputs.commits }}
- name: Commit Body Missing Check
if: ${{ success() || failure() }}
uses: tim-actions/commit-body-check@v1.0.2
with:
commits: ${{ steps.get-pr-commits.outputs.commits }}
- name: Check Subject Line Length
if: ${{ success() || failure() }}
uses: tim-actions/commit-message-checker-with-regex@v0.3.1
with:
commits: ${{ steps.get-pr-commits.outputs.commits }}
pattern: '^.{0,75}(\n.*)*$'
error: 'Subject too long (max 75)'
post_error: ${{ env.error_msg }}
- name: Check Body Line Length
if: ${{ success() || failure() }}
uses: tim-actions/commit-message-checker-with-regex@v0.3.1
with:
commits: ${{ steps.get-pr-commits.outputs.commits }}
pattern: '^.+(\n.{0,72})*$|^.+\n\s*[^a-zA-Z\s\n]|^.+\n\S+$'
error: 'Body line too long (max 72)'
post_error: ${{ env.error_msg }}

View File

@@ -1,11 +1,31 @@
# Copyright (c) 2018 Levente Kurusa
# Copyright (c) 2020 Ant Group
#
# SPDX-License-Identifier: Apache-2.0
#
dist: bionic
os: linux
language: rust
cache: cargo
arch:
- amd64
- arm64
install:
- rustup component add rustfmt
script:
- RUSTFLAGS="--deny warnings" cargo build
- if [ "$TRAVIS_CPU_ARCH" == "amd64" ]; then cargo test -- --color always --nocapture ; fi
- cargo fmt -- --check
rust:
- stable
- beta
- 1.44.1
- nightly
matrix:
jobs:
allow_failures:
- rust: nightly
fast_finish: true
script:
- cargo build --verbose --all

View File

@@ -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"

View File

@@ -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));
// [...]

View File

@@ -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(())
@@ -396,7 +393,8 @@ impl<'a> From<&'a Subsystem> for &'a BlkIoController {
Subsystem::BlkIo(c) => c,
_ => {
assert_eq!(1, 0);
::std::mem::uninitialized()
let v = std::mem::MaybeUninit::uninit();
v.assume_init()
}
}
}
@@ -423,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,

View File

@@ -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,20 +28,41 @@ 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() {
create_v2_cgroup(self.hier.root().clone(), &self.path);
let _ret = create_v2_cgroup(self.hier.root().clone(), &self.path);
} else {
for subsystem in &self.subsystems {
subsystem.to_controller().create();
@@ -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> {
@@ -272,7 +324,7 @@ fn enable_controllers(controllers: &Vec<String>, path: &PathBuf) {
}
}
fn supported_controllers(p: &PathBuf) -> Vec<String> {
fn supported_controllers() -> Vec<String> {
let p = format!("{}/{}", UNIFIED_MOUNTPOINT, "cgroup.controllers");
let ret = fs::read_to_string(p.as_str());
ret.unwrap_or(String::new())
@@ -283,7 +335,7 @@ fn supported_controllers(p: &PathBuf) -> Vec<String> {
fn create_v2_cgroup(root: PathBuf, path: &str) -> Result<()> {
// controler list ["memory", "cpu"]
let controllers = supported_controllers(&root);
let controllers = supported_controllers();
let mut fp = root;
// enable for root
@@ -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)

View File

@@ -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,12 +57,11 @@
//! .read(6, 1, 10)
//! .write(11, 1, 100)
//! .done()
//! .build();
//! .build(h);
//! ```
use crate::error::*;
use crate::{
pid, BlkIoDeviceResource, BlkIoDeviceThrottleResource, Cgroup, DeviceResource, Hierarchy,
BlkIoDeviceResource, BlkIoDeviceThrottleResource, Cgroup, DeviceResource, Hierarchy,
HugePageResource, MaxValue, NetworkPriority, Resources,
};
@@ -71,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,
@@ -139,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);
cg.apply(&self.resources);
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,
@@ -184,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,
@@ -204,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);
@@ -225,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,
@@ -244,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,
@@ -257,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
@@ -283,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
@@ -306,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(
@@ -334,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
@@ -350,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
@@ -382,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
@@ -402,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
}
}

View File

@@ -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(())
}
@@ -111,7 +102,8 @@ impl<'a> From<&'a Subsystem> for &'a CpuController {
Subsystem::Cpu(c) => c,
_ => {
assert_eq!(1, 0);
::std::mem::uninitialized()
let v = std::mem::MaybeUninit::uninit();
v.assume_init()
}
}
}
@@ -130,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,
@@ -305,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)

View File

@@ -89,7 +89,8 @@ impl<'a> From<&'a Subsystem> for &'a CpuAcctController {
Subsystem::CpuAcct(c) => c,
_ => {
assert_eq!(1, 0);
::std::mem::uninitialized()
let v = std::mem::MaybeUninit::uninit();
v.assume_init()
}
}
}
@@ -117,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,

View File

@@ -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(())
}
@@ -125,10 +121,6 @@ impl ControllerInternal for CpuSetController {
return;
}
let current = self.get_path();
let parent = match current.parent() {
Some(p) => p,
None => return,
};
if current != self.get_base() {
match copy_from_parent(current.to_str().unwrap(), "cpuset.cpus") {
@@ -204,7 +196,8 @@ impl<'a> From<&'a Subsystem> for &'a CpuSetController {
Subsystem::CpuSet(c) => c,
_ => {
assert_eq!(1, 0);
::std::mem::uninitialized()
let v = std::mem::MaybeUninit::uninit();
v.assume_init()
}
}
}
@@ -268,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,

View File

@@ -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);
}
}
@@ -182,7 +180,8 @@ impl<'a> From<&'a Subsystem> for &'a DevicesController {
Subsystem::Devices(c) => c,
_ => {
assert_eq!(1, 0);
::std::mem::uninitialized()
let v = std::mem::MaybeUninit::uninit();
v.assume_init()
}
}
}
@@ -190,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,

View File

@@ -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.
@@ -45,7 +48,7 @@ pub enum ErrorKind {
#[derive(Debug)]
pub struct Error {
kind: ErrorKind,
cause: Option<Box<StdError + Send + Sync>>,
cause: Option<Box<dyn StdError + Send + Sync>>,
}
impl fmt::Display for Error {
@@ -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,12 +66,16 @@ 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)
}
}
}
impl StdError for Error {
fn cause(&self) -> Option<&StdError> {
fn cause(&self) -> Option<&dyn StdError> {
match self.cause {
Some(ref x) => Some(&**x),
None => None,

View File

@@ -60,7 +60,7 @@ fn register_memory_event(
}
// write to file and set mode to 0700(FIXME)
fs::write(&event_control_path, data).map_err(|e| Error::with_cause(WriteFailed, e));
fs::write(&event_control_path, data).map_err(|e| Error::with_cause(WriteFailed, e))?;
let mut eventfd_file = unsafe { File::from_raw_fd(eventfd) };
@@ -71,7 +71,7 @@ fn register_memory_event(
loop {
let mut buf = [0; 8];
match eventfd_file.read(&mut buf) {
Err(err) => {
Err(_err) => {
return;
}
Ok(_) => {}

View File

@@ -73,7 +73,8 @@ impl<'a> From<&'a Subsystem> for &'a FreezerController {
Subsystem::Freezer(c) => c,
_ => {
assert_eq!(1, 0);
::std::mem::uninitialized()
let v = std::mem::MaybeUninit::uninit();
v.assume_init()
}
}
}
@@ -81,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,

View File

@@ -8,14 +8,10 @@
//!
//! Currently, we only support the cgroupv1 hierarchy, but in the future we will add support for
//! the Unified Hierarchy.
use nix::sys::statfs;
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;
@@ -36,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,
}
@@ -51,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: &Hierarchy = self as &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()
}
}
@@ -182,12 +171,7 @@ impl Hierarchy for V2 {
}
fn root_control_group(&self) -> Cgroup {
let b: &Hierarchy = self as &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 {
@@ -199,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 {
@@ -220,7 +212,9 @@ pub const UNIFIED_MOUNTPOINT: &'static str = "/sys/fs/cgroup";
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
pub fn is_cgroup2_unified_mode() -> bool {
let path = Path::new(UNIFIED_MOUNTPOINT);
use nix::sys::statfs;
let path = std::path::Path::new(UNIFIED_MOUNTPOINT);
let fs_stat = statfs::statfs(path);
if fs_stat.is_err() {
return false;
@@ -259,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
}

View File

@@ -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(())
}
}
@@ -79,7 +78,8 @@ impl<'a> From<&'a Subsystem> for &'a HugeTlbController {
Subsystem::HugeTlb(c) => c,
_ => {
assert_eq!(1, 0);
::std::mem::uninitialized()
let v = std::mem::MaybeUninit::uninit();
v.assume_init()
}
}
}
@@ -98,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(),
@@ -225,10 +221,15 @@ pub const GB: u128 = 1000 * MB;
pub const TB: u128 = 1000 * GB;
pub const PB: u128 = 1000 * TB;
#[allow(non_upper_case_globals)]
pub const KiB: u128 = 1024;
#[allow(non_upper_case_globals)]
pub const MiB: u128 = 1024 * KiB;
#[allow(non_upper_case_globals)]
pub const GiB: u128 = 1024 * MiB;
#[allow(non_upper_case_globals)]
pub const TiB: u128 = 1024 * GiB;
#[allow(non_upper_case_globals)]
pub const PiB: u128 = 1024 * TiB;
pub fn get_binary_size_map() -> HashMap<String, u128> {

View File

@@ -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.
@@ -619,7 +689,7 @@ impl Subsystem {
}
}
fn to_controller(&self) -> &dyn Controller {
pub fn to_controller(&self) -> &dyn Controller {
match self {
Subsystem::Pid(cont) => cont,
Subsystem::Mem(cont) => cont,
@@ -638,7 +708,7 @@ impl Subsystem {
}
}
fn controller_name(&self) -> String {
pub fn controller_name(&self) -> String {
self.to_controller().control_type().to_string()
}
}
@@ -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 i8) };
}
/// 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

View File

@@ -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,
@@ -505,10 +500,11 @@ impl MemController {
// for v2
pub fn get_mem(&self) -> Result<SetMemory> {
let mut m: SetMemory = Default::default();
self.get_max_value("memory.high").map(|x| m.high = Some(x));
self.get_max_value("memory.low").map(|x| m.low = Some(x));
self.get_max_value("memory.max").map(|x| m.max = Some(x));
self.get_max_value("memory.min").map(|x| m.min = Some(x));
self.get_max_value("memory.high")
.map(|x| m.high = Some(x))?;
self.get_max_value("memory.low").map(|x| m.low = Some(x))?;
self.get_max_value("memory.max").map(|x| m.max = Some(x))?;
self.get_max_value("memory.min").map(|x| m.min = Some(x))?;
Ok(m)
}
@@ -707,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())
@@ -716,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())
@@ -732,6 +738,15 @@ impl MemController {
})
}
/// Reset the max memory usage recorded
pub fn reset_max_usage(&self) -> Result<()> {
self.open_path("memory.max_usage_in_bytes", true)
.and_then(|mut file| {
file.write_all("0".to_string().as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
})
}
/// Set the memory usage limit of the control group, in bytes.
pub fn set_limit(&self, limit: i64) -> Result<()> {
let mut file = "memory.limit_in_bytes";
@@ -746,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())
@@ -767,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())
@@ -794,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<()> {
@@ -824,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 {
@@ -831,7 +862,8 @@ impl<'a> From<&'a Subsystem> for &'a MemController {
Subsystem::Mem(c) => c,
_ => {
assert_eq!(1, 0);
::std::mem::uninitialized()
let v = std::mem::MaybeUninit::uninit();
v.assume_init()
}
}
}
@@ -849,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) {
@@ -873,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

View File

@@ -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(());
}
}
@@ -70,7 +66,8 @@ impl<'a> From<&'a Subsystem> for &'a NetClsController {
Subsystem::NetCls(c) => c,
_ => {
assert_eq!(1, 0);
::std::mem::uninitialized()
let v = std::mem::MaybeUninit::uninit();
v.assume_init()
}
}
}
@@ -89,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,

View File

@@ -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(())
@@ -71,7 +69,8 @@ impl<'a> From<&'a Subsystem> for &'a NetPrioController {
Subsystem::NetPrio(c) => c,
_ => {
assert_eq!(1, 0);
::std::mem::uninitialized()
let v = std::mem::MaybeUninit::uninit();
v.assume_init()
}
}
}
@@ -90,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,

View File

@@ -55,7 +55,8 @@ impl<'a> From<&'a Subsystem> for &'a PerfEventController {
Subsystem::PerfEvent(c) => c,
_ => {
assert_eq!(1, 0);
::std::mem::uninitialized()
let v = std::mem::MaybeUninit::uninit();
v.assume_init()
}
}
}
@@ -63,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,

View File

@@ -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(())
}
@@ -85,7 +81,8 @@ impl<'a> From<&'a Subsystem> for &'a PidController {
Subsystem::Pid(c) => c,
_ => {
assert_eq!(1, 0);
::std::mem::uninitialized()
let v = std::mem::MaybeUninit::uninit();
v.assume_init()
}
}
}
@@ -104,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,

View File

@@ -58,7 +58,8 @@ impl<'a> From<&'a Subsystem> for &'a RdmaController {
Subsystem::Rdma(c) => c,
_ => {
assert_eq!(1, 0);
::std::mem::uninitialized()
let v = std::mem::MaybeUninit::uninit();
v.assume_init()
}
}
}
@@ -74,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,

View File

@@ -7,7 +7,6 @@
//!
use std::path::PathBuf;
use crate::error::ErrorKind::*;
use crate::error::*;
use crate::{ControllIdentifier, ControllerInternal, Controllers, Resources, Subsystem};
@@ -53,7 +52,8 @@ impl<'a> From<&'a Subsystem> for &'a SystemdController {
Subsystem::Systemd(c) => c,
_ => {
assert_eq!(1, 0);
::std::mem::uninitialized()
let v = std::mem::MaybeUninit::uninit();
v.assume_init()
}
}
}
@@ -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,

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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>) {

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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();
}