Compare commits

...

34 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
21 changed files with 247 additions and 156 deletions

View File

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

4
.gitignore vendored
View File

@@ -8,7 +8,3 @@ Cargo.lock
# These are backup files generated by rustfmt # These are backup files generated by rustfmt
**/*.rs.bk **/*.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"] keywords = ["linux", "cgroup", "containers", "isolation"]
categories = ["os", "api-bindings", "os::unix-apis"] categories = ["os", "api-bindings", "os::unix-apis"]
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
version = "0.3.2" version = "0.3.5"
authors = ["The Kata Containers community <kata-dev@lists.katacontainers.io>", "Levente Kurusa <lkurusa@acm.org>", "Sam Wilson <tecywiz121@hotmail.com>"] authors = ["The Kata Containers community <kata-dev@lists.katacontainers.io>", "Levente Kurusa <lkurusa@acm.org>", "Sam Wilson <tecywiz121@hotmail.com>"]
edition = "2018" edition = "2018"
homepage = "https://github.com/kata-containers/cgroups-rs" homepage = "https://github.com/kata-containers/cgroups-rs"

View File

@@ -430,10 +430,10 @@ impl<'a> From<&'a Subsystem> for &'a BlkIoController {
impl BlkIoController { impl BlkIoController {
/// Constructs a new `BlkIoController` with `root` serving as the root of the control group. /// 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 { Self {
base: root.clone(), base: root,
path: root, path: point,
v2, v2,
} }
} }

View File

