Compare commits

...

61 Commits

Author SHA1 Message Date
Fupan Li
de9625ff57 Merge pull request #146 from fidencio/topic/release-v0.3.5
release: v0.3.5
2025-05-29 16:33:54 +08:00
Fupan Li
b6b65f79d1 Merge pull request #141 from michelhe/remove-outdated-docs
hierarchies: Remove outdated documentation
2025-05-29 16:31:55 +08:00
Fupan Li
547fb08c03 Merge pull request #140 from jokemanfire/dev
fix bug: if cgroup path has ":"
2025-05-29 16:31:14 +08:00
Fupan Li
ec9f3547ed Merge pull request #135 from zzzzzzzzzy9/pull-branch
rdma: read rdma.max file
2025-05-29 16:30:09 +08:00
Michel Heily
82a6aa491a hierarchies: Remove outdated documentation
The documentation in the hierarchies module is outdated, this project
does support cgroups2 and the unified hierarchy :)

Signed-off-by: Michel Heily <michelheily@gmail.com>
2025-05-29 10:21:27 +02:00
zzzzzzzzzy9
65c36214b7 rdma: read rdma.max file
This is a simple one, just support rdma.max.

Signed-off-by: zzzzzzzzzy9 <zhang.yu58@zte.com.cn>
Signed-off-by: Fabiano Fidêncio <fidencio@northflank.com>
2025-05-29 10:12:10 +02:00
Fabiano Fidêncio
e0d0b8f4bc release: v0.3.5
There are several fixes since the last release, and itś been a few years
waiting for a new one.

Signed-off-by: Fabiano Fidêncio <fidencio@northflank.com>
2025-05-29 08:53:03 +02:00
Fabiano Fidêncio
db822470e5 Merge pull request #116 from xueruini/fix-mem-panic
Use default memory stats instead of panic.
2025-05-29 08:14:51 +02:00
Fabiano Fidêncio
362373b3ec memory: Fix test_procs_iterator_cgroup test
Otherwise it simply breaks.

Signed-off-by: Alex Man <alexman@stripe.com>
Signed-off-by: Fabiano Fidêncio <fidencio@northflank.com>
2025-05-28 23:02:04 +02:00
Ruini Xue
eadbf53140 memory: Use default memory stats instead of panic.
When containers are terminated, cgroup v2 memory metrics under
/sys/fs/cgroup may disappear.  Previously, kata-agent assumed these
metrics always exist, leading to panics as reported in
kata-containers#138.

This commit returns default value (0) when memory metric files are
missing.  This behaviour aligns with cgroup v1, which also defaults to 0
memory metric files are missing:
- Memory.limit_in_bytes which maps to m.max
  https://github.com/kata-containers/cgroups-rs/blob/main/src/memory.rs#L635
- Memory.soft_limit_in_bytes which maps to m.low
  https://github.com/kata-containers/cgroups-rs/blob/main/src/memory.rs#L661
- MemSwap.fail_cnt:
  https://github.com/kata-containers/cgroups-rs/blob/main/src/memory.rs#L631

Signed-off-by: Ruini Xue <ruini.xue@gmail.com>
Signed-off-by: Alex Man <alexman@stripe.com>
Signed-off-by: Fabiano Fidêncio <fidencio@northflank.com>
2025-05-28 23:02:04 +02:00
Fabiano Fidêncio
b3c57840ee lib: Ignore dead_code warning
I'm not fmiliar with this crate, so let's just ignore the warning for
now instead of removing the code.

Signed-off-by: Fabiano Fidêncio <fidencio@northflank.com>
2025-05-28 23:02:04 +02:00
Fabiano Fidêncio
b10e52d85f Merge pull request #133 from nxfs/controllers-parsing
controllers parsing is resilient to trailing newline
2025-05-28 22:44:03 +02:00
jokemanfire
7d4d4579a3 fix bug: if cgroup path has ":"
This bug is occur in cgroup path "which has :"

Signed-off-by: jokemanfire <hu.dingyang@zte.com.cn>
2024-11-02 20:23:48 +08:00
Tim Zhang
eb3e37a4bc Merge pull request #132 from aa624545345/collect-cgroup-path
Correct path of cgroup when running a container in a container
2024-05-13 17:38:52 +08:00
乔琛 10307740
4005ad844d cgroup: collect path of cgroup when running a container in a container
Path of cgroup is wrong when running a container in a container. Use
the root path of mountinfo fetched from /proc/$(shim_pid)/mountinfo
to trim the path obtained from /proc/self/mountinfo.

Fixes: #131

Signed-off-by: 乔琛 10307740 <qiao.chen@zte.com.cn>
2024-04-16 16:40:05 +08:00
Fernand Sieber
ef3497646f controllers parsing is resilient to trailing newline
In older kernel versions (tested with 5.10.208), the cgroup.controllers
files ends with a newline. This newline is not trimmed and as such the
application later can incorrectly consider that the last controller is not
supported
2024-03-20 13:55:56 +02:00
Peng Tao
69ef63a0ef Merge pull request #130 from Tim-Zhang/release-0.3.4
release: v0.3.4
2023-10-25 15:35:45 +08:00
Tim Zhang
346844ca72 release: v0.3.4
To include #126, #127, #129

Signed-off-by: Tim Zhang <tim@hyper.sh>
2023-10-25 14:48:38 +08:00
Tim Zhang
4f1fe13d91 Merge pull request #127 from nrxus/cgroup2-identify-musl
make musl cgroupv2 identifying same as other target_envs
2023-10-25 14:42:03 +08:00
Andrés Medina
17a6c6b842 fix misidentification of cgroup v2 in musl targets
When compiling for a musl target, use the same CGROUP2_SUPER_MAGIC
constant that we use for other linux targets

Signed-off-by: Andrés Medina <andresnrx@gmail.com>
2023-10-24 15:52:35 -07:00
Tim Zhang
3c4b724433 Merge pull request #129 from mzweilz/mzweilz/main
Support setting max memory limit in cgroup v2
2023-10-23 20:00:55 +08:00
Tim Zhang
01885adb99 Merge pull request #126 from HeRaNO/use-peak
memory: use `peak` in `max_usage_in_bytes`
2023-10-23 20:00:25 +08:00
mengze
ce5f5f638e Support setting max memory limit in cgroup v2
When setting memory.max/memory.swap.max in cgroup v2, Linux uses "max"
instead of "-1" to indicate no restriction on memory usage. The
set_limit and set_memswap_limit functions of the memory controller
accept i64 as a parameter. In cgroupv2, if the parameter is -1, "max"
should be written into the cgroup file.

Fixes #128

Signed-off-by: mengze <mengze@linux.alibaba.com>
2023-10-23 19:14:16 +08:00
HeRaNO
be837166e9 memory: use peak in max_usage_in_bytes
Kernel 5.19 introduces `memory.peak` and kernel 6.5 introduces
`memory.swap.peak` to expose the memory and swap usage watermark.
This change use these values to fill `max_usage_in_bytes`.

Signed-off-by: HeRaNO <heran55@126.com>
2023-09-12 12:14:11 +08:00
Tim Zhang
8d29c194e3 Merge pull request #122 from justxuewei/release-033
release: v0.3.3
2023-08-03 15:18:13 +08:00
Xuewei Niu
8a82ad0ac2 release: v0.3.3
The included patches are

- 89edba0f85: gha: Bump Rust version to 1.69.0
- 66a93b1c3d: devices: Throw an error if device resources are invalid
- 55505e0b3e: Minor changes for cgroup and devices subsystem
- 0b6b229a38: add .path() method

Signed-off-by: Xuewei Niu <niuxuewei.nxw@antgroup.com>
2023-08-03 15:11:31 +08:00
Bin Liu
369f3bebed Merge pull request #120 from fprasx/main
add .path() method
2023-08-03 15:10:08 +08:00
Felix Prasanna
0b6b229a38 add .path() method
Allows the user to re-retrieve the path to the cgroup.

Signed-off-by: Felix Prasanna <felix@neon.tech>
2023-08-01 10:14:45 -04:00
Tim Zhang
f55bdb1775 Merge pull request #121 from justxuewei/devices
Minor changes for cgroup and devices subsystem
2023-08-01 19:46:06 +08:00
Xuewei Niu
55505e0b3e Minor changes for cgroup and devices subsystem
The changes include:

- Expose `create()` and add `exists()` for `Cgroup`: The changes
are allowed to load cgroup and test if the cgroup exists. If not exists,
performing the `create()` directly to avoid performing `new()`.
- Make path of devices cgroup error more details: The origin path is
either `devices.allow` or `devices.deny`. It not shows which cgroup it
belongs to.

Signed-off-by: Xuewei Niu <niuxuewei.nxw@antgroup.com>
2023-08-01 19:36:55 +08:00
Fupan Li
df347c1db8 Merge pull request #118 from justxuewei/devices
devices: Throw an error if device resources are invalid
2023-08-01 09:51:34 +08:00
Xuewei Niu
66a93b1c3d devices: Throw an error if device resources are invalid
The cgroup-rs should throw errors while setting devices cgroup if the rule
is invalid. For example, if a cgroup has permissions of some devices. Then
we set a `a *:* rwm` to its parent's `devices.deny`. An error should be
thrown to make users realize that it is a invalid rule.

Signed-off-by: Xuewei Niu <niuxuewei.nxw@antgroup.com>
2023-07-31 15:18:19 +08:00
Tim Zhang
ca66292f5f Merge pull request #119 from justxuewei/ga/rust1690
gha: Bump Rust version to 1.69.0
2023-07-31 15:17:24 +08:00
Xuewei Niu
89edba0f85 gha: Bump Rust version to 1.69.0
Keep Rust version the same as kata-containers repo 's version.

Signed-off-by: Xuewei Niu <niuxuewei.nxw@antgroup.com>
2023-07-31 15:01:17 +08:00
Tim Zhang
1b61c07b69 Merge pull request #114 from Tim-Zhang/release-0.3.2
release: v0.3.2
2023-03-15 15:53:10 +08:00
Tim Zhang
45e1f0c274 release: v0.3.2
To include patches #104, #113.

Signed-off-by: Tim Zhang <tim@hyper.sh>
2023-03-15 10:50:12 +08:00
Bin Liu
41b5f9c25c Merge pull request #113 from gkurz/fix-hugetlb-limit-in-bytes
Fix HugeTlbController::limit_in_bytes() for v2
2023-03-14 13:32:38 +08:00
Greg Kurz
93a59571e3 Fix HugeTlbController::limit_in_bytes() for v2
With Cgroups v2, the file to use is "max", not "limit_in_bytes".

Fixes #112

Signed-off-by: Greg Kurz <groug@kaod.org>
2023-03-10 13:37:41 +01:00
Tim Zhang
257012f2bb Merge pull request #104 from yaoyinnan/103/fix/add-task
Determine cgroup mode in add_task()
2023-02-09 11:39:29 +08:00
yaoyinnan
3dd0735324 Add UT for add_task().
Add UT for add_task() for cgroup v1 and v2.

Fixes: #103

Signed-off-by: yaoyinnan <yaoyinnan@foxmail.com>
2023-02-07 22:40:33 +08:00
yaoyinnan
6b338cf997 Determine cgroup mode in add_task()
Determine the cgroup mode in add_task() to avoid the wrong operation of the caller writing threads to cgroup.threads in non-thread mode.

Fixes: #103

Signed-off-by: yaoyinnan <yaoyinnan@foxmail.com>
2023-02-07 22:40:06 +08:00
Fupan Li
225be2cdbb Merge pull request #111 from Tim-Zhang/release-0.3.1
release: v0.3.1
2023-02-07 16:34:03 +08:00
Tim Zhang
51779d6915 release: v0.3.1
To include patches #105, #108, #110.

Signed-off-by: Tim Zhang <tim@hyper.sh>
2023-02-07 16:22:48 +08:00
Bin Liu
5ea28f076c Merge pull request #105 from amitlevy/patch-1
Fixed documentation for cpu quota and period
2023-02-07 15:10:48 +08:00
Fupan Li
aa74f34a91 Merge pull request #110 from liubin/fix/add-discard-to-blkio
blkio: add discard field to IoService
2023-02-06 14:59:08 +08:00
Amit Levy
328428ace4 Fixed documentation for cpu quota and period
nanoseconds -> microseonds

Noticed while debugging, also fits Red Hat documentation
https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/resource_management_guide/sec-cpu
, and I read the implementation to confirm

Signed-off-by: Amit Levy <amitlevy49@gmail.com>
2023-02-06 11:40:42 +08:00
Chao Wu
c8bb7e1c7e Merge pull request #108 from jongwu/error_cause
error: replace cause method with source
2023-02-05 23:05:04 +08:00
bin liu
25a1340123 blkio: add discard field to IoService
Some system has the `Discard` field in io service data, current the
library can't handle it correctly.

With this commit the blkio can get metrics whether it has the discard field.

Fixes: #109

Signed-off-by: bin liu <liubin0329@gmail.com>
2023-02-02 22:38:46 +08:00
Jianyong Wu
4203075f19 error: replace cause method with source
cause method for std::error::Error is depricated by rust, source method
is recommended.

Fixes: #107
Signed-off-by: Jianyong Wu <jianyong.wu@arm.com>
2023-02-02 14:00:11 +08:00
Fupan Li
a45ecf0884 Merge pull request #102 from Tim-Zhang/0.3.0
release: v0.3.0
2023-01-11 20:37:19 +08:00
Tim Zhang
2f60f213cc release: v0.3.0
Bump the major version for API changed in #91.

Signed-off-by: Tim Zhang <tim@hyper.sh>
2023-01-11 20:08:37 +08:00
Tim Zhang
91146f0ea3 Merge pull request #101 from Tim-Zhang/release-0.2.12
release: v0.2.12
2023-01-11 11:30:48 +08:00
Tim Zhang
e845665b3a release: v0.2.12
To include patches #91, #94, #98, #99

Signed-off-by: Tim Zhang <tim@hyper.sh>
2023-01-11 11:22:27 +08:00
Tim Zhang
55034f5b05 Merge pull request #97 from dcantah/cg2-kill
cgroup2: Add Kill method
2023-01-11 11:18:48 +08:00
Bin Liu
e2c2618707 Merge pull request #100 from dcantah/fix-clippy
Lint: Fix clippy errors
2023-01-10 14:36:47 +08:00
Danny Canter
07878325c3 cgroup2: Add Kill method
Fixes: #98

Add a Kill method for cgroups v2. This uses the new functionality in
kernels 5.14+ where writing to a cgroup.kill file will send a SIGKILL
to every process running in the cgroup. This would be useful for
kata to avoid freezing+manually sending SIGKILL+thawing process it
does currently to emulate runc's behavior.

Signed-off-by: Danny Canter <danny@dcantah.dev>
2023-01-09 21:56:47 -08:00
Danny Canter
88fb33113d Lint: Fix clippy errors
Fixes: #99

