Compare commits

...

8 Commits

Author SHA1 Message Date
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
15 changed files with 490 additions and 83 deletions

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.2.12"
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"

View File

@@ -36,14 +36,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 +58,67 @@ 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()
}
/// Create this control group.
fn create(&self) {
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 +139,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 +210,7 @@ impl Cgroup {
subsystems,
hier,
path: path.to_str().unwrap().to_string(),
specified_controllers: None,
}
}
@@ -233,36 +285,124 @@ 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)
c.add_task(&tid)
} 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();
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 +421,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> {
@@ -328,9 +495,22 @@ fn supported_controllers() -> Vec<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 +538,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)

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

@@ -51,6 +51,18 @@ pub enum ErrorKind {
#[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,
/// Subsystems is empty.
#[error("subsystems is empty")]
SubsystemsEmpty,
/// An unknown error has occured.
#[error("an unknown error")]
Other,

View File

@@ -12,7 +12,7 @@
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;
@@ -173,6 +173,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()
@@ -249,6 +255,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())
}

View File

@@ -280,9 +280,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;
}
@@ -317,6 +326,9 @@ 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| {
@@ -330,6 +342,9 @@ where
/// 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(
@@ -373,7 +388,7 @@ where
fn add_task(&self, pid: &CgroupPid) -> Result<()> {
let mut file_name = "tasks";
if self.is_v2() {
file_name = "cgroup.procs";
file_name = "cgroup.threads";
}
self.open_path(file_name, true).and_then(|mut file| {
file.write_all(pid.pid.to_string().as_ref()).map_err(|e| {
@@ -387,23 +402,21 @@ where
/// Attach a task to this controller by thread group id.
fn add_task_by_tgid(&self, pid: &CgroupPid) -> Result<()> {
self.open_path("cgroup.procs", true).and_then(|mut file| {
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("cgroup.procs".to_string(), pid.pid.to_string()),
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();
@@ -421,6 +434,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()
}
@@ -468,6 +539,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;
}

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

@@ -8,30 +8,73 @@
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(CgroupPid::from(pid)).unwrap();
cg.add_task_by_tgid(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
assert_eq!(tasks.next(), Some(CgroupPid::from(pid)));
assert_eq!(tasks.next(), None);
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(CgroupPid::from(pid));
tasks = cg.tasks().into_iter();
cg.remove_task_by_tgid(CgroupPid::from(pid)).unwrap();
procs = cg.procs().into_iter();
// Verify that it was indeed removed.
assert_eq!(tasks.next(), None);
assert_eq!(procs.next(), None);
}
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_none());
}
cg.delete().unwrap();
}
@@ -83,7 +126,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);
@@ -103,3 +146,57 @@ fn test_cgroup_v2() {
cg.delete().unwrap();
}
#[test]
fn test_tasks_iterator_cgroup_threaded_mode() {
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_threaded_mode")).unwrap();
let h = cgroups_rs::hierarchies::auto();
let specified_controllers = vec![String::from("cpuset"), String::from("cpu")];
let cg_threaded = Cgroup::new_with_specified_controllers(
h,
String::from("test_tasks_iterator_cgroup_threaded_mode/threaded"),
Some(specified_controllers),
)
.unwrap();
cg_threaded.set_cgroup_type("threaded").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);
// Add a task to the sub control group.
cg_threaded.add_task(CgroupPid::from(pid)).unwrap();
let mut tasks = cg_threaded.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
.move_task_to_parent(CgroupPid::from(pid))
.unwrap();
tasks = cg_threaded.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.delete().unwrap();
cg.delete().unwrap();
}

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