From c9d02afe33f8c9df87759d630b283fb67cf32e38 Mon Sep 17 00:00:00 2001 From: yaoyinnan Date: Fri, 30 Dec 2022 12:46:50 +0800 Subject: [PATCH] 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 --- src/cgroup.rs | 222 +++++++++++++++++++++++++++++++++++++----- src/cgroup_builder.rs | 27 +++-- src/error.rs | 12 +++ src/hierarchies.rs | 14 ++- src/lib.rs | 94 ++++++++++++++++-- tests/builder.rs | 21 ++-- tests/cgroup.rs | 79 ++++++++++++--- tests/cpu.rs | 2 +- tests/cpuset.rs | 10 +- tests/devices.rs | 2 +- tests/hugetlb.rs | 2 +- tests/memory.rs | 6 +- tests/pids.rs | 10 +- tests/resources.rs | 2 +- 14 files changed, 421 insertions(+), 82 deletions(-) diff --git a/src/cgroup.rs b/src/cgroup.rs index 9750344..a11d12e 100644 --- a/src/cgroup.rs +++ b/src/cgroup.rs @@ -36,14 +36,18 @@ pub struct Cgroup { /// The hierarchy. hier: Box, path: String, + + /// List of controllers specifically enabled in the control group. + specified_controllers: Option>, } 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>(hier: Box, path: P) -> Cgroup { + pub fn new>(hier: Box, path: P) -> Result { 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>( + hier: Box, + path: P, + specified_controllers: Option>, + ) -> Result { + 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>( hier: Box, path: P, relative_paths: HashMap, - ) -> Cgroup { + ) -> Result { 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>( + hier: Box, + path: P, + specified_controllers: Vec, + ) -> 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::>(); + } + + 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,102 @@ 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) } /// 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 { + 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 +399,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 { + // 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 { @@ -328,9 +473,22 @@ fn supported_controllers() -> Vec { .collect::>() } -fn create_v2_cgroup(root: PathBuf, path: &str) -> Result<()> { +fn create_v2_cgroup( + root: PathBuf, + path: &str, + specified_controllers: &Option>, +) -> 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 +516,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> { let path = "/proc/self/cgroup".to_string(); get_cgroups_relative_paths_by_path(path) diff --git a/src/cgroup_builder.rs b/src/cgroup_builder.rs index 09eca24..a453d04 100644 --- a/src/cgroup_builder.rs +++ b/src/cgroup_builder.rs @@ -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>, } 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) -> Cgroup { - let cg = Cgroup::new(hier, self.name); - let _ret = cg.apply(&self.resources); - cg + pub fn build(self, hier: Box) -> Result { + 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) -> Self { + self.specified_controllers = Some(specified_controllers); + self } } diff --git a/src/error.rs b/src/error.rs index 95fe5f5..884db3a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -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, diff --git a/src/hierarchies.rs b/src/hierarchies.rs index 056f6ca..1306c28 100644 --- a/src/hierarchies.rs +++ b/src/hierarchies.rs @@ -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()) } diff --git a/src/lib.rs b/src/lib.rs index 2889344..8e8103f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; + /// Get the list of tasks that this controller has. fn tasks(&self) -> Vec; + /// Get the list of procs that this controller has. + fn procs(&self) -> Vec; + 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 { - 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 { + 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 { + 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 { + 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; } diff --git a/tests/builder.rs b/tests/builder.rs index e91d3c9..afce133 100644 --- a/tests/builder.rs +++ b/tests/builder.rs @@ -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(); diff --git a/tests/cgroup.rs b/tests/cgroup.rs index 563168b..7bef852 100644 --- a/tests/cgroup.rs +++ b/tests/cgroup.rs @@ -10,28 +10,25 @@ use cgroups_rs::Controller; use cgroups_rs::{Cgroup, CgroupPid, Subsystem}; #[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(); } @@ -83,7 +80,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 +100,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(); +} diff --git a/tests/cpu.rs b/tests/cpu.rs index 959474e..2b6afb4 100644 --- a/tests/cpu.rs +++ b/tests/cpu.rs @@ -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(); diff --git a/tests/cpuset.rs b/tests/cpuset.rs index 1f7b74d..cecfb63 100644 --- a/tests/cpuset.rs +++ b/tests/cpuset.rs @@ -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()); diff --git a/tests/devices.rs b/tests/devices.rs index 90c1bef..83bb309 100644 --- a/tests/devices.rs +++ b/tests/devices.rs @@ -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(); diff --git a/tests/hugetlb.rs b/tests/hugetlb.rs index 1def769..4319ee5 100644 --- a/tests/hugetlb.rs +++ b/tests/hugetlb.rs @@ -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(); diff --git a/tests/memory.rs b/tests/memory.rs index 2decc97..14374cf 100644 --- a/tests/memory.rs +++ b/tests/memory.rs @@ -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(); diff --git a/tests/pids.rs b/tests/pids.rs index 27a2de7..c555ade 100644 --- a/tests/pids.rs +++ b/tests/pids.rs @@ -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); diff --git a/tests/resources.rs b/tests/resources.rs index 801890a..d983cce 100644 --- a/tests/resources.rs +++ b/tests/resources.rs @@ -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 {