Signed-off-by: Danny Canter <danny@dcantah.dev>
2023-01-08 02:31:56 -08:00
Bin Liu
1211754b62 Merge pull request #91 from yaoyinnan/90/ftr/threaded-mode
cgroup: support to set threaded mode in cgroup v2
2023-01-05 11:57:07 +08:00
yaoyinnan
c9d02afe33 cgroup: support to set threaded mode in cgroup v2
Support to set threaded mode in cgroup v2. The premise of switching to threaded mode is that only the cgroup of cpuset, cpu and pids is supported.

Fixes: #90

Signed-off-by: yaoyinnan <yaoyinnan@foxmail.com>
2022-12-30 12:46:50 +08:00
Tim Zhang
0348f0a95e Merge pull request #94 from liubin/fix/93-include-path-in-error-message
error: introduce thiserror to add path to error message
2022-11-28 10:42:47 +08:00
bin liu
d387c6edc7 error: introduce thiserror to add path to error message
For some cgroup file operations, when failed, add the path
and the value (for write operation) to the error message.

Fixes: #93

Signed-off-by: bin liu <liubin0329@gmail.com>
2022-11-18 17:46:02 +08:00
32 changed files with 1335 additions and 488 deletions

View File

@@ -1,7 +1,7 @@
name: BVT
on: [pull_request]
env:
RUST_VERSION: 1.52
RUST_VERSION: 1.69.0
jobs:
build:
name: Build

4
.gitignore vendored
View File