@@ -9,6 +9,7 @@
use crate::error::ErrorKind::*; use crate::error::ErrorKind::*;
use crate::error::*; use crate::error::*;
use crate::hierarchies::V1;
use crate::{CgroupPid, ControllIdentifier, Controller, Hierarchy, Resources, Subsystem}; use crate::{CgroupPid, ControllIdentifier, Controller, Hierarchy, Resources, Subsystem};
use std::collections::HashMap; use std::collections::HashMap;
@@ -73,8 +74,13 @@ impl Cgroup {
self.hier.v2() self.hier.v2()
} }
/// Return the path the cgroup is located at.
pub fn path(&self) -> &str {
&self.path
}
/// Create this control group. /// Create this control group.
fn create(&self) -> Result<()> { pub fn create(&self) -> Result<()> {
if self.hier.v2() { if self.hier.v2() {
create_v2_cgroup(self.hier.root(), &self.path, &self.specified_controllers) create_v2_cgroup(self.hier.root(), &self.path, &self.specified_controllers)
} else { } else {
@@ -492,6 +498,13 @@ impl Cgroup {
v.dedup(); v.dedup();
v 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"; pub const UNIFIED_MOUNTPOINT: &str = "/sys/fs/cgroup";
@@ -509,7 +522,7 @@ fn supported_controllers() -> Vec<String> {
let ret = fs::read_to_string(p.as_str()); let ret = fs::read_to_string(p.as_str());
ret.unwrap_or_default() ret.unwrap_or_default()
.split(' ') .split(' ')
.map(|x| x.to_string()) .map(|x| x.trim().to_string())
.collect::<Vec<String>>() .collect::<Vec<String>>()
} }
@@ -576,19 +589,47 @@ pub fn get_cgroups_relative_paths_by_pid(pid: u32) -> Result<HashMap<String, Str
get_cgroups_relative_paths_by_path(path) 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>> { fn get_cgroups_relative_paths_by_path(path: String) -> Result<HashMap<String, String>> {
let mut m = HashMap::new(); let mut m = HashMap::new();
let content = let content =
fs::read_to_string(path.clone()).map_err(|e| Error::with_cause(ReadFailed(path), e))?; fs::read_to_string(path.clone()).map_err(|e| Error::with_cause(ReadFailed(path), e))?;
for l in content.lines() { // cgroup path may have ":" , likes
let fl: Vec<&str> = l.split(':').collect(); // "2:cpu,cpuacct:/system.slice/containerd.service/test.slice:cri-containerd:96b37a2edf84351487f42039e137427f1812f678850675fac214caf597ee5e4a"
if fl.len() != 3 { for line in content.lines() {
continue; if let Some((first_value_part, remaining_path)) =
} line.split_once(':').unwrap_or_default().1.split_once(':')
{
let keys: Vec<&str> = fl[1].split(',').collect(); let keys: Vec<&str> = first_value_part.split(',').collect();
for key in &keys { keys.iter().for_each(|key| {
m.insert(key.to_string(), fl[2].to_string()); m.insert(key.to_string(), remaining_path.to_string());
});
} }
} }
Ok(m) Ok(m)

View File

@@ -59,7 +59,6 @@ impl ControllerInternal for CpuController {
fn get_path(&self) -> &PathBuf { fn get_path(&self) -> &PathBuf {
&self.path &self.path
} }
fn get_path_mut(&mut self) -> &mut PathBuf { fn get_path_mut(&mut self) -> &mut PathBuf {
&mut self.path &mut self.path
} }
@@ -113,10 +112,10 @@ impl<'a> From<&'a Subsystem> for &'a CpuController {
impl CpuController { impl CpuController {
/// Contructs a new `CpuController` with `root` serving as the root of the control group. /// 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 { Self {
base: root.clone(), base: root,
path: root, path: point,
v2, v2,
} }
} }

View File

@@ -100,10 +100,10 @@ impl<'a> From<&'a Subsystem> for &'a CpuAcctController {
impl CpuAcctController { impl CpuAcctController {
/// Contructs a new `CpuAcctController` with `root` serving as the root of the control group. /// 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 { Self {
base: root.clone(), base: root,
path: root, path: point,
} }
} }

View File

@@ -254,10 +254,10 @@ fn parse_range(s: String) -> Result<Vec<(u64, u64)>> {
impl CpuSetController { impl CpuSetController {
/// Contructs a new `CpuSetController` with `root` serving as the root of the control group. /// 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 { Self {
base: root.clone(), base: root,
path: root, path: point,
v2, v2,
} }
} }

View File

@@ -46,6 +46,7 @@ pub enum DeviceType {
Block, Block,
} }
#[allow(clippy::derivable_impls)]
impl Default for DeviceType { impl Default for DeviceType {
fn default() -> Self { fn default() -> Self {
DeviceType::All DeviceType::All
@@ -170,9 +171,9 @@ impl ControllerInternal for DevicesController {
for i in &res.devices { for i in &res.devices {
if i.allow { 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 { } 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 { impl DevicesController {
/// Constructs a new `DevicesController` with `root` serving as the root of the control group. /// 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 { Self {
base: root.clone(), base: root,
path: root, path: point,
} }
} }
@@ -238,7 +239,13 @@ impl DevicesController {
let final_str = format!("{} {}:{} {}", devtype.to_char(), major, minor, perms); let final_str = format!("{} {}:{} {}", devtype.to_char(), major, minor, perms);
self.open_path("devices.allow", true).and_then(|mut file| { self.open_path("devices.allow", true).and_then(|mut file| {
file.write_all(final_str.as_ref()).map_err(|e| { file.write_all(final_str.as_ref()).map_err(|e| {
Error::with_cause(WriteFailed("devices.allow".to_string(), final_str), e) Error::with_cause(
WriteFailed(
self.get_path().join("devices.allow").display().to_string(),
final_str,
),
e,
)
}) })
}) })
} }
@@ -271,7 +278,13 @@ impl DevicesController {
let final_str = format!("{} {}:{} {}", devtype.to_char(), major, minor, perms); let final_str = format!("{} {}:{} {}", devtype.to_char(), major, minor, perms);
self.open_path("devices.deny", true).and_then(|mut file| { self.open_path("devices.deny", true).and_then(|mut file| {
file.write_all(final_str.as_ref()).map_err(|e| { file.write_all(final_str.as_ref()).map_err(|e| {
Error::with_cause(WriteFailed("devices.deny".to_string(), final_str), e) Error::with_cause(
WriteFailed(
self.get_path().join("devices.deny").display().to_string(),
final_str,
),
e,
)
}) })
}) })
} }
@@ -284,7 +297,7 @@ impl DevicesController {
match res { match res {
Ok(_) => { Ok(_) => {
s.lines().fold(Ok(Vec::new()), |acc, line| { 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 { if acc.is_err() || ls.len() != 4 {
error!("allowed_devices: acc: {:?}, ls: {:?}", acc, ls); error!("allowed_devices: acc: {:?}, ls: {:?}", acc, ls);
Err(Error::new(ParseError)) Err(Error::new(ParseError))

View File

@@ -84,14 +84,13 @@ impl<'a> From<&'a Subsystem> for &'a FreezerController {
impl FreezerController { impl FreezerController {
/// Contructs a new `FreezerController` with `root` serving as the root of the control group. /// 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 { Self {
base: root.clone(), base: root,
path: root, path: point,
v2, v2,
} }
} }
/// Freezes the processes in the control group. /// Freezes the processes in the control group.
pub fn freeze(&self) -> Result<()> { pub fn freeze(&self) -> Result<()> {
let mut file_name = "freezer.state"; let mut file_name = "freezer.state";

View File

@@ -5,9 +5,6 @@
// //
//! This module represents the various control group hierarchies the Linux kernel supports. //! 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;
use std::fs::File; use std::fs::File;
@@ -37,6 +34,8 @@ use crate::cgroup::Cgroup;
/// See `proc(5)` for format details. /// See `proc(5)` for format details.
#[derive(Debug, PartialEq, Eq, Hash, Clone)] #[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct Mountinfo { pub struct Mountinfo {
/// Mount root directory of the file system.
pub mount_root: PathBuf,
/// Mount pathname relative to the process's root. /// Mount pathname relative to the process's root.
pub mount_point: PathBuf, pub mount_point: PathBuf,
/// Filesystem type (main type with optional sub-type). /// 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; return None;
} }
let mount_point = PathBuf::from(s0_values[4]); 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_values: Vec<_> = s1_values[0].trim().split('.').collect();
let fs_type = match fs_type_values.len() { let fs_type = match fs_type_values.len() {
1 => (fs_type_values[0].to_string(), None), 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(); let super_opts: Vec<String> = s1_values[2].trim().split(',').map(String::from).collect();
Some(Mountinfo { Some(Mountinfo {
mount_root,
mount_point, mount_point,
fs_type, fs_type,
super_opts, super_opts,
@@ -123,47 +124,53 @@ impl Hierarchy for V1 {
// The cgroup writeback feature requires cooperation between memcgs and blkcgs // 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) // 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 // For more Information: https://www.alibabacloud.com/help/doc-detail/155509.htm
if let Some(root) = self.get_mount_point(Controllers::BlkIo) { if let Some((point, root)) = self.get_mount_point(Controllers::BlkIo) {
subs.push(Subsystem::BlkIo(BlkIoController::new(root, false))); subs.push(Subsystem::BlkIo(BlkIoController::new(point, root, false)));
} }
if let Some(root) = self.get_mount_point(Controllers::Mem) { if let Some((point, root)) = self.get_mount_point(Controllers::Mem) {
subs.push(Subsystem::Mem(MemController::new(root, false))); subs.push(Subsystem::Mem(MemController::new(point, root, false)));
} }
if let Some(root) = self.get_mount_point(Controllers::Pids) { if let Some((point, root)) = self.get_mount_point(Controllers::Pids) {
subs.push(Subsystem::Pid(PidController::new(root, false))); subs.push(Subsystem::Pid(PidController::new(point, root, false)));
} }
if let Some(root) = self.get_mount_point(Controllers::CpuSet) { if let Some((point, root)) = self.get_mount_point(Controllers::CpuSet) {
subs.push(Subsystem::CpuSet(CpuSetController::new(root, false))); subs.push(Subsystem::CpuSet(CpuSetController::new(point, root, false)));
} }
if let Some(root) = self.get_mount_point(Controllers::CpuAcct) { if let Some((point, root)) = self.get_mount_point(Controllers::CpuAcct) {
subs.push(Subsystem::CpuAcct(CpuAcctController::new(root))); subs.push(Subsystem::CpuAcct(CpuAcctController::new(point, root)));
} }
if let Some(root) = self.get_mount_point(Controllers::Cpu) { if let Some((point, root)) = self.get_mount_point(Controllers::Cpu) {
subs.push(Subsystem::Cpu(CpuController::new(root, false))); subs.push(Subsystem::Cpu(CpuController::new(point, root, false)));
} }
if let Some(root) = self.get_mount_point(Controllers::Devices) { if let Some((point, root)) = self.get_mount_point(Controllers::Devices) {
subs.push(Subsystem::Devices(DevicesController::new(root))); subs.push(Subsystem::Devices(DevicesController::new(point, root)));
} }
if let Some(root) = self.get_mount_point(Controllers::Freezer) { if let Some((point, root)) = self.get_mount_point(Controllers::Freezer) {
subs.push(Subsystem::Freezer(FreezerController::new(root, false))); subs.push(Subsystem::Freezer(FreezerController::new(
point, root, false,
)));
} }
if let Some(root) = self.get_mount_point(Controllers::NetCls) { if let Some((point, root)) = self.get_mount_point(Controllers::NetCls) {
subs.push(Subsystem::NetCls(NetClsController::new(root))); subs.push(Subsystem::NetCls(NetClsController::new(point, root)));
} }
if let Some(root) = self.get_mount_point(Controllers::PerfEvent) { if let Some((point, root)) = self.get_mount_point(Controllers::PerfEvent) {
subs.push(Subsystem::PerfEvent(PerfEventController::new(root))); subs.push(Subsystem::PerfEvent(PerfEventController::new(point, root)));
} }
if let Some(root) = self.get_mount_point(Controllers::NetPrio) { if let Some((point, root)) = self.get_mount_point(Controllers::NetPrio) {
subs.push(Subsystem::NetPrio(NetPrioController::new(root))); subs.push(Subsystem::NetPrio(NetPrioController::new(point, root)));
} }
if let Some(root) = self.get_mount_point(Controllers::HugeTlb) { if let Some((point, root)) = self.get_mount_point(Controllers::HugeTlb) {
subs.push(Subsystem::HugeTlb(HugeTlbController::new(root, false))); subs.push(Subsystem::HugeTlb(HugeTlbController::new(
point, root, false,
)));
} }
if let Some(root) = self.get_mount_point(Controllers::Rdma) { if let Some((point, root)) = self.get_mount_point(Controllers::Rdma) {
subs.push(Subsystem::Rdma(RdmaController::new(root))); subs.push(Subsystem::Rdma(RdmaController::new(point, root)));
} }
if let Some(root) = self.get_mount_point(Controllers::Systemd) { if let Some((point, root)) = self.get_mount_point(Controllers::Systemd) {
subs.push(Subsystem::Systemd(SystemdController::new(root, false))); subs.push(Subsystem::Systemd(SystemdController::new(
point, root, false,
)));
} }
subs subs
@@ -218,29 +225,51 @@ impl Hierarchy for V2 {
for s in controller_list { for s in controller_list {
match s { match s {
"cpu" => { "cpu" => {
subs.push(Subsystem::Cpu(CpuController::new(self.root(), true))); subs.push(Subsystem::Cpu(CpuController::new(
self.root(),
PathBuf::from(""),
true,
)));
} }
"io" => { "io" => {
subs.push(Subsystem::BlkIo(BlkIoController::new(self.root(), true))); subs.push(Subsystem::BlkIo(BlkIoController::new(
self.root(),
PathBuf::from(""),
true,
)));
} }
"cpuset" => { "cpuset" => {
subs.push(Subsystem::CpuSet(CpuSetController::new(self.root(), true))); subs.push(Subsystem::CpuSet(CpuSetController::new(
self.root(),
PathBuf::from(""),
true,
)));
} }
"memory" => { "memory" => {
subs.push(Subsystem::Mem(MemController::new(self.root(), true))); subs.push(Subsystem::Mem(MemController::new(
self.root(),
PathBuf::from(""),
true,
)));
} }
"pids" => { "pids" => {
subs.push(Subsystem::Pid(PidController::new(self.root(), true))); subs.push(Subsystem::Pid(PidController::new(
self.root(),
PathBuf::from(""),
true,
)));
} }
"freezer" => { "freezer" => {
subs.push(Subsystem::Freezer(FreezerController::new( subs.push(Subsystem::Freezer(FreezerController::new(
self.root(), self.root(),
PathBuf::from(""),
true, true,
))); )));
} }
"hugetlb" => { "hugetlb" => {
subs.push(Subsystem::HugeTlb(HugeTlbController::new( subs.push(Subsystem::HugeTlb(HugeTlbController::new(
self.root(), self.root(),
PathBuf::from(""),
true, true,
))); )));
} }
@@ -275,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| { self.mountinfo.iter().find_map(|m| {
if m.fs_type.0 == "cgroup" && m.super_opts.contains(&controller.to_string()) { 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 None
}) })
@@ -309,43 +338,16 @@ impl Default for V2 {
pub const UNIFIED_MOUNTPOINT: &str = "/sys/fs/cgroup"; 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 { pub fn is_cgroup2_unified_mode() -> bool {
use nix::sys::statfs; use nix::sys::statfs;
let path = std::path::Path::new(UNIFIED_MOUNTPOINT); let path = std::path::Path::new(UNIFIED_MOUNTPOINT);
let fs_stat = statfs::statfs(path); let fs_stat = match statfs::statfs(path) {
if fs_stat.is_err() { Ok(fs_stat) => fs_stat,
return false; Err(_) => return false,
} };
// FIXME notwork, nix will not compile CGROUP2_SUPER_MAGIC because not(target_env = "musl") fs_stat.filesystem_type() == statfs::CGROUP2_SUPER_MAGIC
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
} }
pub fn auto() -> Box<dyn Hierarchy> { pub fn auto() -> Box<dyn Hierarchy> {
@@ -364,19 +366,19 @@ mod tests {
fn test_parse_mount() { fn test_parse_mount() {
let mountinfo = vec![ 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", ("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(), "rw".to_string(),
"cpuset".to_string(), "cpuset".to_string(),
"cpu".to_string(), "cpu".to_string(),
"cpuacct".to_string(), "cpuacct".to_string(),
]}), ]}),
("121 1731 0:42 / /shm rw,nosuid,nodev,noexec,relatime shared:68 master:66 - tmpfs shm rw,size=65536k", ("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(), "rw".to_string(),
"size=65536k".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", ("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(), "rw".to_string(),
"size=65536k".to_string(), "size=65536k".to_string(),
]}), ]}),

View File

@@ -88,11 +88,11 @@ impl<'a> From<&'a Subsystem> for &'a HugeTlbController {
impl HugeTlbController { impl HugeTlbController {
/// Constructs a new `HugeTlbController` with `root` serving as the root of the control group. /// 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(); let sizes = get_hugepage_sizes();
Self { Self {
base: root.clone(), base: root,
path: root, path: point,
sizes, sizes,
v2, v2,
} }

View File

@@ -231,6 +231,7 @@ mod sealed {
}) })
} }
#[allow(dead_code)]
fn get(&self, key: &str) -> Result<String> { fn get(&self, key: &str) -> Result<String> {
self.open_path(key, false).and_then(|mut file: File| { self.open_path(key, false).and_then(|mut file: File| {
let mut string = String::new(); let mut string = String::new();
@@ -255,6 +256,9 @@ pub trait Controller {
/// The file system path to the controller. /// The file system path to the controller.
fn path(&self) -> &Path; 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 /// Apply a set of resources to the Controller, invoking its internal functions to pass the
/// kernel the information. /// kernel the information.
fn apply(&self, res: &Resources) -> Result<()>; fn apply(&self, res: &Resources) -> Result<()>;
@@ -307,6 +311,10 @@ where
self.get_path() 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 /// Apply a set of resources to the Controller, invoking its internal functions to pass the
/// kernel the information. /// kernel the information.
fn apply(&self, res: &Resources) -> Result<()> { fn apply(&self, res: &Resources) -> Result<()> {
@@ -879,6 +887,7 @@ pub enum MaxValue {
Value(i64), Value(i64),
} }
#[allow(clippy::derivable_impls)]
impl Default for MaxValue { impl Default for MaxValue {
fn default() -> Self { fn default() -> Self {
MaxValue::Max MaxValue::Max

View File

@@ -540,10 +540,10 @@ impl ControllerInternal for MemController {
impl MemController { impl MemController {
/// Contructs a new `MemController` with `root` serving as the root of the control group. /// 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 { Self {
base: root.clone(), base: root,
path: root, path: point,
v2, v2,
} }
} }
@@ -573,18 +573,33 @@ impl MemController {
// for v2 // for v2
pub fn get_mem(&self) -> Result<SetMemory> { pub fn get_mem(&self) -> Result<SetMemory> {
let mut m: SetMemory = Default::default(); let m = SetMemory {
self.get_max_value("memory.high") high: self
.map(|x| m.high = Some(x))?; .get_max_value("memory.high")
self.get_max_value("memory.low").map(|x| m.low = Some(x))?; .map_or(Some(MaxValue::default()), Some),
self.get_max_value("memory.max").map(|x| m.max = Some(x))?; low: self
self.get_max_value("memory.min").map(|x| m.min = Some(x))?; .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) Ok(m)
} }
fn memory_stat_v2(&self) -> Memory { 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 { Memory {
fail_cnt: 0, fail_cnt: 0,
@@ -593,7 +608,10 @@ impl MemController {
.open_path("memory.current", false) .open_path("memory.current", false)
.and_then(read_u64_from) .and_then(read_u64_from)
.unwrap_or(0), .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, move_charge_at_immigrate: 0,
numa_stat: NumaStat::default(), numa_stat: NumaStat::default(),
oom_control: OomControl::default(), oom_control: OomControl::default(),
@@ -727,7 +745,7 @@ impl MemController {
.open_path("memory.swap.events", false) .open_path("memory.swap.events", false)
.and_then(flat_keyed_to_hashmap) .and_then(flat_keyed_to_hashmap)
.map(|x| *x.get("fail").unwrap_or(&0) as u64) .map(|x| *x.get("fail").unwrap_or(&0) as u64)
.unwrap(), .unwrap_or(0),
limit_in_bytes: self limit_in_bytes: self
.open_path("memory.swap.max", false) .open_path("memory.swap.max", false)
.and_then(read_i64_from) .and_then(read_i64_from)
@@ -736,7 +754,10 @@ impl MemController {
.open_path("memory.swap.current", false) .open_path("memory.swap.current", false)
.and_then(read_u64_from) .and_then(read_u64_from)
.unwrap_or(0), .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),
} }
} }
@@ -844,13 +865,16 @@ impl MemController {
/// Set the memory usage limit of the control group, in bytes. /// Set the memory usage limit of the control group, in bytes.
pub fn set_limit(&self, limit: i64) -> Result<()> { pub fn set_limit(&self, limit: i64) -> Result<()> {
let mut file_name = "memory.limit_in_bytes"; let mut file_name = "memory.limit_in_bytes";
let mut limit_str = limit.to_string();
if self.v2 { if self.v2 {
file_name = "memory.max"; file_name = "memory.max";
if limit == -1 {
limit_str = "max".to_string();
}
} }
self.open_path(file_name, true).and_then(|mut file| { self.open_path(file_name, true).and_then(|mut file| {
file.write_all(limit.to_string().as_ref()).map_err(|e| { file.write_all(limit_str.as_ref())
Error::with_cause(WriteFailed(file_name.to_string(), limit.to_string()), e) .map_err(|e| Error::with_cause(WriteFailed(file_name.to_string(), limit_str), e))
})
}) })
} }
@@ -881,13 +905,16 @@ impl MemController {
/// Set the memory+swap limit of the control group, in bytes. /// Set the memory+swap limit of the control group, in bytes.
pub fn set_memswap_limit(&self, limit: i64) -> Result<()> { pub fn set_memswap_limit(&self, limit: i64) -> Result<()> {
let mut file_name = "memory.memsw.limit_in_bytes"; let mut file_name = "memory.memsw.limit_in_bytes";
let mut limit_str = limit.to_string();
if self.v2 { if self.v2 {
file_name = "memory.swap.max"; file_name = "memory.swap.max";
if limit == -1 {
limit_str = "max".to_string();
}
} }
self.open_path(file_name, true).and_then(|mut file| { self.open_path(file_name, true).and_then(|mut file| {
file.write_all(limit.to_string().as_ref()).map_err(|e| { file.write_all(limit_str.as_ref())
Error::with_cause(WriteFailed(file_name.to_string(), limit.to_string()), e) .map_err(|e| Error::with_cause(WriteFailed(file_name.to_string(), limit_str), e))
})
}) })
} }

View File

@@ -76,10 +76,10 @@ impl<'a> From<&'a Subsystem> for &'a NetClsController {
impl NetClsController { impl NetClsController {
/// Constructs a new `NetClsController` with `root` serving as the root of the control group. /// 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 { Self {
base: root.clone(), base: root,
path: root, path: point,
} }
} }

View File

@@ -79,10 +79,10 @@ impl<'a> From<&'a Subsystem> for &'a NetPrioController {
impl NetPrioController { impl NetPrioController {
/// Constructs a new `NetPrioController` with `root` serving as the root of the control group. /// 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 { Self {
base: root.clone(), base: root,
path: root, path: point,
} }
} }

View File

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

View File

@@ -92,10 +92,10 @@ impl<'a> From<&'a Subsystem> for &'a PidController {
impl PidController { impl PidController {
/// Constructors a new `PidController` instance, with `root` serving as the controller's root /// Constructors a new `PidController` instance, with `root` serving as the controller's root
/// directory. /// directory.
pub fn new(root: PathBuf, v2: bool) -> Self { pub fn new(point: PathBuf, root: PathBuf, v2: bool) -> Self {
Self { Self {
base: root.clone(), base: root,
path: root, path: point,
v2, v2,
} }
} }

View File

@@ -68,10 +68,10 @@ impl<'a> From<&'a Subsystem> for &'a RdmaController {
impl RdmaController { impl RdmaController {
/// Constructs a new `RdmaController` with `root` serving as the root of the control group. /// 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 { Self {
base: root.clone(), base: root,
path: root, path: point,
} }
} }
@@ -81,6 +81,11 @@ impl RdmaController {
.and_then(read_string_from) .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. /// Set a maximum usage for each RDMA/IB resource.
pub fn set_max(&self, max: &str) -> Result<()> { pub fn set_max(&self, max: &str) -> Result<()> {
self.open_path("rdma.max", true).and_then(|mut file| { self.open_path("rdma.max", true).and_then(|mut file| {

View File

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

View File

@@ -199,7 +199,7 @@ fn test_kill_cgroup() {
} }
} }
}; };
assert!(!status.is_none()); assert!(status.is_some());
} }
cg.delete().unwrap(); cg.delete().unwrap();
} }