@@ -8,7 +8,3 @@ Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
/target
**/*.rs.bk
Cargo.lock

View File

@@ -5,7 +5,7 @@ repository = "https://github.com/kata-containers/cgroups-rs"
keywords = ["linux", "cgroup", "containers", "isolation"]
categories = ["os", "api-bindings", "os::unix-apis"]
license = "MIT OR Apache-2.0"
version = "0.2.11"
version = "0.3.5"
authors = ["The Kata Containers community <kata-dev@lists.katacontainers.io>", "Levente Kurusa <lkurusa@acm.org>", "Sam Wilson <tecywiz121@hotmail.com>"]
edition = "2018"
homepage = "https://github.com/kata-containers/cgroups-rs"
@@ -14,9 +14,10 @@ readme = "README.md"
[dependencies]
log = "0.4"
regex = "1.1"
nix = { version = "0.24", default-features = false, features = ["event", "fs", "process"] }
nix = { version = "0.25.0", default-features = false, features = ["event", "fs", "process"] }
libc = "0.2"
serde = { version = "1.0", features = ["derive"], optional = true }
thiserror = "1"
[dev-dependencies]
libc = "0.2.76"

View File

@@ -43,7 +43,7 @@ pub struct BlkIoData {
pub data: u64,
}
#[derive(Eq, PartialEq, Debug)]
#[derive(Eq, PartialEq, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
/// Per-device activity from the control group.
pub struct IoService {
@@ -59,6 +59,8 @@ pub struct IoService {
pub sync: u64,
/// How many items were asynchronously transferred.
pub r#async: u64,
/// How many items were discarded.
pub discard: u64,
/// Total number of items transferred.
pub total: u64,
}
@@ -87,44 +89,62 @@ pub struct IoStat {
}
fn parse_io_service(s: String) -> Result<Vec<IoService>> {
s.lines()
let mut io_services = Vec::<IoService>::new();
let mut io_service = IoService::default();
let lines = s
.lines()
.filter(|x| x.split_whitespace().count() == 3)
.map(|x| {
let mut spl = x.split_whitespace();
(spl.next().unwrap(), spl.next().unwrap(), spl.next().unwrap())
(
spl.next().unwrap(),
spl.next().unwrap(),
spl.next().unwrap(),
)
})
.map(|(a, b, c)| {
let mut spl = a.split(':');
(spl.next().unwrap(), spl.next().unwrap(), b, c)
})
.collect::<Vec<_>>()
.chunks(5)
.map(|x| {
match x {
[(major, minor, "Read", read_val), (_, _, "Write", write_val),
(_, _, "Sync", sync_val), (_, _, "Async", async_val),
(_, _, "Total", total_val)] =>
Some(IoService {
major: major.parse::<i16>().unwrap(),
minor: minor.parse::<i16>().unwrap(),
read: read_val.parse::<u64>().unwrap(),
write: write_val.parse::<u64>().unwrap(),
sync: sync_val.parse::<u64>().unwrap(),
r#async: async_val.parse::<u64>().unwrap(),
total: total_val.parse::<u64>().unwrap(),
}),
_ => None,
}
})
.fold(Ok(Vec::new()), |acc, x| {
if acc.is_err() || x.is_none() {
Err(Error::new(ParseError))
} else {
let mut acc = acc.unwrap();
acc.push(x.unwrap());
Ok(acc)
}
(
spl.next().unwrap().parse::<i16>(),
spl.next().unwrap().parse::<i16>(),
b,
c,
)
})
.collect::<Vec<_>>();
for (major_num, minor_num, op, val) in lines.iter() {
let major = *major_num.as_ref().map_err(|_| Error::new(ParseError))?;
let minor = *minor_num.as_ref().map_err(|_| Error::new(ParseError))?;
if (major != io_service.major || minor != io_service.minor) && io_service.major != 0 {
// new block device
io_services.push(io_service);
io_service = IoService::default();
}
io_service.major = major;
io_service.minor = minor;
let val = val.parse::<u64>().map_err(|_| Error::new(ParseError))?;
match *op {
"Read" => io_service.read = val,
"Write" => io_service.write = val,
"Sync" => io_service.sync = val,
"Async" => io_service.r#async = val,
"Discard" => io_service.discard = val,
"Total" => io_service.total = val,
_ => {}
}
}
if io_service.major != 0 {
io_services.push(io_service);
}
Ok(io_services)
}
fn get_value(s: &str) -> String {
@@ -410,10 +430,10 @@ impl<'a> From<&'a Subsystem> for &'a BlkIoController {
impl BlkIoController {
/// Constructs a new `BlkIoController` with `root` serving as the root of the control group.
pub fn new(root: PathBuf, v2: bool) -> Self {
pub fn new(point: PathBuf, root: PathBuf, v2: bool) -> Self {
Self {
base: root.clone(),
path: root,
base: root,
path: point,
v2,
}
}
@@ -665,8 +685,12 @@ impl BlkIoController {
pub fn set_leaf_weight(&self, w: u64) -> Result<()> {
self.open_path("blkio.leaf_weight", true)
.and_then(|mut file| {
file.write_all(w.to_string().as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all(w.to_string().as_ref()).map_err(|e| {
Error::with_cause(
WriteFailed("blkio.leaf_weight".to_string(), w.to_string()),
e,
)
})
})
}
@@ -675,7 +699,15 @@ impl BlkIoController {
self.open_path("blkio.leaf_weight_device", true)
.and_then(|mut file| {
file.write_all(format!("{}:{} {}", major, minor, weight).as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
.map_err(|e| {
Error::with_cause(
WriteFailed(
"blkio.leaf_weight_device".to_string(),
format!("{}:{} {}", major, minor, weight),
),
e,
)
})
})
}
@@ -683,95 +715,112 @@ impl BlkIoController {
pub fn reset_stats(&self) -> Result<()> {
self.open_path("blkio.reset_stats", true)
.and_then(|mut file| {
file.write_all("1".to_string().as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all("1".to_string().as_ref()).map_err(|e| {
Error::with_cause(
WriteFailed("blkio.reset_stats".to_string(), "1".to_string()),
e,
)
})
})
}
/// Throttle the bytes per second rate of read operation affecting the block device
/// `major:minor` to `bps`.
pub fn throttle_read_bps_for_device(&self, major: u64, minor: u64, bps: u64) -> Result<()> {
let mut file = "blkio.throttle.read_bps_device";
let mut file_name = "blkio.throttle.read_bps_device";
let mut content = format!("{}:{} {}", major, minor, bps);
if self.v2 {
file = "io.max";
file_name = "io.max";
content = format!("{}:{} rbps={}", major, minor, bps);
}
self.open_path(file, true).and_then(|mut file| {
file.write_all(content.as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
self.open_path(file_name, true).and_then(|mut file| {
file.write_all(content.as_ref()).map_err(|e| {
Error::with_cause(WriteFailed(file_name.to_string(), content.to_string()), e)
})
})
}
/// Throttle the I/O operations per second rate of read operation affecting the block device
/// `major:minor` to `bps`.
pub fn throttle_read_iops_for_device(&self, major: u64, minor: u64, iops: u64) -> Result<()> {
let mut file = "blkio.throttle.read_iops_device";
let mut file_name = "blkio.throttle.read_iops_device";
let mut content = format!("{}:{} {}", major, minor, iops);
if self.v2 {
file = "io.max";
file_name = "io.max";
content = format!("{}:{} riops={}", major, minor, iops);
}
self.open_path(file, true).and_then(|mut file| {
file.write_all(content.as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
self.open_path(file_name, true).and_then(|mut file| {
file.write_all(content.as_ref()).map_err(|e| {
Error::with_cause(WriteFailed(file_name.to_string(), content.to_string()), e)
})
})
}
/// Throttle the bytes per second rate of write operation affecting the block device
/// `major:minor` to `bps`.
pub fn throttle_write_bps_for_device(&self, major: u64, minor: u64, bps: u64) -> Result<()> {
let mut file = "blkio.throttle.write_bps_device";
let mut file_name = "blkio.throttle.write_bps_device";
let mut content = format!("{}:{} {}", major, minor, bps);
if self.v2 {
file = "io.max";
file_name = "io.max";
content = format!("{}:{} wbps={}", major, minor, bps);
}
self.open_path(file, true).and_then(|mut file| {
file.write_all(content.as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
self.open_path(file_name, true).and_then(|mut file| {
file.write_all(content.as_ref()).map_err(|e| {
Error::with_cause(WriteFailed(file_name.to_string(), content.to_string()), e)
})
})
}
/// Throttle the I/O operations per second rate of write operation affecting the block device
/// `major:minor` to `bps`.
pub fn throttle_write_iops_for_device(&self, major: u64, minor: u64, iops: u64) -> Result<()> {
let mut file = "blkio.throttle.write_iops_device";
let mut file_name = "blkio.throttle.write_iops_device";
let mut content = format!("{}:{} {}", major, minor, iops);
if self.v2 {
file = "io.max";
file_name = "io.max";
content = format!("{}:{} wiops={}", major, minor, iops);
}
self.open_path(file, true).and_then(|mut file| {
file.write_all(content.as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
self.open_path(file_name, true).and_then(|mut file| {
file.write_all(content.as_ref()).map_err(|e| {
Error::with_cause(WriteFailed(file_name.to_string(), content.to_string()), e)
})
})
}
/// Set the weight of the control group's tasks.
pub fn set_weight(&self, w: u64) -> Result<()> {
// Attation: may not find in high kernel version.
let mut file = "blkio.weight";
let mut file_name = "blkio.weight";
if self.v2 {
file = "io.bfq.weight";
file_name = "io.bfq.weight";
}
self.open_path(file, true).and_then(|mut file| {
file.write_all(w.to_string().as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
self.open_path(file_name, true).and_then(|mut file| {
file.write_all(w.to_string().as_ref()).map_err(|e| {
Error::with_cause(WriteFailed(file_name.to_string(), w.to_string()), e)
})
})
}
/// Same as `set_weight()`, but settable per each block device.
pub fn set_weight_for_device(&self, major: u64, minor: u64, weight: u64) -> Result<()> {
let mut file = "blkio.weight_device";
let mut file_name = "blkio.weight_device";
if self.v2 {
// Attation: there is no weight for device in runc
// https://github.com/opencontainers/runc/blob/46be7b612e2533c494e6a251111de46d8e286ed5/libcontainer/cgroups/fs2/io.go#L30
// may depends on IO schedulers https://wiki.ubuntu.com/Kernel/Reference/IOSchedulers
file = "io.bfq.weight";
file_name = "io.bfq.weight";
}
self.open_path(file, true).and_then(|mut file| {
self.open_path(file_name, true).and_then(|mut file| {
file.write_all(format!("{}:{} {}", major, minor, weight).as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
.map_err(|e| {
Error::with_cause(
WriteFailed(
file_name.to_string(),
format!("{}:{} {}", major, minor, weight),
),
e,
)
})
})
}
}
@@ -788,6 +837,7 @@ mod test {
8:32 Write 0
8:32 Sync 4280320
8:32 Async 0
8:32 Discard 1
8:32 Total 4280320
8:48 Read 5705479168
8:48 Write 56096055296
@@ -804,28 +854,6 @@ mod test {
8:0 Sync 7192576
8:0 Async 0
8:0 Total 7192576
Total 61823067136
";
static TEST_WRONG_VALUE: &str = "\
8:32 Read 4280320
8:32 Write 0
8:32 Async 0
8:32 Total 4280320 8:48 Read 5705479168
8:48 Write 56096055296
8:48 Sync 11213923328
8:48 Async 50587611136
8:48 Total 61801534464
8:16 Read 10059776
8:16 Write 0
8:16 Sync 10059776
8:16 Async 0
8:16 Total 10059776
8:0 Read 7192576
8:0 Write 0
8:0 Sync 7192576
8:0 Async 0
8:0 Total 7192576
Total 61823067136
";
@@ -855,6 +883,7 @@ Total 61823067136
write: 0,
sync: 4280320,
r#async: 0,
discard: 1,
total: 4280320,
},
IoService {
@@ -864,6 +893,7 @@ Total 61823067136
write: 56096055296,
sync: 11213923328,
r#async: 50587611136,
discard: 0,
total: 61801534464,
},
IoService {
@@ -873,6 +903,7 @@ Total 61823067136
write: 0,
sync: 10059776,
r#async: 0,
discard: 0,
total: 10059776,
},
IoService {
@@ -882,12 +913,34 @@ Total 61823067136
write: 0,
sync: 7192576,
r#async: 0,
discard: 0,
total: 7192576,
}
]
);
let err = parse_io_service(TEST_WRONG_VALUE.to_string()).unwrap_err();
assert_eq!(err.kind(), &ErrorKind::ParseError,);
let invalid_values = vec![
"\
8:32 Read 4280320
8:32 Write a
8:32 Async 1
",
"\
8:32 Read 4280320
b:32 Write 1
8:32 Async 1
",
"\
8:32 Read 4280320
8:32 Write 1
8:c Async 1
",
];
for value in invalid_values {
let err = parse_io_service(value.to_string()).unwrap_err();
assert_eq!(err.kind(), &ErrorKind::ParseError,);
}
}
#[test]

View File

@@ -9,6 +9,7 @@
use crate::error::ErrorKind::*;
use crate::error::*;
use crate::hierarchies::V1;
use crate::{CgroupPid, ControllIdentifier, Controller, Hierarchy, Resources, Subsystem};
use std::collections::HashMap;
@@ -16,6 +17,11 @@ use std::convert::From;
use std::fs;
use std::path::{Path, PathBuf};
pub const CGROUP_MODE_DOMAIN: &str = "domain";
pub const CGROUP_MODE_DOMAIN_THREADED: &str = "domain threaded";
pub const CGROUP_MODE_DOMAIN_INVALID: &str = "domain invalid";
pub const CGROUP_MODE_THREADED: &str = "threaded";
/// A control group is the central structure to this crate.
///
///
@@ -36,14 +42,18 @@ pub struct Cgroup {
/// The hierarchy.
hier: Box<dyn Hierarchy>,
path: String,
/// List of controllers specifically enabled in the control group.
specified_controllers: Option<Vec<String>>,
}
impl Clone for Cgroup {
fn clone(&self) -> Self {
Cgroup {
subsystems: self.subsystems.clone(),
path: self.path.clone(),
hier: crate::hierarchies::auto(),
path: self.path.clone(),
specified_controllers: None,
}
}
}
@@ -54,48 +64,72 @@ impl Default for Cgroup {
subsystems: Vec::new(),
hier: crate::hierarchies::auto(),
path: "".to_string(),
specified_controllers: None,
}
}
}
impl Cgroup {
pub fn v2(&self) -> bool {
self.hier.v2()
}
/// Return the path the cgroup is located at.
pub fn path(&self) -> &str {
&self.path
}
/// Create this control group.
fn create(&self) {
pub fn create(&self) -> Result<()> {
if self.hier.v2() {
let _ret = create_v2_cgroup(self.hier.root(), &self.path);
create_v2_cgroup(self.hier.root(), &self.path, &self.specified_controllers)
} else {
for subsystem in &self.subsystems {
subsystem.to_controller().create();
}
Ok(())
}
}
pub fn v2(&self) -> bool {
self.hier.v2()
}
/// 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 {
pub fn new<P: AsRef<Path>>(hier: Box<dyn Hierarchy>, path: P) -> Result<Cgroup> {
let cg = Cgroup::load(hier, path);
cg.create();
cg
cg.create()?;
Ok(cg)
}
/// 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_with_specified_controllers<P: AsRef<Path>>(
hier: Box<dyn Hierarchy>,
path: P,
specified_controllers: Option<Vec<String>>,
) -> Result<Cgroup> {
let cg = if let Some(sc) = specified_controllers {
Cgroup::load_with_specified_controllers(hier, path, sc)
} else {
Cgroup::load(hier, path)
};
cg.create()?;
Ok(cg)
}
/// Create a new control group in the hierarchy `hier`, with name `path` and `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
/// 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 {
) -> Result<Cgroup> {
let cg = Cgroup::load_with_relative_paths(hier, path, relative_paths);
cg.create();
cg
cg.create()?;
Ok(cg)
}
/// Create a handle for a control group in the hierarchy `hier`, with name `path`.
@@ -116,6 +150,34 @@ impl Cgroup {
path: path.to_str().unwrap().to_string(),
subsystems,
hier,
specified_controllers: None,
}
}
/// Create a handle for a specified 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.
pub fn load_with_specified_controllers<P: AsRef<Path>>(
hier: Box<dyn Hierarchy>,
path: P,
specified_controllers: Vec<String>,
) -> Cgroup {
let path = path.as_ref();
let mut subsystems = hier.subsystems();
if path.as_os_str() != "" {
subsystems = subsystems
.into_iter()
.filter(|x| specified_controllers.contains(&x.controller_name()))
.map(|x| x.enter(path))
.collect::<Vec<_>>();
}
Cgroup {
path: path.to_str().unwrap().to_string(),
subsystems,
hier,
specified_controllers: Some(specified_controllers),
}
}
@@ -159,6 +221,7 @@ impl Cgroup {
subsystems,
hier,
path: path.to_str().unwrap().to_string(),
specified_controllers: None,
}
}
@@ -233,36 +296,137 @@ impl Cgroup {
None
}
/// Removes tasks from the control group by thread group id.
///
/// Note that this means that the task will be moved back to the root control group in the
/// hierarchy and any rules applied to that control group will _still_ apply to the proc.
pub fn remove_task_by_tgid(&self, tgid: CgroupPid) -> Result<()> {
self.hier.root_control_group().add_task_by_tgid(tgid)
}
/// Removes a task from the control group.
///
/// Note that this means that the task will be moved back to the root control group in the
/// hierarchy and any rules applied to that control group will _still_ apply to the task.
pub fn remove_task(&self, pid: CgroupPid) {
let _ = self.hier.root_control_group().add_task(pid);
pub fn remove_task(&self, tid: CgroupPid) -> Result<()> {
self.hier.root_control_group().add_task(tid)
}
/// Moves tasks to the parent control group by thread group id.
pub fn move_task_to_parent_by_tgid(&self, tgid: CgroupPid) -> Result<()> {
self.hier
.parent_control_group(&self.path)
.add_task_by_tgid(tgid)
}
/// Moves a task to the parent control group.
pub fn move_task_to_parent(&self, tid: CgroupPid) -> Result<()> {
self.hier.parent_control_group(&self.path).add_task(tid)
}
/// Return a handle to the parent control group in the hierarchy.
pub fn parent_control_group(&self) -> Cgroup {
self.hier.parent_control_group(&self.path)
}
/// Kill every process in the control group. Only supported for v2 cgroups and on
/// kernels 5.14+. This will fail with InvalidOperation if the 'cgroup.kill' file does
/// not exist.
pub fn kill(&self) -> Result<()> {
if !self.v2() {
return Err(Error::new(CgroupVersion));
}
let val = "1";
let file_name = "cgroup.kill";
let p = self.hier.root().join(self.path.clone()).join(file_name);
// If cgroup.kill doesn't exist they're not on 5.14+ so lets
// surface some error the caller can check against.
if !p.exists() {
return Err(Error::new(InvalidOperation));
}
fs::write(p, val)
.map_err(|e| Error::with_cause(WriteFailed(file_name.to_string(), val.to_string()), e))
}
/// Attach a task to the control group.
pub fn add_task(&self, pid: CgroupPid) -> Result<()> {
pub fn add_task(&self, tid: CgroupPid) -> Result<()> {
if self.v2() {
let subsystems = self.subsystems();
if !subsystems.is_empty() {
let c = subsystems[0].to_controller();
c.add_task(&pid)
let cgroup_type = self.get_cgroup_type()?;
// In cgroup v2, writing to the cgroup.threads file is only supported in thread mode.
if cgroup_type == *CGROUP_MODE_DOMAIN_THREADED
|| cgroup_type == *CGROUP_MODE_THREADED
{
// It is used to move the threads of a process into a cgroup in thread mode.
c.add_task(&tid)
} else {
// When the cgroup type is domain or domain invalid,
// cgroup.threads cannot be written.
Err(Error::new(CgroupMode))
}
} else {
Ok(())
Err(Error::new(SubsystemsEmpty))
}
} else {
self.subsystems()
.iter()
.try_for_each(|sub| sub.to_controller().add_task(&pid))
.try_for_each(|sub| sub.to_controller().add_task(&tid))
}
}
/// 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))
/// Attach tasks to the control group by thread group id.
pub fn add_task_by_tgid(&self, tgid: CgroupPid) -> Result<()> {
if self.v2() {
let subsystems = self.subsystems();
if !subsystems.is_empty() {
let c = subsystems[0].to_controller();
// It is used to move a thread of the process to a cgroup,
// and other threads of the process will also move together.
c.add_task_by_tgid(&tgid)
} else {
Err(Error::new(SubsystemsEmpty))
}
} else {
self.subsystems()
.iter()
.try_for_each(|sub| sub.to_controller().add_task_by_tgid(&tgid))
}
}
/// set cgroup.type
pub fn set_cgroup_type(&self, cgroup_type: &str) -> Result<()> {
if self.v2() {
let subsystems = self.subsystems();
if !subsystems.is_empty() {
let c = subsystems[0].to_controller();
c.set_cgroup_type(cgroup_type)
} else {
Err(Error::new(SubsystemsEmpty))
}
} else {
Err(Error::new(CgroupVersion))
}
}
/// get cgroup.type
pub fn get_cgroup_type(&self) -> Result<String> {
if self.v2() {
let subsystems = self.subsystems();
if !subsystems.is_empty() {
let c = subsystems[0].to_controller();
let cgroup_type = c.get_cgroup_type()?;
Ok(cgroup_type)
} else {
Err(Error::new(SubsystemsEmpty))
}
} else {
Err(Error::new(CgroupVersion))
}
}
/// Set notify_on_release to the control group.
@@ -281,6 +445,33 @@ impl Cgroup {
.try_for_each(|sub| sub.to_controller().set_release_agent(path))
}
/// Returns an Iterator that can be used to iterate over the procs that are currently in the
/// control group.
pub fn procs(&self) -> Vec<CgroupPid> {
// Collect the procs from all subsystems
let mut v = if self.v2() {
let subsystems = self.subsystems();
if !subsystems.is_empty() {
let c = subsystems[0].to_controller();
c.procs()
} else {
vec![]
}
} else {
self.subsystems()
.iter()
.map(|x| x.to_controller().procs())
.fold(vec![], |mut acc, mut x| {
acc.append(&mut x);
acc
})
};
v.sort();
v.dedup();
v
}
/// 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> {
@@ -307,6 +498,13 @@ impl Cgroup {
v.dedup();
v
}
/// Checks if the cgroup exists.
///
/// Returns true if at least one subsystem exists.
pub fn exists(&self) -> bool {
self.subsystems().iter().any(|e| e.to_controller().exists())
}
}
pub const UNIFIED_MOUNTPOINT: &str = "/sys/fs/cgroup";
@@ -324,13 +522,26 @@ fn supported_controllers() -> Vec<String> {
let ret = fs::read_to_string(p.as_str());
ret.unwrap_or_default()
.split(' ')
.map(|x| x.to_string())
.map(|x| x.trim().to_string())
.collect::<Vec<String>>()
}
fn create_v2_cgroup(root: PathBuf, path: &str) -> Result<()> {
fn create_v2_cgroup(
root: PathBuf,
path: &str,
specified_controllers: &Option<Vec<String>>,
) -> Result<()> {
// controler list ["memory", "cpu"]
let controllers = supported_controllers();
let controllers = if let Some(s_controllers) = specified_controllers.clone() {
if verify_supported_controllers(s_controllers.as_ref()) {
s_controllers
} else {
return Err(Error::new(ErrorKind::SpecifiedControllers));
}
} else {
supported_controllers()
};
let mut fp = root;
// enable for root
@@ -358,6 +569,16 @@ fn create_v2_cgroup(root: PathBuf, path: &str) -> Result<()> {
Ok(())
}
pub fn verify_supported_controllers(controllers: &[String]) -> bool {
let sc = supported_controllers();
for controller in controllers.iter() {
if !sc.contains(controller) {
return false;
}
}
true
}
pub fn get_cgroups_relative_paths() -> Result<HashMap<String, String>> {
let path = "/proc/self/cgroup".to_string();
get_cgroups_relative_paths_by_path(path)
@@ -368,18 +589,47 @@ pub fn get_cgroups_relative_paths_by_pid(pid: u32) -> Result<HashMap<String, Str
get_cgroups_relative_paths_by_path(path)
}
fn get_cgroup_destination(mut mount_root: String, pidpath: String) -> String {
if mount_root == "/" {
mount_root = String::from("");
}
pidpath.trim_start_matches(&mount_root).to_string()
}
pub fn existing_path(paths: HashMap<String, String>) -> Result<HashMap<String, String>> {
let mount_roots_v1 = V1::new();
let mut mount_roots_subsystems_map = HashMap::new();
for s in mount_roots_v1.subsystems().iter() {
let controller_name = s.controller_name();
let path_from_cgroup = paths
.get(&controller_name)
.ok_or(Error::new(Common(format!(
"controller {} found in mountinfo, but not found in cgroup.",
controller_name
))))?;
let path_from_mountinfo = s.to_controller().base().to_string_lossy().to_string();
let des_path = get_cgroup_destination(path_from_mountinfo, path_from_cgroup.to_owned());
mount_roots_subsystems_map.insert(controller_name, des_path);
}
Ok(mount_roots_subsystems_map)
}
fn get_cgroups_relative_paths_by_path(path: String) -> Result<HashMap<String, String>> {
let mut m = HashMap::new();
let content = fs::read_to_string(path).map_err(|e| Error::with_cause(ReadFailed, e))?;
for l in content.lines() {
let fl: Vec<&str> = l.split(':').collect();
if fl.len() != 3 {
continue;
}
let keys: Vec<&str> = fl[1].split(',').collect();
for key in &keys {
m.insert(key.to_string(), fl[2].to_string());
let content =
fs::read_to_string(path.clone()).map_err(|e| Error::with_cause(ReadFailed(path), e))?;
// cgroup path may have ":" , likes
// "2:cpu,cpuacct:/system.slice/containerd.service/test.slice:cri-containerd:96b37a2edf84351487f42039e137427f1812f678850675fac214caf597ee5e4a"
for line in content.lines() {
if let Some((first_value_part, remaining_path)) =
line.split_once(':').unwrap_or_default().1.split_once(':')
{
let keys: Vec<&str> = first_value_part.split(',').collect();
keys.iter().for_each(|key| {
m.insert(key.to_string(), remaining_path.to_string());
});
}
}
Ok(m)

View File

@@ -57,11 +57,11 @@
//! .read(6, 1, 10)
//! .write(11, 1, 100)
//! .done()
//! .build(h);
//! .build(h).unwrap();
//! ```
use crate::{
BlkIoDeviceResource, BlkIoDeviceThrottleResource, Cgroup, DeviceResource, Hierarchy,
BlkIoDeviceResource, BlkIoDeviceThrottleResource, Cgroup, DeviceResource, Error, Hierarchy,
HugePageResource, MaxValue, NetworkPriority, Resources,
};
@@ -80,6 +80,8 @@ pub struct CgroupBuilder {
name: String,
/// Internal, unsupported field: use the associated builders instead.
resources: Resources,
/// List of controllers specifically enabled in the control group.
specified_controllers: Option<Vec<String>>,
}
impl CgroupBuilder {
@@ -90,6 +92,7 @@ impl CgroupBuilder {
CgroupBuilder {
name: name.to_owned(),
resources: Resources::default(),
specified_controllers: None,
}
}
@@ -134,10 +137,22 @@ impl CgroupBuilder {
}
/// Finalize the control group, consuming the builder and creating the control group.
pub fn build(self, hier: Box<dyn Hierarchy>) -> Cgroup {
let cg = Cgroup::new(hier, self.name);
let _ret = cg.apply(&self.resources);
cg
pub fn build(self, hier: Box<dyn Hierarchy>) -> Result<Cgroup, Error> {
if let Some(controllers) = self.specified_controllers {
let cg = Cgroup::new_with_specified_controllers(hier, self.name, Some(controllers))?;
cg.apply(&self.resources)?;
Ok(cg)
} else {
let cg = Cgroup::new(hier, self.name)?;
cg.apply(&self.resources)?;
Ok(cg)
}
}
/// Specifically enable some controllers in the control group.
pub fn set_specified_controllers(mut self, specified_controllers: Vec<String>) -> Self {
self.specified_controllers = Some(specified_controllers);
self
}
}

View File

@@ -59,7 +59,6 @@ impl ControllerInternal for CpuController {
fn get_path(&self) -> &PathBuf {
&self.path
}
fn get_path_mut(&mut self) -> &mut PathBuf {
&mut self.path
}
@@ -113,10 +112,10 @@ impl<'a> From<&'a Subsystem> for &'a CpuController {
impl CpuController {
/// Contructs a new `CpuController` with `root` serving as the root of the control group.
pub fn new(root: PathBuf, v2: bool) -> Self {
pub fn new(point: PathBuf, root: PathBuf, v2: bool) -> Self {
Self {
base: root.clone(),
path: root,
base: root,
path: point,
v2,
}
}
@@ -131,7 +130,7 @@ impl CpuController {
let res = file.read_to_string(&mut s);
match res {
Ok(_) => Ok(s),
Err(e) => Err(Error::with_cause(ReadFailed, e)),
Err(e) => Err(Error::with_cause(ReadFailed("cpu.stat".to_string()), e)),
}
})
.unwrap_or_default(),
@@ -145,14 +144,15 @@ impl CpuController {
/// `shares` to `200` ensures that control group `B` receives twice as much as CPU bandwidth.
/// (Assuming both `A` and `B` are of the same parent)
pub fn set_shares(&self, shares: u64) -> Result<()> {
let mut file = "cpu.shares";
let mut file_name = "cpu.shares";
if self.v2 {
file = "cpu.weight";
file_name = "cpu.weight";
}
// NOTE: .CpuShares is not used here. Conversion is the caller's responsibility.
self.open_path(file, true).and_then(|mut file| {
file.write_all(shares.to_string().as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
self.open_path(file_name, true).and_then(|mut file| {
file.write_all(shares.to_string().as_ref()).map_err(|e| {
Error::with_cause(WriteFailed(file_name.to_string(), shares.to_string()), e)
})
})
}
@@ -174,8 +174,12 @@ impl CpuController {
}
self.open_path("cpu.cfs_period_us", true)
.and_then(|mut file| {
file.write_all(us.to_string().as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all(us.to_string().as_ref()).map_err(|e| {
Error::with_cause(
WriteFailed("cpu.cfs_period_us".to_string(), us.to_string()),
e,
)
})
})
}
@@ -200,8 +204,12 @@ impl CpuController {
}
self.open_path("cpu.cfs_quota_us", true)
.and_then(|mut file| {
file.write_all(us.to_string().as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all(us.to_string().as_ref()).map_err(|e| {
Error::with_cause(
WriteFailed("cpu.cfs_quota_us".to_string(), us.to_string()),
e,
)
})
})
}
@@ -262,23 +270,31 @@ impl CpuController {
let line = format!("{} {}", new_quota, new_period);
self.open_path("cpu.max", true).and_then(|mut file| {
file.write_all(line.as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
.map_err(|e| Error::with_cause(WriteFailed("cpu.max".to_string(), line), e))
})
}
pub fn set_rt_runtime(&self, us: i64) -> Result<()> {
self.open_path("cpu.rt_runtime_us", true)
.and_then(|mut file| {
file.write_all(us.to_string().as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all(us.to_string().as_ref()).map_err(|e| {
Error::with_cause(
WriteFailed("cpu.rt_runtime_us".to_string(), us.to_string()),
e,
)
})
})
}
pub fn set_rt_period_us(&self, us: u64) -> Result<()> {
self.open_path("cpu.rt_period_us", true)
.and_then(|mut file| {
file.write_all(us.to_string().as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all(us.to_string().as_ref()).map_err(|e| {
Error::with_cause(
WriteFailed("cpu.rt_period_us".to_string(), us.to_string()),
e,
)
})
})
}
}
@@ -288,7 +304,7 @@ 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)
.map_err(|e| Error::with_cause(ReadFailed, e))?;
.map_err(|e| Error::with_cause(ReadFailed("cpu.max".to_string()), e))?;
let fields = content.trim().split(' ').collect::<Vec<&str>>();
if fields.len() != 2 {

View File

@@ -100,10 +100,10 @@ impl<'a> From<&'a Subsystem> for &'a CpuAcctController {
impl CpuAcctController {
/// Contructs a new `CpuAcctController` with `root` serving as the root of the control group.
pub fn new(root: PathBuf) -> Self {
pub fn new(point: PathBuf, root: PathBuf) -> Self {
Self {
base: root.clone(),
path: root,
base: root,
path: point,
}
}
@@ -148,8 +148,9 @@ impl CpuAcctController {
/// Reset the statistics the kernel has gathered about the control group.
pub fn reset(&self) -> Result<()> {
self.open_path("cpuacct.usage", true).and_then(|mut file| {
file.write_all(b"0")
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all(b"0").map_err(|e| {
Error::with_cause(WriteFailed("cpuacct.usage".to_string(), "0".to_string()), e)
})
})
}
}

View File

@@ -144,7 +144,12 @@ fn find_no_empty_parent(from: &str, file: &str) -> Result<(String, Vec<PathBuf>)
let current_value =
match ::std::fs::read_to_string(current_path.clone().join(file).to_str().unwrap()) {
Ok(cpus) => String::from(cpus.trim()),
Err(e) => return Err(Error::with_cause(ReadFailed, e)),
Err(e) => {
return Err(Error::with_cause(
ReadFailed(current_path.display().to_string()),
e,
))
}
};
if !current_value.is_empty() {
@@ -177,7 +182,12 @@ fn copy_from_parent(current: &str, file: &str) -> Result<()> {
pb.push(file);
match ::std::fs::write(pb.to_str().unwrap(), value.as_bytes()) {
Ok(_) => (),
Err(e) => return Err(Error::with_cause(WriteFailed, e)),
Err(e) => {
return Err(Error::with_cause(
WriteFailed(pb.display().to_string(), pb.display().to_string()),
e,
))
}
}
}
@@ -244,10 +254,10 @@ fn parse_range(s: String) -> Result<Vec<(u64, u64)>> {
impl CpuSetController {
/// Contructs a new `CpuSetController` with `root` serving as the root of the control group.
pub fn new(root: PathBuf, v2: bool) -> Self {
pub fn new(point: PathBuf, root: PathBuf, v2: bool) -> Self {
Self {
base: root.clone(),
path: root,
base: root,
path: point,
v2,
}
}
@@ -347,11 +357,19 @@ impl CpuSetController {
self.open_path("cpuset.cpu_exclusive", true)
.and_then(|mut file| {
if b {
file.write_all(b"1")
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all(b"1").map_err(|e| {
Error::with_cause(
WriteFailed("cpuset.cpu_exclusive".to_string(), "1".to_string()),
e,
)
})
} else {
file.write_all(b"0")
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all(b"0").map_err(|e| {
Error::with_cause(
WriteFailed("cpuset.cpu_exclusive".to_string(), "0".to_string()),
e,
)
})
}
})
}
@@ -362,11 +380,19 @@ impl CpuSetController {
self.open_path("cpuset.mem_exclusive", true)
.and_then(|mut file| {
if b {
file.write_all(b"1")
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all(b"1").map_err(|e| {
Error::with_cause(
WriteFailed("cpuset.mem_exclusive".to_string(), "1".to_string()),
e,
)
})
} else {
file.write_all(b"0")
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all(b"0").map_err(|e| {
Error::with_cause(
WriteFailed("cpuset.mem_exclusive".to_string(), "0".to_string()),
e,
)
})
}
})
}
@@ -377,8 +403,9 @@ impl CpuSetController {
/// be represented via dashes.
pub fn set_cpus(&self, cpus: &str) -> Result<()> {
self.open_path("cpuset.cpus", true).and_then(|mut file| {
file.write_all(cpus.as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all(cpus.as_ref()).map_err(|e| {
Error::with_cause(WriteFailed("cpuset.cpus".to_string(), cpus.to_string()), e)
})
})
}
@@ -387,8 +414,9 @@ impl CpuSetController {
/// Syntax is the same as with `set_cpus()`.
pub fn set_mems(&self, mems: &str) -> Result<()> {
self.open_path("cpuset.mems", true).and_then(|mut file| {
file.write_all(mems.as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all(mems.as_ref()).map_err(|e| {
Error::with_cause(WriteFailed("cpuset.mems".to_string(), mems.to_string()), e)
})
})
}
@@ -401,11 +429,19 @@ impl CpuSetController {
self.open_path("cpuset.mem_hardwall", true)
.and_then(|mut file| {
if b {
file.write_all(b"1")
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all(b"1").map_err(|e| {
Error::with_cause(
WriteFailed("cpuset.mem_hardwall".to_string(), "1".to_string()),
e,
)
})
} else {
file.write_all(b"0")
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all(b"0").map_err(|e| {
Error::with_cause(
WriteFailed("cpuset.mem_hardwall".to_string(), "0".to_string()),
e,
)
})
}
})
}
@@ -416,11 +452,19 @@ impl CpuSetController {
self.open_path("cpuset.sched_load_balance", true)
.and_then(|mut file| {
if b {
file.write_all(b"1")
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all(b"1").map_err(|e| {
Error::with_cause(
WriteFailed("cpuset.sched_load_balance".to_string(), "1".to_string()),
e,
)
})
} else {
file.write_all(b"0")
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all(b"0").map_err(|e| {
Error::with_cause(
WriteFailed("cpuset.sched_load_balance".to_string(), "0".to_string()),
e,
)
})
}
})
}
@@ -431,8 +475,12 @@ impl CpuSetController {
pub fn set_rebalance_relax_domain_level(&self, i: i64) -> Result<()> {
self.open_path("cpuset.sched_relax_domain_level", true)
.and_then(|mut file| {
file.write_all(i.to_string().as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all(i.to_string().as_ref()).map_err(|e| {
Error::with_cause(
WriteFailed("cpuset.sched_relax_domain_level".to_string(), i.to_string()),
e,
)
})
})
}
@@ -442,11 +490,19 @@ impl CpuSetController {
self.open_path("cpuset.memory_migrate", true)
.and_then(|mut file| {
if b {
file.write_all(b"1")
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all(b"1").map_err(|e| {
Error::with_cause(
WriteFailed("cpuset.memory_migrate".to_string(), "1".to_string()),
e,
)
})
} else {
file.write_all(b"0")
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all(b"0").map_err(|e| {
Error::with_cause(
WriteFailed("cpuset.memory_migrate".to_string(), "0".to_string()),
e,
)
})
}
})
}
@@ -457,11 +513,19 @@ impl CpuSetController {
self.open_path("cpuset.memory_spread_page", true)
.and_then(|mut file| {
if b {
file.write_all(b"1")
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all(b"1").map_err(|e| {
Error::with_cause(
WriteFailed("cpuset.memory_spread_page".to_string(), "1".to_string()),
e,
)
})
} else {
file.write_all(b"0")
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all(b"0").map_err(|e| {
Error::with_cause(
WriteFailed("cpuset.memory_spread_page".to_string(), "0".to_string()),
e,
)
})
}
})
}
@@ -472,11 +536,19 @@ impl CpuSetController {
self.open_path("cpuset.memory_spread_slab", true)
.and_then(|mut file| {
if b {
file.write_all(b"1")
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all(b"1").map_err(|e| {
Error::with_cause(
WriteFailed("cpuset.memory_spread_slab".to_string(), "1".to_string()),
e,
)
})
} else {
file.write_all(b"0")
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all(b"0").map_err(|e| {
Error::with_cause(
WriteFailed("cpuset.memory_spread_slab".to_string(), "0".to_string()),
e,
)
})
}
})
}
@@ -493,11 +565,25 @@ impl CpuSetController {
self.open_path("cpuset.memory_pressure_enabled", true)
.and_then(|mut file| {
if b {
file.write_all(b"1")
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all(b"1").map_err(|e| {
Error::with_cause(
WriteFailed(
"cpuset.memory_pressure_enabled".to_string(),
"1".to_string(),
),
e,
)
})
} else {
file.write_all(b"0")
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all(b"0").map_err(|e| {
Error::with_cause(
WriteFailed(
"cpuset.memory_pressure_enabled".to_string(),
"0".to_string(),
),
e,
)
})
}
})
}

View File

@@ -46,6 +46,7 @@ pub enum DeviceType {
Block,
}
#[allow(clippy::derivable_impls)]
impl Default for DeviceType {
fn default() -> Self {
DeviceType::All
@@ -170,9 +171,9 @@ impl ControllerInternal for DevicesController {
for i in &res.devices {
if i.allow {
let _ = self.allow_device(i.devtype, i.major, i.minor, &i.access);
self.allow_device(i.devtype, i.major, i.minor, &i.access)?;
} else {
let _ = self.deny_device(i.devtype, i.major, i.minor, &i.access);
self.deny_device(i.devtype, i.major, i.minor, &i.access)?;
}
}
@@ -203,10 +204,10 @@ impl<'a> From<&'a Subsystem> for &'a DevicesController {
impl DevicesController {
/// Constructs a new `DevicesController` with `root` serving as the root of the control group.
pub fn new(root: PathBuf) -> Self {
pub fn new(point: PathBuf, root: PathBuf) -> Self {
Self {
base: root.clone(),
path: root,
base: root,
path: point,
}
}
@@ -237,8 +238,15 @@ impl DevicesController {
};
let final_str = format!("{} {}:{} {}", devtype.to_char(), major, minor, perms);
self.open_path("devices.allow", true).and_then(|mut file| {
file.write_all(final_str.as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all(final_str.as_ref()).map_err(|e| {
Error::with_cause(
WriteFailed(
self.get_path().join("devices.allow").display().to_string(),
final_str,
),
e,
)
})
})
}
@@ -269,8 +277,15 @@ impl DevicesController {
};
let final_str = format!("{} {}:{} {}", devtype.to_char(), major, minor, perms);
self.open_path("devices.deny", true).and_then(|mut file| {
file.write_all(final_str.as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all(final_str.as_ref()).map_err(|e| {
Error::with_cause(
WriteFailed(
self.get_path().join("devices.deny").display().to_string(),
final_str,
),
e,
)
})
})
}
@@ -282,7 +297,7 @@ impl DevicesController {
match res {
Ok(_) => {
s.lines().fold(Ok(Vec::new()), |acc, line| {
let ls = line.to_string().split(|c| c == ' ' || c == ':').map(|x| x.to_string()).collect::<Vec<String>>();
let ls = line.split(|c| c == ' ' || c == ':').map(|x| x.to_string()).collect::<Vec<String>>();
if acc.is_err() || ls.len() != 4 {
error!("allowed_devices: acc: {:?}, ls: {:?}", acc, ls);
Err(Error::new(ParseError))
@@ -315,7 +330,7 @@ impl DevicesController {
}
})
},
Err(e) => Err(Error::with_cause(ReadFailed, e)),
Err(e) => Err(Error::with_cause(ReadFailed("devices.list".to_string()), e)),
}
})
}

View File

@@ -8,40 +8,67 @@ use std::error::Error as StdError;
use std::fmt;
/// The different types of errors that can occur while manipulating control groups.
#[derive(Debug, Eq, PartialEq)]
#[derive(thiserror::Error, Debug, Eq, PartialEq)]
pub enum ErrorKind {
#[error("fs error")]
FsError,
#[error("common error: {0}")]
Common(String),
/// An error occured while writing to a control group file.
WriteFailed,
#[error("unable to write to a control group file {0}, value {1}")]
WriteFailed(String, String),
/// An error occured while trying to read from a control group file.
ReadFailed,
#[error("unable to read a control group file {0}")]
ReadFailed(String),
/// An error occured while trying to remove a control group.
#[error("unable 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.
#[error("unable to parse control group file")]
ParseError,
/// You tried to do something invalid.
///
/// This could be because you tried to set a value in a control group that is not a root
/// control group. Or, when using unified hierarchy, you tried to add a task in a leaf node.
#[error("the requested operation is invalid")]
InvalidOperation,
/// The path of the control group was invalid.
///
/// This could be caused by trying to escape the control group filesystem via a string of "..".
/// This crate checks against this and operations will fail with this error.
#[error("the given path is invalid")]
InvalidPath,
#[error("invalid bytes size")]
InvalidBytesSize,
/// The specified controller is not in the list of supported controllers.
#[error("specified controller is not in the list of supported controllers")]
SpecifiedControllers,
/// Using method in wrong cgroup version.
#[error("using method in wrong cgroup version")]
CgroupVersion,
/// Using method in wrong cgroup mode.
#[error("using method in wrong cgroup mode.")]
CgroupMode,
/// Subsystems is empty.
#[error("subsystems is empty")]
SubsystemsEmpty,
/// An unknown error has occured.
#[error("an unknown error")]
Other,
}
@@ -53,29 +80,16 @@ pub struct Error {
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let msg = match &self.kind {
ErrorKind::FsError => "fs error".to_string(),
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(),
ErrorKind::InvalidBytesSize => "invalid bytes size".to_string(),
ErrorKind::Other => "an unknown error".to_string(),
};
if let Some(cause) = &self.cause {
write!(f, "{} caused by: {:?}", msg, cause)
write!(f, "{} caused by: {:?}", &self.kind, cause)
} else {
write!(f, "{}", msg)
write!(f, "{}", &self.kind)
}
}
}
impl StdError for Error {
fn cause(&self) -> Option<&dyn StdError> {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
#[allow(clippy::manual_map)]
match self.cause {
Some(ref x) => Some(&**x),

View File

@@ -46,10 +46,11 @@ fn register_memory_event(
arg: &str,
) -> Result<Receiver<String>> {
let path = cg_dir.join(event_name);
let event_file = File::open(path).map_err(|e| Error::with_cause(ReadFailed, e))?;
let event_file = File::open(path.clone())
.map_err(|e| Error::with_cause(ReadFailed(path.display().to_string()), e))?;
let eventfd =
eventfd(0, EfdFlags::EFD_CLOEXEC).map_err(|e| Error::with_cause(ReadFailed, e))?;
let eventfd = eventfd(0, EfdFlags::EFD_CLOEXEC)
.map_err(|e| Error::with_cause(ReadFailed("eventfd".to_string()), e))?;
let event_control_path = cg_dir.join("cgroup.event_control");
let data = if arg.is_empty() {
@@ -59,7 +60,12 @@ 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.clone()).map_err(|e| {
Error::with_cause(
WriteFailed(event_control_path.display().to_string(), data),
e,
)
})?;
let mut eventfd_file = unsafe { File::from_raw_fd(eventfd) };

View File

@@ -84,50 +84,49 @@ impl<'a> From<&'a Subsystem> for &'a FreezerController {
impl FreezerController {
/// Contructs a new `FreezerController` with `root` serving as the root of the control group.
pub fn new(root: PathBuf, v2: bool) -> Self {
pub fn new(point: PathBuf, root: PathBuf, v2: bool) -> Self {
Self {
base: root.clone(),
path: root,
base: root,
path: point,
v2,
}
}
/// Freezes the processes in the control group.
pub fn freeze(&self) -> Result<()> {
let mut file = "freezer.state";
let mut file_name = "freezer.state";
let mut content = "FROZEN".to_string();
if self.v2 {
file = "cgroup.freeze";
file_name = "cgroup.freeze";
content = "1".to_string();
}
self.open_path(file, true).and_then(|mut file| {
self.open_path(file_name, true).and_then(|mut file| {
file.write_all(content.as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
.map_err(|e| Error::with_cause(WriteFailed(file_name.to_string(), content), e))
})
}
/// Thaws, that is, unfreezes the processes in the control group.
pub fn thaw(&self) -> Result<()> {
let mut file = "freezer.state";
let mut file_name = "freezer.state";
let mut content = "THAWED".to_string();
if self.v2 {
file = "cgroup.freeze";
file_name = "cgroup.freeze";
content = "0".to_string();
}
self.open_path(file, true).and_then(|mut file| {
self.open_path(file_name, true).and_then(|mut file| {
file.write_all(content.as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
.map_err(|e| Error::with_cause(WriteFailed(file_name.to_string(), content), e))
})
}
/// Retrieve the state of processes in the control group.
pub fn state(&self) -> Result<FreezerState> {
let mut file = "freezer.state";
let mut file_name = "freezer.state";
if self.v2 {
file = "cgroup.freeze";
file_name = "cgroup.freeze";
}
self.open_path(file, false).and_then(|mut file| {
self.open_path(file_name, false).and_then(|mut file| {
let mut s = String::new();
let res = file.read_to_string(&mut s);
match res {
@@ -139,7 +138,7 @@ impl FreezerController {
"FREEZING" => Ok(FreezerState::Freezing),
_ => Err(Error::new(ParseError)),
},
Err(e) => Err(Error::with_cause(ReadFailed, e)),
Err(e) => Err(Error::with_cause(ReadFailed(file_name.to_string()), e)),
}
})
}

View File

@@ -5,14 +5,11 @@
//
//! This module represents the various control group hierarchies the Linux kernel supports.
//!
//! Currently, we only support the cgroupv1 hierarchy, but in the future we will add support for
//! the Unified Hierarchy.
use std::fs;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use crate::blkio::BlkIoController;
use crate::cpu::CpuController;
@@ -37,6 +34,8 @@ use crate::cgroup::Cgroup;
/// See `proc(5)` for format details.
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct Mountinfo {
/// Mount root directory of the file system.
pub mount_root: PathBuf,
/// Mount pathname relative to the process's root.
pub mount_point: PathBuf,
/// Filesystem type (main type with optional sub-type).
@@ -57,6 +56,7 @@ pub(crate) fn parse_mountinfo_for_line(line: &str) -> Option<Mountinfo> {
return None;
}
let mount_point = PathBuf::from(s0_values[4]);
let mount_root = PathBuf::from(s0_values[3]);
let fs_type_values: Vec<_> = s1_values[0].trim().split('.').collect();
let fs_type = match fs_type_values.len() {
1 => (fs_type_values[0].to_string(), None),
@@ -69,6 +69,7 @@ pub(crate) fn parse_mountinfo_for_line(line: &str) -> Option<Mountinfo> {
let super_opts: Vec<String> = s1_values[2].trim().split(',').map(String::from).collect();
Some(Mountinfo {
mount_root,
mount_point,
fs_type,
super_opts,
@@ -123,47 +124,53 @@ impl Hierarchy for V1 {
// 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 let Some((point, root)) = self.get_mount_point(Controllers::BlkIo) {
subs.push(Subsystem::BlkIo(BlkIoController::new(point, root, false)));
}
if let Some(root) = self.get_mount_point(Controllers::Mem) {
subs.push(Subsystem::Mem(MemController::new(root, false)));
if let Some((point, root)) = self.get_mount_point(Controllers::Mem) {
subs.push(Subsystem::Mem(MemController::new(point, root, false)));
}
if let Some(root) = self.get_mount_point(Controllers::Pids) {
subs.push(Subsystem::Pid(PidController::new(root, false)));
if let Some((point, root)) = self.get_mount_point(Controllers::Pids) {
subs.push(Subsystem::Pid(PidController::new(point, root, false)));
}
if let Some(root) = self.get_mount_point(Controllers::CpuSet) {
subs.push(Subsystem::CpuSet(CpuSetController::new(root, false)));
if let Some((point, root)) = self.get_mount_point(Controllers::CpuSet) {
subs.push(Subsystem::CpuSet(CpuSetController::new(point, root, false)));
}
if let Some(root) = self.get_mount_point(Controllers::CpuAcct) {
subs.push(Subsystem::CpuAcct(CpuAcctController::new(root)));
if let Some((point, root)) = self.get_mount_point(Controllers::CpuAcct) {
subs.push(Subsystem::CpuAcct(CpuAcctController::new(point, root)));
}
if let Some(root) = self.get_mount_point(Controllers::Cpu) {
subs.push(Subsystem::Cpu(CpuController::new(root, false)));
if let Some((point, root)) = self.get_mount_point(Controllers::Cpu) {
subs.push(Subsystem::Cpu(CpuController::new(point, root, false)));
}
if let Some(root) = self.get_mount_point(Controllers::Devices) {
subs.push(Subsystem::Devices(DevicesController::new(root)));
if let Some((point, root)) = self.get_mount_point(Controllers::Devices) {
subs.push(Subsystem::Devices(DevicesController::new(point, root)));
}
if let Some(root) = self.get_mount_point(Controllers::Freezer) {
subs.push(Subsystem::Freezer(FreezerController::new(root, false)));
if let Some((point, root)) = self.get_mount_point(Controllers::Freezer) {
subs.push(Subsystem::Freezer(FreezerController::new(
point, root, false,
)));
}
if let Some(root) = self.get_mount_point(Controllers::NetCls) {
subs.push(Subsystem::NetCls(NetClsController::new(root)));
if let Some((point, root)) = self.get_mount_point(Controllers::NetCls) {
subs.push(Subsystem::NetCls(NetClsController::new(point, root)));
}
if let Some(root) = self.get_mount_point(Controllers::PerfEvent) {
subs.push(Subsystem::PerfEvent(PerfEventController::new(root)));
if let Some((point, root)) = self.get_mount_point(Controllers::PerfEvent) {
subs.push(Subsystem::PerfEvent(PerfEventController::new(point, root)));
}
if let Some(root) = self.get_mount_point(Controllers::NetPrio) {
subs.push(Subsystem::NetPrio(NetPrioController::new(root)));
if let Some((point, root)) = self.get_mount_point(Controllers::NetPrio) {
subs.push(Subsystem::NetPrio(NetPrioController::new(point, root)));
}
if let Some(root) = self.get_mount_point(Controllers::HugeTlb) {
subs.push(Subsystem::HugeTlb(HugeTlbController::new(root, false)));
if let Some((point, root)) = self.get_mount_point(Controllers::HugeTlb) {
subs.push(Subsystem::HugeTlb(HugeTlbController::new(
point, root, false,
)));
}
if let Some(root) = self.get_mount_point(Controllers::Rdma) {
subs.push(Subsystem::Rdma(RdmaController::new(root)));
if let Some((point, root)) = self.get_mount_point(Controllers::Rdma) {
subs.push(Subsystem::Rdma(RdmaController::new(point, root)));
}
if let Some(root) = self.get_mount_point(Controllers::Systemd) {
subs.push(Subsystem::Systemd(SystemdController::new(root, false)));
if let Some((point, root)) = self.get_mount_point(Controllers::Systemd) {
subs.push(Subsystem::Systemd(SystemdController::new(
point, root, false,
)));
}
subs
@@ -173,6 +180,12 @@ impl Hierarchy for V1 {
Cgroup::load(auto(), "")
}
fn parent_control_group(&self, path: &str) -> Cgroup {
let path = Path::new(path);
let parent_path = path.parent().unwrap().to_string_lossy().to_string();
Cgroup::load(auto(), parent_path)
}
fn root(&self) -> PathBuf {
self.mountinfo
.iter()
@@ -212,29 +225,51 @@ impl Hierarchy for V2 {
for s in controller_list {
match s {
"cpu" => {
subs.push(Subsystem::Cpu(CpuController::new(self.root(), true)));
subs.push(Subsystem::Cpu(CpuController::new(
self.root(),
PathBuf::from(""),
true,
)));
}
"io" => {
subs.push(Subsystem::BlkIo(BlkIoController::new(self.root(), true)));
subs.push(Subsystem::BlkIo(BlkIoController::new(
self.root(),
PathBuf::from(""),
true,
)));
}
"cpuset" => {
subs.push(Subsystem::CpuSet(CpuSetController::new(self.root(), true)));
subs.push(Subsystem::CpuSet(CpuSetController::new(
self.root(),
PathBuf::from(""),
true,
)));
}
"memory" => {
subs.push(Subsystem::Mem(MemController::new(self.root(), true)));
subs.push(Subsystem::Mem(MemController::new(
self.root(),
PathBuf::from(""),
true,
)));
}
"pids" => {
subs.push(Subsystem::Pid(PidController::new(self.root(), true)));
subs.push(Subsystem::Pid(PidController::new(
self.root(),
PathBuf::from(""),
true,
)));
}
"freezer" => {
subs.push(Subsystem::Freezer(FreezerController::new(
self.root(),
PathBuf::from(""),
true,
)));
}
"hugetlb" => {
subs.push(Subsystem::HugeTlb(HugeTlbController::new(
self.root(),
PathBuf::from(""),
true,
)));
}
@@ -249,6 +284,12 @@ impl Hierarchy for V2 {
Cgroup::load(auto(), "")
}
fn parent_control_group(&self, path: &str) -> Cgroup {
let path = Path::new(path);
let parent_path = path.parent().unwrap().to_string_lossy().to_string();
Cgroup::load(auto(), parent_path)
}
fn root(&self) -> PathBuf {
PathBuf::from(self.root.clone())
}
@@ -263,10 +304,10 @@ impl V1 {
}
}
pub fn get_mount_point(&self, controller: Controllers) -> Option<PathBuf> {
pub fn get_mount_point(&self, controller: Controllers) -> Option<(PathBuf, 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());
return Some((m.mount_point.to_owned(), m.mount_root.to_owned()));
}
None
})
@@ -297,43 +338,16 @@ impl Default for V2 {
pub const UNIFIED_MOUNTPOINT: &str = "/sys/fs/cgroup";
#[cfg(any(
all(target_os = "linux", not(target_env = "musl")),
target_os = "android"
))]
pub fn is_cgroup2_unified_mode() -> bool {
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;
}
let fs_stat = match statfs::statfs(path) {
Ok(fs_stat) => fs_stat,
Err(_) => return false,
};
// FIXME notwork, nix will not compile CGROUP2_SUPER_MAGIC because not(target_env = "musl")
fs_stat.unwrap().filesystem_type() == statfs::CGROUP2_SUPER_MAGIC
}
pub const INIT_CGROUP_PATHS: &str = "/proc/1/cgroup";
#[cfg(all(target_os = "linux", target_env = "musl"))]
pub fn is_cgroup2_unified_mode() -> bool {
let lines = fs::read_to_string(INIT_CGROUP_PATHS);
if lines.is_err() {
return false;
}
for line in lines.unwrap().lines() {
let fields: Vec<&str> = line.split(':').collect();
if fields.len() != 3 {
continue;
}
if fields[0] != "0" {
return false;
}
}
true
fs_stat.filesystem_type() == statfs::CGROUP2_SUPER_MAGIC
}
pub fn auto() -> Box<dyn Hierarchy> {
@@ -352,19 +366,19 @@ mod tests {
fn test_parse_mount() {
let mountinfo = vec![
("29 26 0:26 / /sys/fs/cgroup/cpuset,cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:10 - cgroup cgroup rw,cpuset,cpu,cpuacct",
Mountinfo{mount_point: PathBuf::from("/sys/fs/cgroup/cpuset,cpu,cpuacct"), fs_type: ("cgroup".to_string(), None), super_opts: vec![
Mountinfo{mount_root: PathBuf::from("/"), mount_point: PathBuf::from("/sys/fs/cgroup/cpuset,cpu,cpuacct"), fs_type: ("cgroup".to_string(), None), super_opts: vec![
"rw".to_string(),
"cpuset".to_string(),
"cpu".to_string(),
"cpuacct".to_string(),
]}),
("121 1731 0:42 / /shm rw,nosuid,nodev,noexec,relatime shared:68 master:66 - tmpfs shm rw,size=65536k",
Mountinfo{mount_point: PathBuf::from("/shm"), fs_type: ("tmpfs".to_string(), None), super_opts: vec![
Mountinfo{mount_root: PathBuf::from("/"), mount_point: PathBuf::from("/shm"), fs_type: ("tmpfs".to_string(), None), super_opts: vec![
"rw".to_string(),
"size=65536k".to_string(),
]}),
("121 1731 0:42 / /shm rw,nosuid,nodev,noexec,relatime shared:68 master:66 - tmpfs.123 shm rw,size=65536k",
Mountinfo{mount_point: PathBuf::from("/shm"), fs_type: ("tmpfs".to_string(), Some("123".to_string())), super_opts: vec![
Mountinfo{mount_root: PathBuf::from("/"), mount_point: PathBuf::from("/shm"), fs_type: ("tmpfs".to_string(), Some("123".to_string())), super_opts: vec![
"rw".to_string(),
"size=65536k".to_string(),
]}),

View File

@@ -88,11 +88,11 @@ impl<'a> From<&'a Subsystem> for &'a HugeTlbController {
impl HugeTlbController {
/// Constructs a new `HugeTlbController` with `root` serving as the root of the control group.
pub fn new(root: PathBuf, v2: bool) -> Self {
pub fn new(point: PathBuf, root: PathBuf, v2: bool) -> Self {
let sizes = get_hugepage_sizes();
Self {
base: root.clone(),
path: root,
base: root,
path: point,
sizes,
v2,
}
@@ -138,8 +138,11 @@ impl HugeTlbController {
/// Get the limit (in bytes) of how much memory can be backed by hugepages of a certain size
/// (`hugetlb_size`).
pub fn limit_in_bytes(&self, hugetlb_size: &str) -> Result<u64> {
self.open_path(&format!("hugetlb.{}.limit_in_bytes", hugetlb_size), false)
.and_then(read_u64_from)
let mut file_name = format!("hugetlb.{}.limit_in_bytes", hugetlb_size);
if self.v2 {
file_name = format!("hugetlb.{}.max", hugetlb_size);
}
self.open_path(&file_name, false).and_then(read_u64_from)
}
/// Get the current usage of memory that is backed by hugepages of a certain size
@@ -165,13 +168,14 @@ impl HugeTlbController {
/// Set the limit (in bytes) of how much memory can be backed by hugepages of a certain size
/// (`hugetlb_size`).
pub fn set_limit_in_bytes(&self, hugetlb_size: &str, limit: u64) -> Result<()> {
let mut file = format!("hugetlb.{}.limit_in_bytes", hugetlb_size);
let mut file_name = format!("hugetlb.{}.limit_in_bytes", hugetlb_size);
if self.v2 {
file = format!("hugetlb.{}.max", hugetlb_size);
file_name = format!("hugetlb.{}.max", hugetlb_size);
}
self.open_path(&file, true).and_then(|mut file| {
file.write_all(limit.to_string().as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
self.open_path(&file_name, true).and_then(|mut file| {
file.write_all(limit.to_string().as_ref()).map_err(|e| {
Error::with_cause(WriteFailed(file_name.to_string(), limit.to_string()), e)
})
})
}
}

View File

@@ -181,12 +181,21 @@ mod sealed {
if w {
match File::create(&path) {
Err(e) => Err(Error::with_cause(ErrorKind::WriteFailed, e)),
Err(e) => Err(Error::with_cause(
ErrorKind::WriteFailed(
path.display().to_string(),
"[CREATE FILE]".to_string(),
),
e,
)),
Ok(file) => Ok(file),
}
} else {
match File::open(&path) {
Err(e) => Err(Error::with_cause(ErrorKind::ReadFailed, e)),
Err(e) => Err(Error::with_cause(
ErrorKind::ReadFailed(path.display().to_string()),
e,
)),
Ok(file) => Ok(file),
}
}
@@ -198,7 +207,7 @@ mod sealed {
let res = file.read_to_string(&mut string);
match res {
Ok(_) => parse_max_value(&string),
Err(e) => Err(Error::with_cause(ReadFailed, e)),
Err(e) => Err(Error::with_cause(ReadFailed(f.to_string()), e)),
}
})
}
@@ -216,17 +225,19 @@ mod sealed {
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))
file.write_all(value.as_ref()).map_err(|e| {
Error::with_cause(WriteFailed(key.to_string(), value.to_string()), e)
})
})
}
#[allow(dead_code)]
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)),
Err(e) => Err(Error::with_cause(ReadFailed(key.to_string()), e)),
}
})
}
@@ -245,6 +256,9 @@ pub trait Controller {
/// The file system path to the controller.
fn path(&self) -> &Path;
/// Root path of the file system to the controller.
fn base(&self) -> &Path;
/// Apply a set of resources to the Controller, invoking its internal functions to pass the
/// kernel the information.
fn apply(&self, res: &Resources) -> Result<()>;
@@ -270,9 +284,18 @@ pub trait Controller {
/// Attach a task to this controller.
fn add_task_by_tgid(&self, pid: &CgroupPid) -> Result<()>;
/// set cgroup type.
fn set_cgroup_type(&self, cgroup_type: &str) -> Result<()>;
/// get cgroup type.
fn get_cgroup_type(&self) -> Result<String>;
/// Get the list of tasks that this controller has.
fn tasks(&self) -> Vec<CgroupPid>;
/// Get the list of procs that this controller has.
fn procs(&self) -> Vec<CgroupPid>;
fn v2(&self) -> bool;
}
@@ -288,6 +311,10 @@ where
self.get_path()
}
fn base(&self) -> &Path {
self.get_base()
}
/// Apply a set of resources to the Controller, invoking its internal functions to pass the
/// kernel the information.
fn apply(&self, res: &Resources) -> Result<()> {
@@ -307,18 +334,32 @@ where
/// Set notify_on_release
fn set_notify_on_release(&self, enable: bool) -> Result<()> {
if self.is_v2() {
return Err(Error::new(ErrorKind::CgroupVersion));
}
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))
write!(file, "{}", enable as i32).map_err(|e| {
Error::with_cause(
ErrorKind::WriteFailed("notify_on_release".to_string(), enable.to_string()),
e,
)
})
})
}
/// Set release_agent
fn set_release_agent(&self, path: &str) -> Result<()> {
if self.is_v2() {
return Err(Error::new(ErrorKind::CgroupVersion));
}
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))
file.write_all(path.as_bytes()).map_err(|e| {
Error::with_cause(
ErrorKind::WriteFailed("release_agent".to_string(), path.to_string()),
e,
)
})
})
}
/// Does this controller already exist?
@@ -353,31 +394,37 @@ where
/// Attach a task to this controller.
fn add_task(&self, pid: &CgroupPid) -> Result<()> {
let mut file = "tasks";
let mut file_name = "tasks";
if self.is_v2() {
file = "cgroup.procs";
file_name = "cgroup.threads";
}
self.open_path(file, true).and_then(|mut file| {
file.write_all(pid.pid.to_string().as_ref())
.map_err(|e| Error::with_cause(ErrorKind::WriteFailed, e))
self.open_path(file_name, true).and_then(|mut file| {
file.write_all(pid.pid.to_string().as_ref()).map_err(|e| {
Error::with_cause(
ErrorKind::WriteFailed(file_name.to_string(), pid.pid.to_string()),
e,
)
})
})
}
/// 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))
let file_name = "cgroup.procs";
self.open_path(file_name, true).and_then(|mut file| {
file.write_all(pid.pid.to_string().as_ref()).map_err(|e| {
Error::with_cause(
ErrorKind::WriteFailed(file_name.to_string(), pid.pid.to_string()),
e,
)
})
})
}
/// Get the list of tasks that this controller has.
fn tasks(&self) -> Vec<CgroupPid> {
let mut file = "tasks";
if self.is_v2() {
file = "cgroup.procs";
}
self.open_path(file, false)
/// Get the list of procs that this controller has.
fn procs(&self) -> Vec<CgroupPid> {
let file_name = "cgroup.procs";
self.open_path(file_name, false)
.map(|file| {
let bf = BufReader::new(file);
let mut v = Vec::new();
@@ -395,6 +442,64 @@ where
.unwrap_or_default()
}
/// Get the list of tasks that this controller has.
fn tasks(&self) -> Vec<CgroupPid> {
let mut file_name = "tasks";
if self.is_v2() {
file_name = "cgroup.threads";
}
self.open_path(file_name, false)
.map(|file| {
let bf = BufReader::new(file);
let mut v = Vec::new();
for line in bf.lines() {
match line {
Ok(line) => {
let n = line.trim().parse().unwrap_or(0u64);
v.push(n);
}
Err(_) => break,
}
}
v.into_iter().map(CgroupPid::from).collect()
})
.unwrap_or_default()
}
/// set cgroup.type
fn set_cgroup_type(&self, cgroup_type: &str) -> Result<()> {
if !self.is_v2() {
return Err(Error::new(ErrorKind::CgroupVersion));
}
let file_name = "cgroup.type";
self.open_path(file_name, true).and_then(|mut file| {
file.write_all(cgroup_type.as_bytes()).map_err(|e| {
Error::with_cause(
ErrorKind::WriteFailed(file_name.to_string(), cgroup_type.to_string()),
e,
)
})
})
}
/// get cgroup.type
fn get_cgroup_type(&self) -> Result<String> {
if !self.is_v2() {
return Err(Error::new(ErrorKind::CgroupVersion));
}
let file_name = "cgroup.type";
self.open_path(file_name, 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(
ErrorKind::ReadFailed(file_name.to_string()),
e,
)),
}
})
}
fn v2(&self) -> bool {
self.is_v2()
}
@@ -409,8 +514,11 @@ fn remove_dir(dir: &Path) -> Result<()> {
}
if dir.exists() && dir.is_dir() {
for entry in fs::read_dir(dir).map_err(|e| Error::with_cause(ReadFailed, e))? {
let entry = entry.map_err(|e| Error::with_cause(ReadFailed, e))?;
for entry in fs::read_dir(dir)
.map_err(|e| Error::with_cause(ReadFailed(dir.display().to_string()), e))?
{
let entry =
entry.map_err(|e| Error::with_cause(ReadFailed(dir.display().to_string()), e))?;
let path = entry.path();
if path.is_dir() {
remove_dir(&path)?;
@@ -439,6 +547,9 @@ pub trait Hierarchy: std::fmt::Debug + Send + Sync {
/// Return a handle to the root control group in the hierarchy.
fn root_control_group(&self) -> Cgroup;
/// Return a handle to the parent control group in the hierarchy.
fn parent_control_group(&self, path: &str) -> Cgroup;
fn v2(&self) -> bool;
}
@@ -501,9 +612,9 @@ pub struct CpuResources {
/// 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: Option<u64>,
/// In one `period`, how much can the tasks run in nanoseconds.
/// In one `period`, how much can the tasks run in microseconds.
pub quota: Option<i64>,
/// Period of time in nanoseconds.
/// Period of time in microseconds.
pub period: Option<u64>,
/// This is currently a no-operation.
pub realtime_runtime: Option<i64>,
@@ -776,6 +887,7 @@ pub enum MaxValue {
Value(i64),
}
#[allow(clippy::derivable_impls)]
impl Default for MaxValue {
fn default() -> Self {
MaxValue::Max
@@ -817,7 +929,7 @@ pub fn parse_max_value(s: &str) -> Result<MaxValue> {
pub fn flat_keyed_to_vec(mut file: File) -> Result<Vec<(String, i64)>> {
let mut content = String::new();
file.read_to_string(&mut content)
.map_err(|e| Error::with_cause(ReadFailed, e))?;
.map_err(|e| Error::with_cause(ReadFailed("FIXME: read_string_from".to_string()), e))?;
let mut v = Vec::new();
for line in content.lines() {
@@ -837,7 +949,7 @@ pub fn flat_keyed_to_vec(mut file: File) -> Result<Vec<(String, i64)>> {
pub fn flat_keyed_to_hashmap(mut file: File) -> Result<HashMap<String, i64>> {
let mut content = String::new();
file.read_to_string(&mut content)
.map_err(|e| Error::with_cause(ReadFailed, e))?;
.map_err(|e| Error::with_cause(ReadFailed("FIXME: read_string_from".to_string()), e))?;
let mut h = HashMap::new();
for line in content.lines() {
@@ -857,7 +969,7 @@ pub fn flat_keyed_to_hashmap(mut file: File) -> Result<HashMap<String, i64>> {
pub fn nested_keyed_to_hashmap(mut file: File) -> Result<HashMap<String, HashMap<String, i64>>> {
let mut content = String::new();
file.read_to_string(&mut content)
.map_err(|e| Error::with_cause(ReadFailed, e))?;
.map_err(|e| Error::with_cause(ReadFailed("FIXME: read_string_from".to_string()), e))?;
let mut h = HashMap::new();
for line in content.lines() {
@@ -891,7 +1003,10 @@ where
.trim()
.parse::<T>()
.map_err(|e| Error::with_cause(ParseError, e)),
Err(e) => Err(Error::with_cause(ReadFailed, e)),
Err(e) => Err(Error::with_cause(
ReadFailed("FIXME: can't get path in fn read_from".to_string()),
e,
)),
}
}
@@ -899,7 +1014,10 @@ fn read_string_from(mut file: File) -> Result<String> {
let mut string = String::new();
match file.read_to_string(&mut string) {
Ok(_) => Ok(string.trim().to_string()),
Err(e) => Err(Error::with_cause(ReadFailed, e)),
Err(e) => Err(Error::with_cause(
ReadFailed("FIXME: can't get path in fn read_string_from".to_string()),
e,
)),
}
}

View File

@@ -540,10 +540,10 @@ impl ControllerInternal for MemController {
impl MemController {
/// Contructs a new `MemController` with `root` serving as the root of the control group.
pub fn new(root: PathBuf, v2: bool) -> Self {
pub fn new(point: PathBuf, root: PathBuf, v2: bool) -> Self {
Self {
base: root.clone(),
path: root,
base: root,
path: point,
v2,
}
}
@@ -562,8 +562,9 @@ impl MemController {
if let Some(v) = v {
let v = v.to_string();
self.open_path(f, true).and_then(|mut file| {
file.write_all(v.as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all(v.as_ref()).map_err(|e| {
Error::with_cause(WriteFailed(f.to_string(), format!("{:?}", v)), e)
})
})?;
}
}
@@ -572,18 +573,33 @@ 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))?;
let m = SetMemory {
high: self
.get_max_value("memory.high")
.map_or(Some(MaxValue::default()), Some),
low: self
.get_max_value("memory.low")
.map_or(Some(MaxValue::Value(0)), Some),
max: self
.get_max_value("memory.max")
.map_or(Some(MaxValue::default()), Some),
min: self
.get_max_value("memory.min")
.map_or(Some(MaxValue::Value(0)), Some),
};
Ok(m)
}
fn memory_stat_v2(&self) -> Memory {
let set = self.get_mem().unwrap();
// NOTE: get_mem() always returns T, but let's
// still do `unwrap_or` for safety.
let set = self.get_mem().unwrap_or(SetMemory {
low: Some(MaxValue::Value(0)),
high: Some(MaxValue::default()),
max: Some(MaxValue::default()),
min: Some(MaxValue::Value(0)),
});
Memory {
fail_cnt: 0,
@@ -592,7 +608,10 @@ impl MemController {
.open_path("memory.current", false)
.and_then(read_u64_from)
.unwrap_or(0),
max_usage_in_bytes: 0,
max_usage_in_bytes: self
.open_path("memory.peak", false)
.and_then(read_u64_from)
.unwrap_or(0),
move_charge_at_immigrate: 0,
numa_stat: NumaStat::default(),
oom_control: OomControl::default(),
@@ -726,7 +745,7 @@ impl MemController {
.open_path("memory.swap.events", false)
.and_then(flat_keyed_to_hashmap)
.map(|x| *x.get("fail").unwrap_or(&0) as u64)
.unwrap(),
.unwrap_or(0),
limit_in_bytes: self
.open_path("memory.swap.max", false)
.and_then(read_i64_from)
@@ -735,7 +754,10 @@ impl MemController {
.open_path("memory.swap.current", false)
.and_then(read_u64_from)
.unwrap_or(0),
max_usage_in_bytes: 0,
max_usage_in_bytes: self
.open_path("memory.swap.peak", false)
.and_then(read_u64_from)
.unwrap_or(0),
}
}
@@ -769,8 +791,12 @@ impl MemController {
/// Reset the fail counter
pub fn reset_fail_count(&self) -> Result<()> {
self.open_path("memory.failcnt", true).and_then(|mut file| {
file.write_all("0".to_string().as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all("0".to_string().as_ref()).map_err(|e| {
Error::with_cause(
WriteFailed("memory.failcnt".to_string(), "0".to_string()),
e,
)
})
})
}
@@ -783,8 +809,12 @@ impl MemController {
self.open_path("memory.kmem.failcnt", true)
.and_then(|mut file| {
file.write_all("0".to_string().as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all("0".to_string().as_ref()).map_err(|e| {
Error::with_cause(
WriteFailed("memory.kmem.failcnt".to_string(), "0".to_string()),
e,
)
})
})
}
@@ -797,8 +827,12 @@ impl MemController {
self.open_path("memory.kmem.tcp.failcnt", true)
.and_then(|mut file| {
file.write_all("0".to_string().as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all("0".to_string().as_ref()).map_err(|e| {
Error::with_cause(
WriteFailed("memory.kmem.tcp.failcnt".to_string(), "0".to_string()),
e,
)
})
})
}
@@ -806,8 +840,12 @@ impl MemController {
pub fn reset_memswap_fail_count(&self) -> Result<()> {
self.open_path("memory.memsw.failcnt", true)
.and_then(|mut file| {
file.write_all("0".to_string().as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all("0".to_string().as_ref()).map_err(|e| {
Error::with_cause(
WriteFailed("memory.memsw.failcnt".to_string(), "0".to_string()),
e,
)
})
})
}
@@ -815,20 +853,28 @@ impl MemController {
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))
file.write_all("0".to_string().as_ref()).map_err(|e| {
Error::with_cause(
WriteFailed("memory.max_usage_in_bytes".to_string(), "0".to_string()),
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";
let mut file_name = "memory.limit_in_bytes";
let mut limit_str = limit.to_string();
if self.v2 {
file = "memory.max";
file_name = "memory.max";
if limit == -1 {
limit_str = "max".to_string();
}
}
self.open_path(file, true).and_then(|mut file| {
file.write_all(limit.to_string().as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
self.open_path(file_name, true).and_then(|mut file| {
file.write_all(limit_str.as_ref())
.map_err(|e| Error::with_cause(WriteFailed(file_name.to_string(), limit_str), e))
})
}
@@ -848,20 +894,27 @@ impl MemController {
warn!("memory.kmem.limit_in_bytes is unsupported by the kernel");
Ok(())
}
Err(e) => Err(Error::with_cause(WriteFailed, e)),
Err(e) => Err(Error::with_cause(
WriteFailed("memory.kmem.limit_in_bytes".to_string(), limit.to_string()),
e,
)),
}
})
}
/// Set the memory+swap limit of the control group, in bytes.
pub fn set_memswap_limit(&self, limit: i64) -> Result<()> {
let mut file = "memory.memsw.limit_in_bytes";
let mut file_name = "memory.memsw.limit_in_bytes";
let mut limit_str = limit.to_string();
if self.v2 {
file = "memory.swap.max";
file_name = "memory.swap.max";
if limit == -1 {
limit_str = "max".to_string();
}
}
self.open_path(file, true).and_then(|mut file| {
file.write_all(limit.to_string().as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
self.open_path(file_name, true).and_then(|mut file| {
file.write_all(limit_str.as_ref())
.map_err(|e| Error::with_cause(WriteFailed(file_name.to_string(), limit_str), e))
})
}
@@ -874,8 +927,15 @@ impl MemController {
self.open_path("memory.kmem.tcp.limit_in_bytes", true)
.and_then(|mut file| {
file.write_all(limit.to_string().as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all(limit.to_string().as_ref()).map_err(|e| {
Error::with_cause(
WriteFailed(
"memory.kmem.tcp.limit_in_bytes".to_string(),
limit.to_string(),
),
e,
)
})
})
}
@@ -884,13 +944,14 @@ impl MemController {
/// This limit is enforced when the system is nearing OOM conditions. Contrast this with the
/// hard limit, which is _always_ enforced.
pub fn set_soft_limit(&self, limit: i64) -> Result<()> {
let mut file = "memory.soft_limit_in_bytes";
let mut file_name = "memory.soft_limit_in_bytes";
if self.v2 {
file = "memory.low"
file_name = "memory.low"
}
self.open_path(file, true).and_then(|mut file| {
file.write_all(limit.to_string().as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
self.open_path(file_name, true).and_then(|mut file| {
file.write_all(limit.to_string().as_ref()).map_err(|e| {
Error::with_cause(WriteFailed(file_name.to_string(), limit.to_string()), e)
})
})
}
@@ -899,22 +960,27 @@ 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<()> {
let mut file = "memory.swappiness";
let mut file_name = "memory.swappiness";
if self.v2 {
file = "memory.swap.max"
file_name = "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))
self.open_path(file_name, true).and_then(|mut file| {
file.write_all(swp.to_string().as_ref()).map_err(|e| {
Error::with_cause(WriteFailed(file_name.to_string(), swp.to_string()), e)
})
})
}
pub fn disable_oom_killer(&self) -> Result<()> {
self.open_path("memory.oom_control", true)
.and_then(|mut file| {
file.write_all("1".to_string().as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all("1".to_string().as_ref()).map_err(|e| {
Error::with_cause(
WriteFailed("memory.oom_control".to_string(), "1".to_string()),
e,
)
})
})
}

View File

@@ -76,10 +76,10 @@ impl<'a> From<&'a Subsystem> for &'a NetClsController {
impl NetClsController {
/// Constructs a new `NetClsController` with `root` serving as the root of the control group.
pub fn new(root: PathBuf) -> Self {
pub fn new(point: PathBuf, root: PathBuf) -> Self {
Self {
base: root.clone(),
path: root,
base: root,
path: point,
}
}
@@ -88,8 +88,9 @@ impl NetClsController {
self.open_path("net_cls.classid", true)
.and_then(|mut file| {
let s = format!("{:#08X}", class);
file.write_all(s.as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all(s.as_ref()).map_err(|e| {
Error::with_cause(WriteFailed("net_cls.classid".to_string(), s), e)
})
})
}

View File

@@ -79,10 +79,10 @@ impl<'a> From<&'a Subsystem> for &'a NetPrioController {
impl NetPrioController {
/// Constructs a new `NetPrioController` with `root` serving as the root of the control group.
pub fn new(root: PathBuf) -> Self {
pub fn new(point: PathBuf, root: PathBuf) -> Self {
Self {
base: root.clone(),
path: root,
base: root,
path: point,
}
}
@@ -132,7 +132,15 @@ impl NetPrioController {
self.open_path("net_prio.ifpriomap", true)
.and_then(|mut file| {
file.write_all(format!("{} {}", eif, prio).as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
.map_err(|e| {
Error::with_cause(
WriteFailed(
"net_prio.ifpriomap".to_string(),
format!("{} {}", eif, prio),
),
e,
)
})
})
}
}

View File

@@ -65,10 +65,10 @@ impl<'a> From<&'a Subsystem> for &'a PerfEventController {
impl PerfEventController {
/// Constructs a new `PerfEventController` with `root` serving as the root of the control group.
pub fn new(root: PathBuf) -> Self {
pub fn new(point: PathBuf, root: PathBuf) -> Self {
Self {
base: root.clone(),
path: root,
base: root,
path: point,
}
}
}

View File

@@ -92,10 +92,10 @@ impl<'a> From<&'a Subsystem> for &'a PidController {
impl PidController {
/// Constructors a new `PidController` instance, with `root` serving as the controller's root
/// directory.
pub fn new(root: PathBuf, v2: bool) -> Self {
pub fn new(point: PathBuf, root: PathBuf, v2: bool) -> Self {
Self {
base: root.clone(),
path: root,
base: root,
path: point,
v2,
}
}
@@ -112,7 +112,7 @@ impl PidController {
},
None => Err(Error::new(ParseError)),
},
Err(e) => Err(Error::with_cause(ReadFailed, e)),
Err(e) => Err(Error::with_cause(ReadFailed("pids.events".to_string()), e)),
}
})
}
@@ -130,7 +130,7 @@ impl PidController {
let res = file.read_to_string(&mut string);
match res {
Ok(_) => parse_max_value(&string),
Err(e) => Err(Error::with_cause(ReadFailed, e)),
Err(e) => Err(Error::with_cause(ReadFailed("pids.max".to_string()), e)),
}
})
}
@@ -145,7 +145,10 @@ impl PidController {
let string_to_write = max_pid.to_string();
match file.write_all(string_to_write.as_ref()) {
Ok(_) => Ok(()),
Err(e) => Err(Error::with_cause(WriteFailed, e)),
Err(e) => Err(Error::with_cause(
WriteFailed("pids.max".to_string(), format!("{:?}", max_pid)),
e,
)),
}
})
}

View File

@@ -68,10 +68,10 @@ impl<'a> From<&'a Subsystem> for &'a RdmaController {
impl RdmaController {
/// Constructs a new `RdmaController` with `root` serving as the root of the control group.
pub fn new(root: PathBuf) -> Self {
pub fn new(point: PathBuf, root: PathBuf) -> Self {
Self {
base: root.clone(),
path: root,
base: root,
path: point,
}
}
@@ -81,11 +81,17 @@ impl RdmaController {
.and_then(read_string_from)
}
/// Returns the max usage of RDMA/IB specific resources.
pub fn max(&self) -> Result<String> {
self.open_path("rdma.max", false).and_then(read_string_from)
}
/// Set a maximum usage for each RDMA/IB resource.
pub fn set_max(&self, max: &str) -> Result<()> {
self.open_path("rdma.max", true).and_then(|mut file| {
file.write_all(max.as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
file.write_all(max.as_ref()).map_err(|e| {
Error::with_cause(WriteFailed("rdma.max".to_string(), max.to_string()), e)
})
})
}
}

View File

@@ -62,10 +62,10 @@ impl<'a> From<&'a Subsystem> for &'a SystemdController {
impl SystemdController {
/// Constructs a new `SystemdController` with `root` serving as the root of the control group.
pub fn new(root: PathBuf, v2: bool) -> Self {
pub fn new(point: PathBuf, root: PathBuf, v2: bool) -> Self {
Self {
base: root.clone(),
path: root,
base: root,
path: point,
_v2: v2,
}
}

View File

@@ -22,7 +22,8 @@ pub fn test_cpu_res_build() {
.cpu()
.shares(85)
.done()
.build(h);
.build(h)
.unwrap();
{
let cpu: &CpuController = cg.controller_of().unwrap();
@@ -42,7 +43,8 @@ pub fn test_memory_res_build() {
.swappiness(70)
.memory_hard_limit(1024 * 1024 * 1024)
.done()
.build(h);
.build(h)
.unwrap();
{
let c: &MemController = cg.controller_of().unwrap();
@@ -64,7 +66,8 @@ pub fn test_pid_res_build() {
.pid()
.maximum_number_of_processes(MaxValue::Value(123))
.done()
.build(h);
.build(h)
.unwrap();
{
let c: &PidController = cg.controller_of().unwrap();
@@ -83,7 +86,8 @@ pub fn test_devices_res_build() {
.devices()
.device(1, 6, DeviceType::Char, true, vec![DevicePermissions::Read])
.done()
.build(h);
.build(h)
.unwrap();
{
let c: &DevicesController = cg.controller_of().unwrap();
@@ -113,7 +117,8 @@ pub fn test_network_res_build() {
.network()
.class_id(1337)
.done()
.build(h);
.build(h)
.unwrap();
{
let c: &NetClsController = cg.controller_of().unwrap();
@@ -134,7 +139,8 @@ pub fn test_hugepages_res_build() {
.hugepages()
.limit("2MB".to_string(), 4 * 2 * 1024 * 1024)
.done()
.build(h);
.build(h)
.unwrap();
{
let c: &HugeTlbController = cg.controller_of().unwrap();
@@ -152,7 +158,8 @@ pub fn test_blkio_res_build() {
.blkio()
.weight(100)
.done()
.build(h);
.build(h)
.unwrap();
{
let c: &BlkIoController = cg.controller_of().unwrap();

View File

@@ -5,29 +5,60 @@
//
//! Simple unit tests about the control groups system.
use cgroups_rs::cgroup::{
CGROUP_MODE_DOMAIN, CGROUP_MODE_DOMAIN_INVALID, CGROUP_MODE_DOMAIN_THREADED,
CGROUP_MODE_THREADED,
};
use cgroups_rs::memory::MemController;
use cgroups_rs::Controller;
use cgroups_rs::{Cgroup, CgroupPid, Subsystem};
use std::process::Command;
use std::thread::sleep;
use std::time::Duration;
#[test]
fn test_tasks_iterator() {
fn test_procs_iterator_cgroup() {
let h = cgroups_rs::hierarchies::auto();
let pid = libc::pid_t::from(nix::unistd::getpid()) as u64;
let cg = Cgroup::new(h, String::from("test_tasks_iterator"));
let cg = Cgroup::new(h, String::from("test_procs_iterator_cgroup")).unwrap();
{
// Add a task to the control group.
cg.add_task_by_tgid(CgroupPid::from(pid)).unwrap();
let mut procs = cg.procs().into_iter();
// Verify that the task is indeed in the xcontrol group
assert_eq!(procs.next(), Some(CgroupPid::from(pid)));
assert_eq!(procs.next(), None);
// Now, try removing it.
cg.remove_task_by_tgid(CgroupPid::from(pid)).unwrap();
procs = cg.procs().into_iter();
// Verify that it was indeed removed.
assert_eq!(procs.next(), None);
}
cg.delete().unwrap();
}
#[test]
fn test_tasks_iterator_cgroup_v1() {
if cgroups_rs::hierarchies::is_cgroup2_unified_mode() {
return;
}
let h = cgroups_rs::hierarchies::auto();
let pid = libc::pid_t::from(nix::unistd::getpid()) as u64;
let cg = Cgroup::new(h, String::from("test_tasks_iterator_cgroup_v1")).unwrap();
{
// Add a task to the control group.
cg.add_task(CgroupPid::from(pid)).unwrap();
use std::{thread, time};
thread::sleep(time::Duration::from_millis(100));
let mut tasks = cg.tasks().into_iter();
// Verify that the task is indeed in the control group
// Verify that the task is indeed in the xcontrol group
assert_eq!(tasks.next(), Some(CgroupPid::from(pid)));
assert_eq!(tasks.next(), None);
// Now, try removing it.
cg.remove_task(CgroupPid::from(pid));
cg.remove_task(CgroupPid::from(pid)).unwrap();
tasks = cg.tasks().into_iter();
// Verify that it was indeed removed.
@@ -36,6 +67,143 @@ fn test_tasks_iterator() {
cg.delete().unwrap();
}
#[test]
fn test_tasks_iterator_cgroup_threaded_mode() {
if !cgroups_rs::hierarchies::is_cgroup2_unified_mode() {
return;
}
let pid = libc::pid_t::from(nix::unistd::getpid()) as u64;
let cg = Cgroup::new(
cgroups_rs::hierarchies::auto(),
String::from("test_tasks_iterator_cgroup_threaded_mode"),
)
.unwrap();
let cg_threaded_sub1 = Cgroup::new_with_specified_controllers(
cgroups_rs::hierarchies::auto(),
String::from("test_tasks_iterator_cgroup_threaded_mode/threaded_sub1"),
Some(vec![String::from("cpuset"), String::from("cpu")]),
)
.unwrap();
let cg_threaded_sub2 = Cgroup::new_with_specified_controllers(
cgroups_rs::hierarchies::auto(),
String::from("test_tasks_iterator_cgroup_threaded_mode/threaded_sub2"),
Some(vec![String::from("cpuset"), String::from("cpu")]),
)
.unwrap();
{
// Verify that cgroup type of the control group is domain mode.
assert_eq!(cg.get_cgroup_type().unwrap(), CGROUP_MODE_DOMAIN);
// Set cgroup type of the sub-control group is thread mode.
cg_threaded_sub1
.set_cgroup_type(CGROUP_MODE_THREADED)
.unwrap();
// Verify that cgroup type of the sub-control group is thread mode.
assert_eq!(
cg_threaded_sub1.get_cgroup_type().unwrap(),
CGROUP_MODE_THREADED
);
// Verify that the cgroup type of the sub-control group that does
// not set the cgroup type is domain invalid mode.
assert_eq!(
cg_threaded_sub2.get_cgroup_type().unwrap(),
CGROUP_MODE_DOMAIN_INVALID
);
// Verify whether the cgroup type of the parent control group of
// the control group whose cgroup type is set to thread mode is
// domain thread mode.
assert_eq!(cg.get_cgroup_type().unwrap(), CGROUP_MODE_DOMAIN_THREADED);
// Set cgroup type of the sub-control group is thread mode.
cg_threaded_sub2
.set_cgroup_type(CGROUP_MODE_THREADED)
.unwrap();
// Verify that cgroup type of the sub-control group is thread mode.
assert_eq!(
cg_threaded_sub2.get_cgroup_type().unwrap(),
CGROUP_MODE_THREADED
);
// Add a proc to the control group.
cg.add_task_by_tgid(CgroupPid::from(pid)).unwrap();
let mut procs = cg.procs().into_iter();
// Verify that the task is indeed in the x control group
assert_eq!(procs.next(), Some(CgroupPid::from(pid)));
assert_eq!(procs.next(), None);
// Add a task to the sub control group.
cg_threaded_sub1.add_task(CgroupPid::from(pid)).unwrap();
let mut tasks = cg_threaded_sub1.tasks().into_iter();
// Verify that the task is indeed in the xcontrol group
assert_eq!(tasks.next(), Some(CgroupPid::from(pid)));
assert_eq!(tasks.next(), None);
// Now, try move it to parent.
cg_threaded_sub1
.move_task_to_parent(CgroupPid::from(pid))
.unwrap();
tasks = cg_threaded_sub1.tasks().into_iter();
// Verify that it was indeed removed.
assert_eq!(tasks.next(), None);
// Now, try removing it.
cg.remove_task_by_tgid(CgroupPid::from(pid)).unwrap();
procs = cg.procs().into_iter();
// Verify that it was indeed removed.
assert_eq!(procs.next(), None);
}
cg_threaded_sub1.delete().unwrap();
cg_threaded_sub2.delete().unwrap();
cg.delete().unwrap();
}
#[test]
fn test_kill_cgroup() {
if !cgroups_rs::hierarchies::is_cgroup2_unified_mode() {
return;
}
let h = cgroups_rs::hierarchies::auto();
let cg = Cgroup::new(h, String::from("test_kill_cgroup")).unwrap();
{
// Spawn a proc, don't want to getpid(2) here.
let mut child = Command::new("sleep").arg("infinity").spawn().unwrap();
cg.add_task_by_tgid(CgroupPid::from(child.id() as u64))
.unwrap();
let cg_procs = cg.procs();
assert_eq!(cg_procs.len(), 1_usize);
// Now kill and wait on the proc.
cg.kill().unwrap();
let mut tries = 0;
let status: Option<std::process::ExitStatus> = loop {
match child.try_wait() {
Ok(Some(status)) => {
break Some(status);
}
Ok(None) => {
if tries > 3 {
break None;
}
sleep(Duration::from_millis(100));
tries += 1;
}
Err(e) => {
child.kill().unwrap();
panic!("error attempting to wait: {}", e);
}
}
};
assert!(status.is_some());
}
cg.delete().unwrap();
}
#[test]
fn test_cgroup_with_relative_paths() {
if cgroups_rs::hierarchies::is_cgroup2_unified_mode() {
@@ -83,7 +251,7 @@ fn test_cgroup_v2() {
return;
}
let h = cgroups_rs::hierarchies::auto();
let cg = Cgroup::new(h, String::from("test_v2"));
let cg = Cgroup::new(h, String::from("test_v2")).unwrap();
let mem_controller: &MemController = cg.controller_of().unwrap();
let (mem, swp, rev) = (4 * 1024 * 1000, 2 * 1024 * 1000, 1024 * 1000);

View File

@@ -10,7 +10,7 @@ use cgroups_rs::Cgroup;
#[test]
fn test_cfs_quota_and_periods() {
let h = cgroups_rs::hierarchies::auto();
let cg = Cgroup::new(h, String::from("test_cfs_quota_and_periods"));
let cg = Cgroup::new(h, String::from("test_cfs_quota_and_periods")).unwrap();
let cpu_controller: &CpuController = cg.controller_of().unwrap();

View File

@@ -13,7 +13,7 @@ use std::fs;
#[test]
fn test_cpuset_memory_pressure_root_cg() {
let h = cgroups_rs::hierarchies::auto();
let cg = Cgroup::new(h, String::from("test_cpuset_memory_pressure_root_cg"));
let cg = Cgroup::new(h, String::from("test_cpuset_memory_pressure_root_cg")).unwrap();
{
let cpuset: &CpuSetController = cg.controller_of().unwrap();
@@ -27,7 +27,7 @@ fn test_cpuset_memory_pressure_root_cg() {
#[test]
fn test_cpuset_set_cpus() {
let h = cgroups_rs::hierarchies::auto();
let cg = Cgroup::new(h, String::from("test_cpuset_set_cpus"));
let cg = Cgroup::new(h, String::from("test_cpuset_set_cpus")).unwrap();
{
let cpuset: &CpuSetController = cg.controller_of().unwrap();
@@ -64,7 +64,7 @@ fn test_cpuset_set_cpus() {
#[test]
fn test_cpuset_set_cpus_add_task() {
let h = cgroups_rs::hierarchies::auto();
let cg = Cgroup::new(h, String::from("test_cpuset_set_cpus_add_task/sub-dir"));
let cg = Cgroup::new(h, String::from("test_cpuset_set_cpus_add_task/sub-dir")).unwrap();
let cpuset: &CpuSetController = cg.controller_of().unwrap();
let set = cpuset.cpuset();
@@ -77,13 +77,13 @@ fn test_cpuset_set_cpus_add_task() {
// Add a task to the control group.
let pid_i = libc::pid_t::from(nix::unistd::getpid()) as u64;
let _ = cg.add_task(CgroupPid::from(pid_i));
let _ = cg.add_task_by_tgid(CgroupPid::from(pid_i));
let tasks = cg.tasks();
assert!(!tasks.is_empty());
println!("tasks after added: {:?}", tasks);
// remove task
let _ = cg.remove_task(CgroupPid::from(pid_i));
cg.remove_task_by_tgid(CgroupPid::from(pid_i)).unwrap();
let tasks = cg.tasks();
println!("tasks after deleted: {:?}", tasks);
assert_eq!(0, tasks.len());

View File

@@ -17,7 +17,7 @@ fn test_devices_parsing() {
}
let h = cgroups_rs::hierarchies::auto();
let cg = Cgroup::new(h, String::from("test_devices_parsing"));
let cg = Cgroup::new(h, String::from("test_devices_parsing")).unwrap();
{
let devices: &DevicesController = cg.controller_of().unwrap();

View File

@@ -17,7 +17,7 @@ fn test_hugetlb_sizes() {
}
let h = cgroups_rs::hierarchies::auto();
let cg = Cgroup::new(h, String::from("test_hugetlb_sizes"));
let cg = Cgroup::new(h, String::from("test_hugetlb_sizes")).unwrap();
{
let hugetlb_controller: &HugeTlbController = cg.controller_of().unwrap();
let _ = hugetlb_controller.get_sizes();

View File

@@ -11,7 +11,7 @@ use cgroups_rs::{Cgroup, MaxValue};
#[test]
fn test_disable_oom_killer() {
let h = cgroups_rs::hierarchies::auto();
let cg = Cgroup::new(h, String::from("test_disable_oom_killer"));
let cg = Cgroup::new(h, String::from("test_disable_oom_killer")).unwrap();
{
let mem_controller: &MemController = cg.controller_of().unwrap();
@@ -40,7 +40,7 @@ fn set_kmem_limit_v1() {
return;
}
let cg = Cgroup::new(h, String::from("set_kmem_limit_v1"));
let cg = Cgroup::new(h, String::from("set_kmem_limit_v1")).unwrap();
{
let mem_controller: &MemController = cg.controller_of().unwrap();
mem_controller.set_kmem_limit(1).unwrap();
@@ -55,7 +55,7 @@ fn set_mem_v2() {
return;
}
let cg = Cgroup::new(h, String::from("set_mem_v2"));
let cg = Cgroup::new(h, String::from("set_mem_v2")).unwrap();
{
let mem_controller: &MemController = cg.controller_of().unwrap();

View File

@@ -17,7 +17,7 @@ use libc::pid_t;
#[test]
fn create_and_delete_cgroup() {
let h = cgroups_rs::hierarchies::auto();
let cg = Cgroup::new(h, String::from("create_and_delete_cgroup"));
let cg = Cgroup::new(h, String::from("create_and_delete_cgroup")).unwrap();
{
let pidcontroller: &PidController = cg.controller_of().unwrap();
pidcontroller.set_pid_max(MaxValue::Value(1337)).unwrap();
@@ -31,7 +31,7 @@ fn create_and_delete_cgroup() {
#[test]
fn test_pids_current_is_zero() {
let h = cgroups_rs::hierarchies::auto();
let cg = Cgroup::new(h, String::from("test_pids_current_is_zero"));
let cg = Cgroup::new(h, String::from("test_pids_current_is_zero")).unwrap();
{
let pidcontroller: &PidController = cg.controller_of().unwrap();
let current = pidcontroller.get_pid_current();
@@ -43,7 +43,7 @@ fn test_pids_current_is_zero() {
#[test]
fn test_pids_events_is_zero() {
let h = cgroups_rs::hierarchies::auto();
let cg = Cgroup::new(h, String::from("test_pids_events_is_zero"));
let cg = Cgroup::new(h, String::from("test_pids_events_is_zero")).unwrap();
{
let pidcontroller: &PidController = cg.controller_of().unwrap();
let events = pidcontroller.get_pid_events();
@@ -56,7 +56,7 @@ fn test_pids_events_is_zero() {
#[test]
fn test_pid_events_is_not_zero() {
let h = cgroups_rs::hierarchies::auto();
let cg = Cgroup::new(h, String::from("test_pid_events_is_not_zero"));
let cg = Cgroup::new(h, String::from("test_pid_events_is_not_zero")).unwrap();
{
let pids: &PidController = cg.controller_of().unwrap();
let before = pids.get_pid_events();
@@ -65,7 +65,7 @@ fn test_pid_events_is_not_zero() {
match unsafe { fork() } {
Ok(ForkResult::Parent { child, .. }) => {
// move the process into the control group
let _ = pids.add_task(&(pid_t::from(child) as u64).into());
let _ = pids.add_task_by_tgid(&(pid_t::from(child) as u64).into());
println!("added task to cg: {:?}", child);

View File

@@ -11,7 +11,7 @@ use cgroups_rs::{Cgroup, MaxValue, PidResources, Resources};
#[test]
fn pid_resources() {
let h = cgroups_rs::hierarchies::auto();
let cg = Cgroup::new(h, String::from("pid_resources"));
let cg = Cgroup::new(h, String::from("pid_resources")).unwrap();
{
let res = Resources {
pid: PidResources {