From 9fe6cb58e46bfedd874cb3ef7fe82465495d62dc Mon Sep 17 00:00:00 2001 From: bin liu Date: Tue, 25 Aug 2020 13:28:36 +0800 Subject: [PATCH] change *limit* in memory from u64 to i64 Signed-off-by: bin liu --- Cargo.toml | 4 +- src/blkio.rs | 12 +++- src/cgroup.rs | 62 ++++++++++++++++++-- src/cgroup_builder.rs | 21 +++---- src/cpu.rs | 12 +++- src/cpuset.rs | 19 +++++- src/error.rs | 27 ++++++--- src/events.rs | 83 ++++++++++++++++++++++++++ src/hierarchies.rs | 133 ++++++++++++++++++++++++++++++++++++++---- src/hugetlb.rs | 12 +++- src/lib.rs | 75 +++++++++++++++++++++--- src/memory.rs | 117 ++++++++++++++++++++++++++++++------- src/pid.rs | 48 +++++---------- tests/builder.rs | 47 ++++++++++----- tests/cgroup.rs | 7 ++- tests/cpuset.rs | 43 +++++++++++++- tests/devices.rs | 7 ++- tests/hugetlb.rs | 7 ++- tests/memory.rs | 85 +++++++++++++++++++++++---- tests/pids.rs | 32 +++++----- tests/resources.rs | 13 +++-- 21 files changed, 700 insertions(+), 166 deletions(-) create mode 100644 src/events.rs diff --git a/Cargo.toml b/Cargo.toml index 1556a40..6c99a68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ edition = "2018" [dependencies] log = "0.4" regex = "1.1" +nix = "0.18.0" [dev-dependencies] -nix = "0.11.0" -libc = "0.2.43" +libc = "0.2.76" diff --git a/src/blkio.rs b/src/blkio.rs index fa55f64..97464bd 100644 --- a/src/blkio.rs +++ b/src/blkio.rs @@ -21,6 +21,7 @@ use crate::{ pub struct BlkIoController { base: PathBuf, path: PathBuf, + v2: bool, } #[derive(Eq, PartialEq, Debug)] @@ -269,6 +270,10 @@ impl ControllerInternal for BlkIoController { &self.base } + fn is_v2(&self) -> bool { + self.v2 + } + fn apply(&self, res: &Resources) -> Result<()> { // get the resources that apply to this controller let res: &BlkIoResources = &res.blkio; @@ -341,12 +346,15 @@ fn read_u64_from(mut file: File) -> Result { impl BlkIoController { /// Constructs a new `BlkIoController` with `oroot` serving as the root of the control group. - pub fn new(oroot: PathBuf) -> Self { + pub fn new(oroot: PathBuf, v2: bool) -> Self { let mut root = oroot; - root.push(Self::controller_type().to_string()); + if !v2{ + root.push(Self::controller_type().to_string()); + } Self { base: root.clone(), path: root, + v2: v2, } } diff --git a/src/cgroup.rs b/src/cgroup.rs index 140a4f4..dd424ef 100644 --- a/src/cgroup.rs +++ b/src/cgroup.rs @@ -5,7 +5,8 @@ use crate::error::*; use crate::{CgroupPid, ControllIdentifier, Controller, Hierarchy, Resources, Subsystem}; use std::convert::From; -use std::path::Path; +use std::fs; +use std::path::{Path, PathBuf}; /// A control group is the central structure to this crate. /// @@ -24,14 +25,19 @@ pub struct Cgroup<'b> { subsystems: Vec, /// The hierarchy. - hier: &'b Hierarchy, + hier: Box<&'b dyn Hierarchy>, + path: String, } impl<'b> Cgroup<'b> { /// Create this control group. fn create(&self) { - for subsystem in &self.subsystems { - subsystem.to_controller().create(); + if self.hier.v2() { + create_v2_cgroup(self.hier.root().clone(), &self.path); + }else{ + for subsystem in &self.subsystems { + subsystem.to_controller().create(); + } } } @@ -41,7 +47,7 @@ impl<'b> Cgroup<'b> { /// /// Note that if the handle goes out of scope and is dropped, the control group is _not_ /// destroyed. - pub fn new>(hier: &Hierarchy, path: P) -> Cgroup { + pub fn new>(hier: Box<&'b dyn Hierarchy>, path: P) -> Cgroup<'b> { let cg = Cgroup::load(hier, path); cg.create(); cg @@ -54,7 +60,7 @@ impl<'b> Cgroup<'b> { /// /// Note that if the handle goes out of scope and is dropped, the control group is _not_ /// destroyed. - pub fn load>(hier: &Hierarchy, path: P) -> Cgroup { + pub fn load>(hier: Box<&'b dyn Hierarchy>, path: P) -> Cgroup<'b> { let path = path.as_ref(); let mut subsystems = hier.subsystems(); if path.as_os_str() != "" { @@ -67,6 +73,7 @@ impl<'b> Cgroup<'b> { let cg = Cgroup { subsystems: subsystems, hier: hier, + path: path.to_str().unwrap().to_string(), }; cg @@ -165,3 +172,46 @@ impl<'b> Cgroup<'b> { v } } + +pub const UNIFIED_MOUNTPOINT: &'static str = "/sys/fs/cgroup"; + +fn supported_controllers(p: &PathBuf) -> Vec{ + let p = format!("{}/{}", UNIFIED_MOUNTPOINT, "cgroup.controllers"); + let ret = fs::read_to_string(p.as_str()); + ret.unwrap_or(String::new()).split(" ").map(|x| x.to_string() ).collect::>() +} + +fn create_v2_cgroup(root: PathBuf, path: &str) -> Result<()> { + // controler list ["memory", "cpu"] + let controllers = supported_controllers(&root); + let mut fp = root; + + // path: "a/b/c" + let elements = path.split("/").collect::>(); + let last_index = elements.len() - 1 ; + for (i, ele) in elements.iter().enumerate() { + // ROOT/a + fp.push(ele); + // create dir, need not check if is a file or directory + if !fp.exists(){ + // FIXME set mode to 0755 + match ::std::fs::create_dir(fp.clone()) { + Err(e) => return Err(Error::with_cause(ErrorKind::FsError, e)), + Ok(_) => {}, + } + } + + if i < last_index { + // enable controllers for substree + let mut f = fp.clone(); + f.push("cgroup.subtree_control"); + for c in &controllers{ + let body = format!("+{}", c); + // FIXME set mode to 0644 + let _rest = fs::write(f.as_path(), body.as_bytes()); + } + } + } + + Ok(()) +} \ No newline at end of file diff --git a/src/cgroup_builder.rs b/src/cgroup_builder.rs index 31999c8..f6a7f2c 100644 --- a/src/cgroup_builder.rs +++ b/src/cgroup_builder.rs @@ -55,7 +55,7 @@ //! ``` use crate::error::*; -use crate::{pid, BlkIoDeviceResource, BlkIoDeviceThrottleResource, Cgroup, DeviceResource, Hierarchy, HugePageResource, NetworkPriority, Resources}; +use crate::{pid, BlkIoDeviceResource, BlkIoDeviceThrottleResource, Cgroup, DeviceResource, Hierarchy, HugePageResource, MaxValue, NetworkPriority, Resources}; macro_rules! gen_setter { ($res:ident, $cont:ident, $func:ident, $name:ident, $ty:ty) => { @@ -71,7 +71,7 @@ macro_rules! gen_setter { /// A control group builder instance pub struct CgroupBuilder<'a> { name: String, - hierarchy: &'a Hierarchy, + hierarchy: Box<&'a dyn Hierarchy>, /// Internal, unsupported field: use the associated builders instead. resources: Resources, } @@ -80,7 +80,7 @@ impl<'a> CgroupBuilder<'a> { /// Start building a control group with the supplied hierarchy and name pair. /// /// Note that this does not actually create the control group until `build()` is called. - pub fn new(name: &'a str, hierarchy: &'a Hierarchy) -> CgroupBuilder<'a> { + pub fn new(name: &'a str, hierarchy: Box<&'a dyn Hierarchy>) -> CgroupBuilder<'a> { CgroupBuilder { name: name.to_owned(), hierarchy: hierarchy, @@ -155,11 +155,11 @@ pub struct MemoryResourceBuilder<'a> { impl<'a> MemoryResourceBuilder<'a> { - gen_setter!(memory, MemController, set_kmem_limit, kernel_memory_limit, u64); - gen_setter!(memory, MemController, set_limit, memory_hard_limit, u64); - gen_setter!(memory, MemController, set_soft_limit, memory_soft_limit, u64); - gen_setter!(memory, MemController, set_tcp_limit, kernel_tcp_memory_limit, u64); - gen_setter!(memory, MemController, set_memswap_limit, memory_swap_limit, u64); + gen_setter!(memory, MemController, set_kmem_limit, kernel_memory_limit, i64); + gen_setter!(memory, MemController, set_limit, memory_hard_limit, i64); + gen_setter!(memory, MemController, set_soft_limit, memory_soft_limit, i64); + gen_setter!(memory, MemController, set_tcp_limit, kernel_tcp_memory_limit, i64); + gen_setter!(memory, MemController, set_memswap_limit, memory_swap_limit, i64); gen_setter!(memory, MemController, set_swappiness, swappiness, u64); /// Finish the construction of the memory resources of a control group. @@ -175,7 +175,7 @@ pub struct PidResourceBuilder<'a> { impl<'a> PidResourceBuilder<'a> { - gen_setter!(pid, PidController, set_pid_max, maximum_number_of_processes, pid::PidMax); + gen_setter!(pid, PidController, set_pid_max, maximum_number_of_processes, MaxValue); /// Finish the construction of the pid resources of a control group. pub fn done(self) -> CgroupBuilder<'a> { @@ -190,7 +190,8 @@ pub struct CpuResourceBuilder<'a> { impl<'a> CpuResourceBuilder<'a> { - gen_setter!(cpu, CpuSetController, set_cpus, cpus, String); + // FIXME this should all changed to options. + gen_setter!(cpu, CpuSetController, set_cpus, cpus, Option); gen_setter!(cpu, CpuSetController, set_mems, mems, String); gen_setter!(cpu, CpuController, set_shares, shares, u64); gen_setter!(cpu, CpuController, set_cfs_quota, quota, i64); diff --git a/src/cpu.rs b/src/cpu.rs index 3e43ea8..ae48c1c 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -23,6 +23,7 @@ use crate::{ pub struct CpuController { base: PathBuf, path: PathBuf, + v2: bool, } /// The current state of the control group and its processes. @@ -51,6 +52,10 @@ impl ControllerInternal for CpuController { &self.base } + fn is_v2(&self) -> bool { + self.v2 + } + fn apply(&self, res: &Resources) -> Result<()> { // get the resources that apply to this controller let res: &CpuResources = &res.cpu; @@ -109,12 +114,15 @@ fn read_u64_from(mut file: File) -> Result { impl CpuController { /// Contructs a new `CpuController` with `oroot` serving as the root of the control group. - pub fn new(oroot: PathBuf) -> Self { + pub fn new(oroot: PathBuf, v2: bool) -> Self { let mut root = oroot; - root.push(Self::controller_type().to_string()); + if !v2 { + root.push(Self::controller_type().to_string()); + } Self { base: root.clone(), path: root, + v2: v2, } } diff --git a/src/cpuset.rs b/src/cpuset.rs index 92e981d..fd2661a 100644 --- a/src/cpuset.rs +++ b/src/cpuset.rs @@ -23,6 +23,7 @@ use crate::{ pub struct CpuSetController { base: PathBuf, path: PathBuf, + v2: bool, } /// The current state of the `cpuset` controller for this control group. @@ -95,12 +96,18 @@ impl ControllerInternal for CpuSetController { &self.base } + fn is_v2(&self) -> bool { + self.v2 + } + fn apply(&self, res: &Resources) -> Result<()> { // get the resources that apply to this controller let res: &CpuResources = &res.cpu; if res.update_values { - let _ = self.set_cpus(&res.cpus); + if res.cpus.is_some(){ + let _ = self.set_cpus(res.cpus.as_ref().unwrap().as_str()); + } let _ = self.set_mems(&res.mems); } @@ -108,6 +115,9 @@ impl ControllerInternal for CpuSetController { } fn post_create(&self){ + if self.is_v2(){ + return + } let current = self.get_path(); let parent = match current.parent() { Some(p) => p, @@ -246,12 +256,15 @@ fn parse_range(s: String) -> Result> { impl CpuSetController { /// Contructs a new `CpuSetController` with `oroot` serving as the root of the control group. - pub fn new(oroot: PathBuf) -> Self { + pub fn new(oroot: PathBuf, v2: bool) -> Self { let mut root = oroot; - root.push(Self::controller_type().to_string()); + if !v2{ + root.push(Self::controller_type().to_string()); + } Self { base: root.clone(), path: root, + v2: v2, } } diff --git a/src/error.rs b/src/error.rs index f14a15c..7bfea9b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -4,6 +4,9 @@ use std::fmt; /// The different types of errors that can occur while manipulating control groups. #[derive(Debug, Eq, PartialEq)] pub enum ErrorKind { + FsError, + Common(String), + /// An error occured while writing to a control group file. WriteFailed, @@ -41,14 +44,16 @@ pub struct Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let msg = match self.kind { - ErrorKind::WriteFailed => "unable to write to a control group file", - ErrorKind::ReadFailed => "unable to read a control group file", - ErrorKind::ParseError => "unable to parse control group file", - ErrorKind::InvalidOperation => "the requested operation is invalid", - ErrorKind::InvalidPath => "the given path is invalid", - ErrorKind::InvalidBytesSize => "invalid bytes size", - ErrorKind::Other => "an unknown error", + let msg = match &self.kind { + ErrorKind::FsError => "fs error".to_string(), + ErrorKind::Common(s) => s.clone(), + ErrorKind::WriteFailed => "unable to write to a control group file".to_string(), + ErrorKind::ReadFailed => "unable to read a control group file".to_string(), + ErrorKind::ParseError => "unable to parse control group file".to_string(), + ErrorKind::InvalidOperation => "the requested operation is invalid".to_string(), + ErrorKind::InvalidPath => "the given path is invalid".to_string(), + ErrorKind::InvalidBytesSize => "invalid bytes size".to_string(), + ErrorKind::Other => "an unknown error".to_string(), }; write!(f, "{}", msg) @@ -65,6 +70,12 @@ impl StdError for Error { } impl Error { + pub(crate) fn from_string(s: String) -> Self { + Self { + kind: ErrorKind::Common(s), + cause: None, + } + } pub(crate) fn new(kind: ErrorKind) -> Self { Self { kind, diff --git a/src/events.rs b/src/events.rs new file mode 100644 index 0000000..3e7ac7d --- /dev/null +++ b/src/events.rs @@ -0,0 +1,83 @@ +use eventfd::{eventfd, EfdFlags}; +use nix::sys::eventfd; +use std::fs::{self, File}; +use std::io::Read; +use std::os::unix::io::{AsRawFd, FromRawFd}; +use std::path::{Path, PathBuf}; +use std::sync::mpsc::{self, Receiver}; +use std::thread; + +use crate::error::*; +use crate::error::ErrorKind::*; + + +// notify_on_oom returns channel on which you can expect event about OOM, +// if process died without OOM this channel will be closed. +pub fn notify_on_oom_v2(key: &str, dir: &PathBuf) -> Result> { + register_memory_event(key, dir, "memory.oom_control", "") +} + +// notify_on_oom returns channel on which you can expect event about OOM, +// if process died without OOM this channel will be closed. +pub fn notify_on_oom_v1(key: &str, dir: &PathBuf) -> Result> { + register_memory_event(key, dir, "memory.oom_control", "") +} + +// level is one of "low", "medium", or "critical" +fn notify_memory_pressure(key: &str, dir: &PathBuf, level: &str) -> Result> { + if level != "low" && level != "medium" && level != "critical" { + return Err(Error::from_string(format!("invalid pressure level {}", level))); + } + + register_memory_event(key, dir, "memory.pressure_level", level) +} + +fn register_memory_event( + key: &str, + cg_dir: &PathBuf, + event_name: &str, + arg: &str, +) -> Result> { + let path = cg_dir.join(event_name); + let event_file = File::open(path).map_err(|e| Error::with_cause(ReadFailed, e))?; + + let eventfd = eventfd(0, EfdFlags::EFD_CLOEXEC).map_err(|e| Error::with_cause(ReadFailed, e))?; + + let event_control_path = cg_dir.join("cgroup.event_control"); + let data; + if arg == "" { + data = format!("{} {}", eventfd, event_file.as_raw_fd()); + } else { + data = format!("{} {} {}", eventfd, event_file.as_raw_fd(), arg); + } + + // write to file and set mode to 0700(FIXME) + fs::write(&event_control_path, data).map_err(|e| Error::with_cause(WriteFailed, e)); + + let mut eventfd_file = unsafe { File::from_raw_fd(eventfd) }; + + let (sender, receiver) = mpsc::channel(); + let key = key.to_string(); + + thread::spawn(move || { + loop { + let mut buf = [0; 8]; + match eventfd_file.read(&mut buf) { + Err(err) => { + return; + } + Ok(_) => { + } + } + + // When a cgroup is destroyed, an event is sent to eventfd. + // So if the control path is gone, return instead of notifying. + if !Path::new(&event_control_path).exists() { + return; + } + sender.send(key.clone()).unwrap(); + } + }); + + Ok(receiver) +} \ No newline at end of file diff --git a/src/hierarchies.rs b/src/hierarchies.rs index 204c887..f99fd3f 100644 --- a/src/hierarchies.rs +++ b/src/hierarchies.rs @@ -2,8 +2,9 @@ //! //! Currently, we only support the cgroupv1 hierarchy, but in the future we will add support for //! the Unified Hierarchy. +use nix::sys::statfs; -use std::fs::File; +use std::fs::{self, File}; use std::io::BufRead; use std::io::BufReader; use std::path::{Path, PathBuf}; @@ -32,23 +33,32 @@ pub struct V1 { mount_point: String, } +pub struct V2 { + root: String, +} + impl Hierarchy for V1 { + + fn v2(&self) -> bool { + false + } + fn subsystems(&self) -> Vec { let mut subs = vec![]; if self.check_support(Controllers::Pids) { - subs.push(Subsystem::Pid(PidController::new(self.root()))); + subs.push(Subsystem::Pid(PidController::new(self.root(), false))); } if self.check_support(Controllers::Mem) { - subs.push(Subsystem::Mem(MemController::new(self.root()))); + subs.push(Subsystem::Mem(MemController::new(self.root(), false))); } if self.check_support(Controllers::CpuSet) { - subs.push(Subsystem::CpuSet(CpuSetController::new(self.root()))); + subs.push(Subsystem::CpuSet(CpuSetController::new(self.root(), false))); } if self.check_support(Controllers::CpuAcct) { subs.push(Subsystem::CpuAcct(CpuAcctController::new(self.root()))); } if self.check_support(Controllers::Cpu) { - subs.push(Subsystem::Cpu(CpuController::new(self.root()))); + subs.push(Subsystem::Cpu(CpuController::new(self.root(), false))); } if self.check_support(Controllers::Devices) { subs.push(Subsystem::Devices(DevicesController::new(self.root()))); @@ -60,7 +70,7 @@ impl Hierarchy for V1 { subs.push(Subsystem::NetCls(NetClsController::new(self.root()))); } if self.check_support(Controllers::BlkIo) { - subs.push(Subsystem::BlkIo(BlkIoController::new(self.root()))); + subs.push(Subsystem::BlkIo(BlkIoController::new(self.root(), true))); } if self.check_support(Controllers::PerfEvent) { subs.push(Subsystem::PerfEvent(PerfEventController::new(self.root()))); @@ -69,7 +79,7 @@ impl Hierarchy for V1 { subs.push(Subsystem::NetPrio(NetPrioController::new(self.root()))); } if self.check_support(Controllers::HugeTlb) { - subs.push(Subsystem::HugeTlb(HugeTlbController::new(self.root()))); + subs.push(Subsystem::HugeTlb(HugeTlbController::new(self.root(), false))); } if self.check_support(Controllers::Rdma) { subs.push(Subsystem::Rdma(RdmaController::new(self.root()))); @@ -79,7 +89,8 @@ impl Hierarchy for V1 { } fn root_control_group(&self) -> Cgroup { - Cgroup::load(self, "".to_string()) + let b : &Hierarchy = self as &Hierarchy; + Cgroup::load(Box::new(&*b), "".to_string()) } fn check_support(&self, sub: Controllers) -> bool { @@ -99,10 +110,82 @@ impl Hierarchy for V1 { } } +impl Hierarchy for V2 { + fn v2(&self) -> bool { + true + } + + fn subsystems(&self) -> Vec { + let mut subs = vec![]; + + let p = format!("{}/{}", UNIFIED_MOUNTPOINT, "cgroup.controllers"); + let ret = fs::read_to_string(p.as_str()); + if ret.is_err() { + return subs; + } + + let controllers = ret.unwrap().trim().to_string(); + println!("controllers: {:?}", controllers); + + let controller_list: Vec<&str> = controllers.split(' ').collect(); + + for s in controller_list { + match s { + "cpu" => {subs.push(Subsystem::Cpu(CpuController::new(self.root(), true)));}, + "io" => {subs.push(Subsystem::BlkIo(BlkIoController::new(self.root(), true)));}, + "cpuset" => {subs.push(Subsystem::CpuSet(CpuSetController::new(self.root(), true)));}, + "memory" => {subs.push(Subsystem::Mem(MemController::new(self.root(), true)));}, + "pids" => {subs.push(Subsystem::Pid(PidController::new(self.root(), true)));}, + _ => {}, + } + } + + if self.check_support(Controllers::CpuAcct) { + subs.push(Subsystem::CpuAcct(CpuAcctController::new(self.root()))); + } + if self.check_support(Controllers::Devices) { + subs.push(Subsystem::Devices(DevicesController::new(self.root()))); + } + if self.check_support(Controllers::Freezer) { + subs.push(Subsystem::Freezer(FreezerController::new(self.root()))); + } + if self.check_support(Controllers::NetCls) { + subs.push(Subsystem::NetCls(NetClsController::new(self.root()))); + } + if self.check_support(Controllers::PerfEvent) { + subs.push(Subsystem::PerfEvent(PerfEventController::new(self.root()))); + } + if self.check_support(Controllers::NetPrio) { + subs.push(Subsystem::NetPrio(NetPrioController::new(self.root()))); + } + if self.check_support(Controllers::HugeTlb) { + subs.push(Subsystem::HugeTlb(HugeTlbController::new(self.root(), true))); + } + if self.check_support(Controllers::Rdma) { + subs.push(Subsystem::Rdma(RdmaController::new(self.root()))); + } + + subs + } + + fn root_control_group(&self) -> Cgroup { + let b : &Hierarchy = self as &Hierarchy; + Cgroup::load(Box::new(&*b), "".to_string()) + } + + fn check_support(&self, _sub: Controllers) -> bool { + return false; + } + + fn root(&self) -> PathBuf { + PathBuf::from(self.root.clone()) + } +} + impl V1 { /// Finds where control groups are mounted to and returns a hierarchy in which control groups /// can be created. - pub fn new() -> Self { + pub fn new() -> V1 { let mount_point = find_v1_mount().unwrap(); V1 { mount_point: mount_point, @@ -110,6 +193,36 @@ impl V1 { } } +impl V2 { + /// Finds where control groups are mounted to and returns a hierarchy in which control groups + /// can be created. + pub fn new() -> V2 { + V2 { + root: String::from(UNIFIED_MOUNTPOINT), + } + } +} + +pub const UNIFIED_MOUNTPOINT: &'static str = "/sys/fs/cgroup"; + +pub fn is_cgroup2_unified_mode() -> bool { + let path = Path::new(UNIFIED_MOUNTPOINT); + let fs_stat = statfs::statfs(path); + if fs_stat.is_err() { + return false + } + + fs_stat.unwrap().filesystem_type() == statfs::CGROUP2_SUPER_MAGIC +} + +pub fn auto() -> Box { + if is_cgroup2_unified_mode() { + Box::new(V2::new()) + }else{ + Box::new(V1::new()) + } +} + fn find_v1_mount() -> Option { // Open mountinfo so we can get a parseable mount list let mountinfo_path = Path::new("/proc/self/mountinfo"); @@ -125,7 +238,7 @@ fn find_v1_mount() -> Option { let line = _line.unwrap(); let mut fields = line.split_whitespace(); let index = line.find(" - ").unwrap(); - let mut more_fields = line[index + 3..].split_whitespace().collect::>(); + let more_fields = line[index + 3..].split_whitespace().collect::>(); if more_fields.len() == 0 { continue; } diff --git a/src/hugetlb.rs b/src/hugetlb.rs index 3a1560f..dcf98fa 100644 --- a/src/hugetlb.rs +++ b/src/hugetlb.rs @@ -23,6 +23,7 @@ pub struct HugeTlbController { base: PathBuf, path: PathBuf, sizes: Vec, + v2: bool, } impl ControllerInternal for HugeTlbController { @@ -39,6 +40,10 @@ impl ControllerInternal for HugeTlbController { &self.base } + fn is_v2(&self) -> bool { + self.v2 + } + fn apply(&self, res: &Resources) -> Result<()> { // get the resources that apply to this controller let res: &HugePageResources = &res.hugepages; @@ -85,14 +90,17 @@ fn read_u64_from(mut file: File) -> Result { impl HugeTlbController { /// Constructs a new `HugeTlbController` with `oroot` serving as the root of the control group. - pub fn new(oroot: PathBuf) -> Self { + pub fn new(oroot: PathBuf, v2: bool) -> Self { let mut root = oroot; - root.push(Self::controller_type().to_string()); + if !v2 { + root.push(Self::controller_type().to_string()); + } let sizes = get_hugepage_sizes().unwrap(); Self { base: root.clone(), path: root, sizes: sizes, + v2: v2, } } diff --git a/src/lib.rs b/src/lib.rs index c7b539b..f113330 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ use log::*; use std::fs::File; -use std::io::{BufRead, BufReader, Write}; +use std::io::{Read, BufRead, BufReader, Write}; use std::path::{Path, PathBuf}; pub mod blkio; @@ -11,6 +11,7 @@ pub mod cpuacct; pub mod cpuset; pub mod devices; pub mod error; +pub mod events; pub mod freezer; pub mod hierarchies; pub mod hugetlb; @@ -28,6 +29,7 @@ use crate::cpuacct::CpuAcctController; use crate::cpuset::CpuSetController; use crate::devices::DevicesController; use crate::error::*; +use crate::error::ErrorKind::*; use crate::freezer::FreezerController; use crate::hugetlb::HugeTlbController; use crate::memory::MemController; @@ -124,6 +126,9 @@ mod sealed { fn post_create(&self){ } + fn is_v2(&self) -> bool { + false + } fn verify_path(&self) -> Result<()> { if self.get_path().starts_with(self.get_base()) { @@ -152,6 +157,17 @@ mod sealed { } } + fn get_max_value(&self, f: &str) -> Result { + self.open_path(f, false).and_then(|mut file| { + let mut string = String::new(); + let res = file.read_to_string(&mut string); + match res { + Ok(_) => parse_max_value(&string), + Err(e) => Err(Error::with_cause(ReadFailed, e)), + } + }) + } + #[doc(hidden)] fn path_exists(&self, p: &str) -> bool { if let Err(_) = self.verify_path() { @@ -194,6 +210,8 @@ pub trait Controller { /// Get the list of tasks that this controller has. fn tasks(&self) -> Vec; + + fn v2(&self) -> bool; } impl Controller for T where T: ControllerInternal { @@ -256,6 +274,11 @@ impl Controller for T where T: ControllerInternal { Ok(v.into_iter().map(CgroupPid::from).collect()) }).unwrap_or(vec![]) } + + fn v2(&self) -> bool { + self.is_v2() + } + } #[doc(hidden)] @@ -275,6 +298,8 @@ pub trait Hierarchy { /// Return a handle to the root control group in the hierarchy. fn root_control_group(&self) -> Cgroup; + fn v2(&self) -> bool; + /// Checks whether a certain subsystem is supported in the hierarchy. /// /// This is an internal function and should not be used. @@ -288,16 +313,16 @@ pub struct MemoryResources { /// Whether values should be applied to the controller. pub update_values: bool, /// How much memory (in bytes) can the kernel consume. - pub kernel_memory_limit: u64, + pub kernel_memory_limit: i64, /// Upper limit of memory usage of the control group's tasks. - pub memory_hard_limit: u64, + pub memory_hard_limit: i64, /// How much memory the tasks in the control group can use when the system is under memory /// pressure. - pub memory_soft_limit: u64, + pub memory_soft_limit: i64, /// How much of the kernel's memory (in bytes) can be used for TCP-related buffers. - pub kernel_tcp_memory_limit: u64, + pub kernel_tcp_memory_limit: i64, /// How much memory and swap together can the tasks in the control group use. - pub memory_swap_limit: u64, + pub memory_swap_limit: i64, /// Controls the tendency of the kernel to swap out parts of the address space of the tasks to /// disk. Lower value implies less likely. /// @@ -316,7 +341,7 @@ pub struct PidResources { /// Note that attaching processes to the control group will still succeed _even_ if the limit /// would be violated, however forks/clones inside the control group will have with `EAGAIN` if /// they would violate the limit set here. - pub maximum_number_of_processes: pid::PidMax, + pub maximum_number_of_processes: MaxValue, } /// Resources limits about how the tasks can use the CPU. @@ -327,7 +352,7 @@ pub struct CpuResources { // cpuset /// A comma-separated list of CPU IDs where the task in the control group can run. Dashes /// between numbers indicate ranges. - pub cpus: String, + pub cpus: Option, /// Same syntax as the `cpus` field of this structure, but applies to memory nodes instead of /// processors. pub mems: String, @@ -584,3 +609,37 @@ impl Subsystem { } } } + + + +/// The values for `memory.hight` or `pids.max` +#[derive(Eq, PartialEq, Copy, Clone, Debug)] +pub enum MaxValue { + /// This value is returned when the text is `"max"`. + Max, + /// When the value is a numerical value, they are returned via this enum field. + Value(i64), +} + +impl Default for MaxValue { + fn default() -> Self { + MaxValue::Max + } +} + +pub fn parse_max_value(s: &String) -> Result { + if s.trim() == "max" { + return Ok(MaxValue::Max) + } + match s.trim().parse() { + Ok(val) => Ok(MaxValue::Value(val)), + Err(e) => Err(Error::with_cause(ParseError, e)), + } +} + +pub fn max_value_to_string(m: MaxValue) -> String { + match m { + MaxValue::Max => "max".to_string(), + MaxValue::Value(num) => num.to_string(), + } +} \ No newline at end of file diff --git a/src/memory.rs b/src/memory.rs index a9adedb..8793459 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -6,14 +6,18 @@ use std::collections::HashMap; use std::fs::File; use std::io::{Read, Write}; use std::path::PathBuf; +use std::sync::mpsc::{Receiver}; use crate::error::ErrorKind::*; use crate::error::*; +use crate::events; use crate::{ ControllIdentifier, ControllerInternal, Controllers, MemoryResources, Resources, Subsystem, }; +use crate::{MaxValue, max_value_to_string, parse_max_value}; + /// A controller that allows controlling the `memory` subsystem of a Cgroup. /// /// In essence, using the memory controller, the user can gather statistics about the memory usage @@ -23,6 +27,16 @@ use crate::{ pub struct MemController { base: PathBuf, path: PathBuf, + v2: bool, +} + + +#[derive(Default, Debug, PartialEq, Eq)] +pub struct SetMemory { + pub low: Option, + pub high: Option, + pub min: Option, + pub max: Option, } /// Controls statistics and controls about the OOM killer operating in this control group. @@ -240,8 +254,8 @@ pub struct MemoryStat { pub inactive_file: u64, pub active_file: u64, pub unevictable: u64, - pub hierarchical_memory_limit: u64, - pub hierarchical_memsw_limit: u64, + pub hierarchical_memory_limit: i64, + pub hierarchical_memsw_limit: i64, pub total_cache: u64, pub total_rss: u64, pub total_rss_huge: u64, @@ -296,8 +310,8 @@ fn parse_memory_stat(s: String) -> Result { inactive_file: *raw.get("inactive_file").unwrap_or(&0), active_file: *raw.get("active_file").unwrap_or(&0), unevictable: *raw.get("unevictable").unwrap_or(&0), - hierarchical_memory_limit: *raw.get("hierarchical_memory_limit").unwrap_or(&0), - hierarchical_memsw_limit: *raw.get("hierarchical_memsw_limit").unwrap_or(&0), + hierarchical_memory_limit: *raw.get("hierarchical_memory_limit").unwrap_or(&0) as i64, + hierarchical_memsw_limit: *raw.get("hierarchical_memsw_limit").unwrap_or(&0) as i64, total_cache: *raw.get("total_cache").unwrap_or(&0), total_rss: *raw.get("total_rss").unwrap_or(&0), total_rss_huge: *raw.get("total_rss_huge").unwrap_or(&0), @@ -326,7 +340,7 @@ pub struct MemSwap { /// How many times the limit has been hit. pub fail_cnt: u64, /// Memory and swap usage limit in bytes. - pub limit_in_bytes: u64, + pub limit_in_bytes: i64, /// Current usage of memory and swap in bytes. pub usage_in_bytes: u64, /// The maximum observed usage of memory and swap in bytes. @@ -340,7 +354,7 @@ pub struct Memory { /// How many times the limit has been hit. pub fail_cnt: u64, /// The limit in bytes of the memory usage of the control group's tasks. - pub limit_in_bytes: u64, + pub limit_in_bytes: i64, /// The current usage of memory by the control group's tasks. pub usage_in_bytes: u64, /// The maximum observed usage of memory by the control group's tasks. @@ -362,7 +376,7 @@ pub struct Memory { pub oom_control: OomControl, /// Allows setting a limit to memory usage which is enforced when the system (note, _not_ the /// control group) detects memory pressure. - pub soft_limit_in_bytes: u64, + pub soft_limit_in_bytes: i64, /// Contains a wide array of statistics about the memory usage of the tasks in the control /// group. pub stat: MemoryStat, @@ -385,7 +399,7 @@ pub struct Tcp { pub fail_cnt: u64, /// The limit in bytes of the memory usage of the kernel's TCP buffers by control group's /// tasks. - pub limit_in_bytes: u64, + pub limit_in_bytes: i64, /// The current memory used by the kernel's TCP buffers related to these tasks. pub usage_in_bytes: u64, /// The observed maximum usage of memory by the kernel's TCP buffers (that originated from @@ -402,7 +416,7 @@ pub struct Kmem { /// How many times the limit has been hit. pub fail_cnt: u64, /// The limit in bytes of the kernel memory used by the control group's tasks. - pub limit_in_bytes: u64, + pub limit_in_bytes: i64, /// The current usage of kernel memory used by the control group's tasks, in bytes. pub usage_in_bytes: u64, /// The maximum observed usage of kernel memory used by the control group's tasks, in bytes. @@ -425,6 +439,10 @@ impl ControllerInternal for MemController { &self.base } + fn is_v2(&self) -> bool { + self.v2 + } + fn apply(&self, res: &Resources) -> Result<()> { // get the resources that apply to this controller let memres: &MemoryResources = &res.memory; @@ -444,15 +462,48 @@ impl ControllerInternal for MemController { impl MemController { /// Contructs a new `MemController` with `oroot` serving as the root of the control group. - pub fn new(oroot: PathBuf) -> Self { + pub fn new(oroot: PathBuf, v2: bool) -> Self { let mut root = oroot; - root.push(Self::controller_type().to_string()); + if !v2 { + root.push(Self::controller_type().to_string()); + } Self { base: root.clone(), path: root, + v2: v2, } } + // for v2 + pub fn set_mem(&self, m: SetMemory) -> Result<()> { + let values = vec![(m.high, "memory.high"),(m.low, "memory.low"),(m.max, "memory.max"),(m.min, "memory.min")]; + for value in values{ + let v = value.0; + let f = value.1; + if v.is_some() { + let v = v.unwrap(); + let v = max_value_to_string(v); + self.open_path(f, true) + .and_then(|mut file| { + file.write_all(v.as_ref()) + .map_err(|e| Error::with_cause(WriteFailed, e)) + })?; + } + } + Ok(()) + } + + // for v2 + pub fn get_mem(&self) -> Result { + let mut m: SetMemory = Default::default(); + self.get_max_value("memory.high").map(|x| m.high = Some(x)); + self.get_max_value("memory.low").map(|x| m.low = Some(x)); + self.get_max_value("memory.max").map(|x| m.max = Some(x)); + self.get_max_value("memory.min").map(|x| m.min = Some(x)); + + Ok(m) + } + /// Gathers overall statistics (and the current state of) about the memory usage of the control /// group's tasks. /// @@ -466,7 +517,7 @@ impl MemController { .unwrap_or(0), limit_in_bytes: self .open_path("memory.limit_in_bytes", false) - .and_then(read_u64_from) + .and_then(read_i64_from) .unwrap_or(0), usage_in_bytes: self .open_path("memory.usage_in_bytes", false) @@ -492,7 +543,7 @@ impl MemController { .unwrap_or(OomControl::default()), soft_limit_in_bytes: self .open_path("memory.soft_limit_in_bytes", false) - .and_then(read_u64_from) + .and_then(read_i64_from) .unwrap_or(0), stat: self .open_path("memory.stat", false) @@ -519,8 +570,8 @@ impl MemController { .unwrap_or(0), limit_in_bytes: self .open_path("memory.kmem.limit_in_bytes", false) - .and_then(read_u64_from) - .unwrap_or(0), + .and_then(read_i64_from) + .unwrap_or(-1), usage_in_bytes: self .open_path("memory.kmem.usage_in_bytes", false) .and_then(read_u64_from) @@ -546,7 +597,7 @@ impl MemController { .unwrap_or(0), limit_in_bytes: self .open_path("memory.kmem.tcp.limit_in_bytes", false) - .and_then(read_u64_from) + .and_then(read_i64_from) .unwrap_or(0), usage_in_bytes: self .open_path("memory.kmem.tcp.usage_in_bytes", false) @@ -569,7 +620,7 @@ impl MemController { .unwrap_or(0), limit_in_bytes: self .open_path("memory.memsw.limit_in_bytes", false) - .and_then(read_u64_from) + .and_then(read_i64_from) .unwrap_or(0), usage_in_bytes: self .open_path("memory.memsw.usage_in_bytes", false) @@ -618,7 +669,7 @@ impl MemController { } /// Set the memory usage limit of the control group, in bytes. - pub fn set_limit(&self, limit: u64) -> Result<()> { + pub fn set_limit(&self, limit: i64) -> Result<()> { self.open_path("memory.limit_in_bytes", true) .and_then(|mut file| { file.write_all(limit.to_string().as_ref()) @@ -627,7 +678,7 @@ impl MemController { } /// Set the kernel memory limit of the control group, in bytes. - pub fn set_kmem_limit(&self, limit: u64) -> Result<()> { + pub fn set_kmem_limit(&self, limit: i64) -> Result<()> { self.open_path("memory.kmem.limit_in_bytes", true) .and_then(|mut file| { file.write_all(limit.to_string().as_ref()) @@ -636,7 +687,7 @@ impl MemController { } /// Set the memory+swap limit of the control group, in bytes. - pub fn set_memswap_limit(&self, limit: u64) -> Result<()> { + pub fn set_memswap_limit(&self, limit: i64) -> Result<()> { self.open_path("memory.memsw.limit_in_bytes", true) .and_then(|mut file| { file.write_all(limit.to_string().as_ref()) @@ -645,7 +696,7 @@ impl MemController { } /// Set how much kernel memory can be used for TCP-related buffers by the control group. - pub fn set_tcp_limit(&self, limit: u64) -> Result<()> { + pub fn set_tcp_limit(&self, limit: i64) -> Result<()> { self.open_path("memory.kmem.tcp.limit_in_bytes", true) .and_then(|mut file| { file.write_all(limit.to_string().as_ref()) @@ -657,7 +708,7 @@ impl MemController { /// /// This limit is enforced when the system is nearing OOM conditions. Contrast this with the /// hard limit, which is _always_ enforced. - pub fn set_soft_limit(&self, limit: u64) -> Result<()> { + pub fn set_soft_limit(&self, limit: i64) -> Result<()> { self.open_path("memory.soft_limit_in_bytes", true) .and_then(|mut file| { file.write_all(limit.to_string().as_ref()) @@ -684,6 +735,14 @@ impl MemController { .map_err(|e| Error::with_cause(WriteFailed, e)) }) } + + pub fn register_oom_event(&self, key: &str) -> Result>{ + if self.v2{ + events::notify_on_oom_v2(key, self.get_path()) + }else { + events::notify_on_oom_v1(key, self.get_path()) + } + } } impl ControllIdentifier for MemController { @@ -717,6 +776,17 @@ fn read_u64_from(mut file: File) -> Result { } } +fn read_i64_from(mut file: File) -> Result { + let mut string = String::new(); + match file.read_to_string(&mut string) { + Ok(_) => string + .trim() + .parse() + .map_err(|e| Error::with_cause(ParseError, e)), + Err(e) => Err(Error::with_cause(ReadFailed, e)), + } +} + fn read_string_from(mut file: File) -> Result { let mut string = String::new(); match file.read_to_string(&mut string) { @@ -727,6 +797,7 @@ fn read_string_from(mut file: File) -> Result { #[cfg(test)] mod tests { + use std::collections::HashMap; use crate::memory::{ parse_memory_stat, parse_numa_stat, parse_oom_control, MemoryStat, NumaStat, OomControl, }; @@ -830,6 +901,7 @@ total_unevictable 81920 #[test] fn test_parse_memory_stat() { let ok = parse_memory_stat(GOOD_MEMORYSTAT_VAL.to_string()).unwrap(); + let raw = ok.raw.clone(); assert_eq!( ok, MemoryStat { @@ -869,6 +941,7 @@ total_unevictable 81920 total_inactive_file: 1272135680, total_active_file: 2338816000, total_unevictable: 81920, + raw: raw, } ); } diff --git a/src/pid.rs b/src/pid.rs index d7f42d2..3be3da0 100644 --- a/src/pid.rs +++ b/src/pid.rs @@ -10,7 +10,7 @@ use crate::error::*; use crate::error::ErrorKind::*; use crate::{ - ControllIdentifier, ControllerInternal, Controllers, PidResources, Resources, Subsystem, + ControllIdentifier, ControllerInternal, Controllers, MaxValue, max_value_to_string, parse_max_value, PidResources, Resources, Subsystem, }; /// A controller that allows controlling the `pids` subsystem of a Cgroup. @@ -18,22 +18,7 @@ use crate::{ pub struct PidController { base: PathBuf, path: PathBuf, -} - -/// The values found in the `pids.max` file in a Cgroup's `pids` subsystem. -#[derive(Eq, PartialEq, Copy, Clone, Debug)] -pub enum PidMax { - /// This value is returned when the text found `pids.max` is `"max"`. - Max, - /// When the value in `pids.max` is a numerical value, they are returned via this enum field. - Value(i64), -} - -impl Default for PidMax { - /// By default, (as per the kernel) `pids.max` should contain `"max"`. - fn default() -> Self { - PidMax::Max - } + v2: bool, } impl ControllerInternal for PidController { @@ -50,6 +35,10 @@ impl ControllerInternal for PidController { &self.base } + fn is_v2(&self) -> bool { + self.v2 + } + fn apply(&self, res: &Resources) -> Result<()> { // get the resources that apply to this controller let pidres: &PidResources = &res.pid; @@ -107,12 +96,15 @@ fn read_u64_from(mut file: File) -> Result { impl PidController { /// Constructors a new `PidController` instance, with `oroot` serving as the controller's root /// directory. - pub fn new(oroot: PathBuf) -> Self { + pub fn new(oroot: PathBuf, v2: bool) -> Self { let mut root = oroot; - root.push(Self::controller_type().to_string()); + if !v2 { + root.push(Self::controller_type().to_string()); + } Self { base: root.clone(), path: root, + v2: v2, } } @@ -140,19 +132,12 @@ impl PidController { } /// The maximum number of processes that can exist at one time in the control group. - pub fn get_pid_max(&self) -> Result { + pub fn get_pid_max(&self) -> Result { self.open_path("pids.max", false).and_then(|mut file| { let mut string = String::new(); let res = file.read_to_string(&mut string); match res { - Ok(_) => if string.trim() == "max" { - Ok(PidMax::Max) - } else { - match string.trim().parse() { - Ok(val) => Ok(PidMax::Value(val)), - Err(e) => Err(Error::with_cause(ParseError, e)), - } - }, + Ok(_) => parse_max_value(&string), Err(e) => Err(Error::with_cause(ReadFailed, e)), } }) @@ -163,12 +148,9 @@ impl PidController { /// Note that if `get_pid_current()` returns a higher number than what you /// are about to set (`max_pid`), then no processess will be killed. Additonally, attaching /// extra processes to a control group disregards the limit. - pub fn set_pid_max(&self, max_pid: PidMax) -> Result<()> { + pub fn set_pid_max(&self, max_pid: MaxValue) -> Result<()> { self.open_path("pids.max", true).and_then(|mut file| { - let string_to_write = match max_pid { - PidMax::Max => "max".to_string(), - PidMax::Value(num) => num.to_string(), - }; + let string_to_write = max_value_to_string(max_pid); match file.write_all(string_to_write.as_ref()) { Ok(_) => Ok(()), Err(e) => Err(Error::with_cause(WriteFailed, e)), diff --git a/tests/builder.rs b/tests/builder.rs index 45c1112..bd3687c 100644 --- a/tests/builder.rs +++ b/tests/builder.rs @@ -11,8 +11,9 @@ use cgroups::cgroup_builder::*; #[test] pub fn test_cpu_res_build() { - let v1 = crate::hierarchies::V1::new(); - let cg: Cgroup = CgroupBuilder::new("test_cpu_res_build", &v1) + let h = cgroups::hierarchies::auto(); + let h = Box::new(&*h); + let cg: Cgroup = CgroupBuilder::new("test_cpu_res_build", h) .cpu() .shares(85) .done() @@ -29,8 +30,9 @@ pub fn test_cpu_res_build() { #[test] pub fn test_memory_res_build() { - let v1 = crate::hierarchies::V1::new(); - let cg: Cgroup = CgroupBuilder::new("test_memory_res_build", &v1) + let h = cgroups::hierarchies::auto(); + let h = Box::new(&*h); + let cg: Cgroup = CgroupBuilder::new("test_memory_res_build", h) .memory() .kernel_memory_limit(128 * 1024 * 1024) .swappiness(70) @@ -50,17 +52,18 @@ pub fn test_memory_res_build() { #[test] pub fn test_pid_res_build() { - let v1 = crate::hierarchies::V1::new(); - let cg: Cgroup = CgroupBuilder::new("test_pid_res_build", &v1) + let h = cgroups::hierarchies::auto(); + let h = Box::new(&*h); + let cg: Cgroup = CgroupBuilder::new("test_pid_res_build", h) .pid() - .maximum_number_of_processes(PidMax::Value(123)) + .maximum_number_of_processes(MaxValue::Value(123)) .done() .build(); { let c: &PidController = cg.controller_of().unwrap(); assert!(c.get_pid_max().is_ok()); - assert_eq!(c.get_pid_max().unwrap(), PidMax::Value(123)); + assert_eq!(c.get_pid_max().unwrap(), MaxValue::Value(123)); } cg.delete(); @@ -69,8 +72,9 @@ pub fn test_pid_res_build() { #[test] #[ignore] // ignore this test for now, not sure why my kernel doesn't like it pub fn test_devices_res_build() { - let v1 = crate::hierarchies::V1::new(); - let cg: Cgroup = CgroupBuilder::new("test_devices_res_build", &v1) + let h = cgroups::hierarchies::auto(); + let h = Box::new(&*h); + let cg: Cgroup = CgroupBuilder::new("test_devices_res_build", h) .devices() .device(1, 6, DeviceType::Char, true, vec![DevicePermissions::Read]) @@ -95,8 +99,13 @@ pub fn test_devices_res_build() { #[test] pub fn test_network_res_build() { - let v1 = crate::hierarchies::V1::new(); - let cg: Cgroup = CgroupBuilder::new("test_network_res_build", &v1) + let h = cgroups::hierarchies::auto(); + if h.v2() { + // FIXME + return + } + let h = Box::new(&*h); + let cg: Cgroup = CgroupBuilder::new("test_network_res_build", h) .network() .class_id(1337) .done() @@ -112,8 +121,13 @@ pub fn test_network_res_build() { #[test] pub fn test_hugepages_res_build() { - let v1 = crate::hierarchies::V1::new(); - let cg: Cgroup = CgroupBuilder::new("test_hugepages_res_build", &v1) + let h = cgroups::hierarchies::auto(); + if h.v2() { + // FIXME + return + } + let h = Box::new(&*h); + let cg: Cgroup = CgroupBuilder::new("test_hugepages_res_build", h) .hugepages() .limit("2MB".to_string(), 4 * 2 * 1024 * 1024) .done() @@ -129,8 +143,9 @@ pub fn test_hugepages_res_build() { #[test] pub fn test_blkio_res_build() { - let v1 = crate::hierarchies::V1::new(); - let cg: Cgroup = CgroupBuilder::new("test_blkio_res_build", &v1) + let h = cgroups::hierarchies::auto(); + let h = Box::new(&*h); + let cg: Cgroup = CgroupBuilder::new("test_blkio_res_build", h) .blkio() .weight(100) .done() diff --git a/tests/cgroup.rs b/tests/cgroup.rs index 8b15d9a..177eea3 100644 --- a/tests/cgroup.rs +++ b/tests/cgroup.rs @@ -1,11 +1,12 @@ //! Simple unit tests about the control groups system. -use cgroups::{Cgroup, CgroupPid}; +use cgroups::{Cgroup, CgroupPid, Hierarchy}; #[test] fn test_tasks_iterator() { - let hier = cgroups::hierarchies::V1::new(); + let h = cgroups::hierarchies::auto(); + let h = Box::new(&*h); let pid = libc::pid_t::from(nix::unistd::getpid()) as u64; - let cg = Cgroup::new(&hier, String::from("test_tasks_iterator")); + let cg = Cgroup::new(h, String::from("test_tasks_iterator")); { // Add a task to the control group. cg.add_task(CgroupPid::from(pid)); diff --git a/tests/cpuset.rs b/tests/cpuset.rs index c7a8fb0..bd7d8a4 100644 --- a/tests/cpuset.rs +++ b/tests/cpuset.rs @@ -1,11 +1,12 @@ use cgroups::cpuset::CpuSetController; use cgroups::error::ErrorKind; -use cgroups::Cgroup; +use cgroups::{Cgroup, CpuResources, Hierarchy, Resources}; #[test] fn test_cpuset_memory_pressure_root_cg() { - let hier = cgroups::hierarchies::V1::new(); - let cg = Cgroup::new(&hier, String::from("test_cpuset_memory_pressure_root_cg")); + let h = cgroups::hierarchies::auto(); + let h = Box::new(&*h); + let cg = Cgroup::new(h, String::from("test_cpuset_memory_pressure_root_cg")); { let cpuset: &CpuSetController = cg.controller_of().unwrap(); @@ -15,3 +16,39 @@ fn test_cpuset_memory_pressure_root_cg() { } cg.delete(); } + + +#[test] +fn test_cpuset_set_cpus() { + let h = cgroups::hierarchies::auto(); + let h = Box::new(&*h); + let cg = Cgroup::new(h, String::from("test_cpuset_set_cpus")); + { + let cpuset: &CpuSetController = cg.controller_of().unwrap(); + + let set = cpuset.cpuset(); + assert_eq!(0, set.cpus.len()); + + // 0 + let r = cpuset.set_cpus("0"); + assert_eq!(true, r.is_ok()); + + let set = cpuset.cpuset(); + assert_eq!(1, set.cpus.len()); + assert_eq!((0,0), set.cpus[0]); + + + // 0-1 + // FIXME need two cores + let r = cpuset.set_cpus("0-1"); + assert_eq!(true, r.is_ok()); + + let set = cpuset.cpuset(); + assert_eq!(1, set.cpus.len()); + assert_eq!((0,1), set.cpus[0]); + + + + } + cg.delete(); +} \ No newline at end of file diff --git a/tests/devices.rs b/tests/devices.rs index 2fbb305..891c8a9 100644 --- a/tests/devices.rs +++ b/tests/devices.rs @@ -1,12 +1,13 @@ //! Integration tests about the devices subsystem use cgroups::devices::{DevicePermissions, DeviceType, DevicesController}; -use cgroups::{Cgroup, DeviceResource}; +use cgroups::{Cgroup, DeviceResource, Hierarchy}; #[test] fn test_devices_parsing() { - let hier = cgroups::hierarchies::V1::new(); - let cg = Cgroup::new(&hier, String::from("test_devices_parsing")); + let h = cgroups::hierarchies::auto(); + let h = Box::new(&*h); + let cg = Cgroup::new(h, String::from("test_devices_parsing")); { let devices: &DevicesController = cg.controller_of().unwrap(); diff --git a/tests/hugetlb.rs b/tests/hugetlb.rs index 00ac46a..f7b0afb 100644 --- a/tests/hugetlb.rs +++ b/tests/hugetlb.rs @@ -1,6 +1,6 @@ //! Integration tests about the hugetlb subsystem use cgroups::hugetlb::HugeTlbController; -use cgroups::Cgroup; +use cgroups::{Cgroup, Hierarchy}; use cgroups::Controller; use cgroups::error::ErrorKind::*; @@ -8,8 +8,9 @@ use cgroups::error::*; #[test] fn test_hugetlb_sizes() { - let hier = cgroups::hierarchies::V1::new(); - let cg = Cgroup::new(&hier, String::from("test_hugetlb_sizes")); + let h = cgroups::hierarchies::auto(); + let h = Box::new(&*h); + let cg = Cgroup::new(h, String::from("test_hugetlb_sizes")); { let hugetlb_controller: &HugeTlbController = cg.controller_of().unwrap(); let sizes = hugetlb_controller.get_sizes(); diff --git a/tests/memory.rs b/tests/memory.rs index c80f35b..8838afe 100644 --- a/tests/memory.rs +++ b/tests/memory.rs @@ -1,6 +1,6 @@ //! Integration tests about the hugetlb subsystem -use cgroups::memory::MemController; -use cgroups::Cgroup; +use cgroups::memory::{MemController, SetMemory}; +use cgroups::{Cgroup, Hierarchy, MaxValue}; use cgroups::Controller; use cgroups::error::ErrorKind::*; @@ -8,8 +8,9 @@ use cgroups::error::*; #[test] fn test_disable_oom_killer() { - let hier = cgroups::hierarchies::V1::new(); - let cg = Cgroup::new(&hier, String::from("test_disable_oom_killer")); + let h = cgroups::hierarchies::auto(); + let h = Box::new(&*h); + let cg = Cgroup::new(h, String::from("test_disable_oom_killer")); { let mem_controller: &MemController = cg.controller_of().unwrap(); @@ -17,13 +18,77 @@ fn test_disable_oom_killer() { let m = mem_controller.memory_stat(); assert_eq!(m.oom_control.oom_kill_disable, false); - // disable oom killer - let r = mem_controller.disable_oom_killer(); - assert_eq!(r.is_err(), false); + // FIXME only v1 + if !mem_controller.v2(){ + // disable oom killer + let r = mem_controller.disable_oom_killer(); + assert_eq!(r.is_err(), false); + + // after disable + let m = mem_controller.memory_stat(); + assert_eq!(m.oom_control.oom_kill_disable, true); + } - // after disable - let m = mem_controller.memory_stat(); - assert_eq!(m.oom_control.oom_kill_disable, true); } cg.delete(); } + +#[test] +fn set_mem_v2() { + let h = cgroups::hierarchies::auto(); + if !h.v2() { + return + } + + let h = Box::new(&*h); + let cg = Cgroup::new(h, String::from("set_mem_v2")); + { + let mem_controller: &MemController = cg.controller_of().unwrap(); + + // before disable + let m = mem_controller.get_mem().unwrap(); + // case 1: get default value + assert_eq!(m.low, Some(MaxValue::Value(0))); + assert_eq!(m.min, Some(MaxValue::Value(0))); + assert_eq!(m.high, Some(MaxValue::Max)); + assert_eq!(m.max, Some(MaxValue::Max)); + + // case 2: set parts + let m = SetMemory{ + low: Some(MaxValue::Value(1024*1024* 2)), + high: Some(MaxValue::Value(1024*1024*1024* 2)), + min: Some(MaxValue::Value(1024*1024* 3)), + max: None, + }; + let r = mem_controller.set_mem(m); + assert_eq!(true, r.is_ok()); + + let m = mem_controller.get_mem().unwrap(); + // get + assert_eq!(m.low, Some(MaxValue::Value(1024*1024* 2))); + assert_eq!(m.min, Some(MaxValue::Value(1024*1024* 3))); + assert_eq!(m.high, Some(MaxValue::Value(1024*1024*1024* 2))); + assert_eq!(m.max, Some(MaxValue::Max)); + + + + // case 3: set parts + let m = SetMemory{ + max: Some(MaxValue::Value(1024*1024*1024* 2)), + min: Some(MaxValue::Value(1024*1024* 4)), + high: Some(MaxValue::Max), + low: None, + }; + let r = mem_controller.set_mem(m); + assert_eq!(true, r.is_ok()); + + let m = mem_controller.get_mem().unwrap(); + // get + assert_eq!(m.low, Some(MaxValue::Value(1024*1024* 2))); + assert_eq!(m.min, Some(MaxValue::Value(1024*1024* 4))); + assert_eq!(m.max, Some(MaxValue::Value(1024*1024*1024* 2))); + assert_eq!(m.high, Some(MaxValue::Max)); + } + + cg.delete(); +} diff --git a/tests/pids.rs b/tests/pids.rs index 4c91e54..74dd792 100644 --- a/tests/pids.rs +++ b/tests/pids.rs @@ -1,7 +1,7 @@ //! Integration tests about the pids subsystem -use cgroups::pid::{PidController, PidMax}; +use cgroups::pid::{PidController}; use cgroups::Controller; -use cgroups::{Cgroup, CgroupPid, PidResources, Resources}; +use cgroups::{Cgroup, CgroupPid, Hierarchy, MaxValue, PidResources, Resources}; use nix::sys::wait::{waitpid, WaitStatus}; use nix::unistd::{fork, ForkResult, Pid}; @@ -12,22 +12,24 @@ use std::thread; #[test] fn create_and_delete_cgroup() { - let hier = cgroups::hierarchies::V1::new(); - let cg = Cgroup::new(&hier, String::from("create_and_delete_cgroup")); + let h = cgroups::hierarchies::auto(); + let h = Box::new(&*h); + let cg = Cgroup::new(h, String::from("create_and_delete_cgroup")); { let pidcontroller: &PidController = cg.controller_of().unwrap(); - pidcontroller.set_pid_max(PidMax::Value(1337)); + pidcontroller.set_pid_max(MaxValue::Value(1337)); let max = pidcontroller.get_pid_max(); assert!(max.is_ok()); - assert_eq!(max.unwrap(), PidMax::Value(1337)); + assert_eq!(max.unwrap(), MaxValue::Value(1337)); } cg.delete(); } #[test] fn test_pids_current_is_zero() { - let hier = cgroups::hierarchies::V1::new(); - let cg = Cgroup::new(&hier, String::from("test_pids_current_is_zero")); + let h = cgroups::hierarchies::auto(); + let h = Box::new(&*h); + let cg = Cgroup::new(h, String::from("test_pids_current_is_zero")); { let pidcontroller: &PidController = cg.controller_of().unwrap(); let current = pidcontroller.get_pid_current(); @@ -38,8 +40,9 @@ fn test_pids_current_is_zero() { #[test] fn test_pids_events_is_zero() { - let hier = cgroups::hierarchies::V1::new(); - let cg = Cgroup::new(&hier, String::from("test_pids_events_is_zero")); + let h = cgroups::hierarchies::auto(); + let h = Box::new(&*h); + let cg = Cgroup::new(h, String::from("test_pids_events_is_zero")); { let pidcontroller: &PidController = cg.controller_of().unwrap(); let events = pidcontroller.get_pid_events(); @@ -51,8 +54,9 @@ fn test_pids_events_is_zero() { #[test] fn test_pid_events_is_not_zero() { - let hier = cgroups::hierarchies::V1::new(); - let cg = Cgroup::new(&hier, String::from("test_pid_events_is_not_zero")); + let h = cgroups::hierarchies::auto(); + let h = Box::new(&*h); + let cg = Cgroup::new(h, String::from("test_pid_events_is_not_zero")); { let pids: &PidController = cg.controller_of().unwrap(); let before = pids.get_pid_events(); @@ -66,7 +70,7 @@ fn test_pid_events_is_not_zero() { println!("added task to cg: {:?}", child); // Set limit to one - pids.set_pid_max(PidMax::Value(1)); + pids.set_pid_max(MaxValue::Value(1)); println!("err = {:?}", pids.get_pid_max()); // wait on the child @@ -84,7 +88,7 @@ fn test_pid_events_is_not_zero() { } Ok(ForkResult::Child) => loop { let pids_max = pids.get_pid_max(); - if pids_max.is_ok() && pids_max.unwrap() == PidMax::Value(1) { + if pids_max.is_ok() && pids_max.unwrap() == MaxValue::Value(1) { if let Err(_) = fork() { unsafe { libc::exit(0) }; } else { diff --git a/tests/resources.rs b/tests/resources.rs index c612df1..d26a896 100644 --- a/tests/resources.rs +++ b/tests/resources.rs @@ -1,16 +1,17 @@ //! Integration test about setting resources using `apply()` -use cgroups::pid::{PidController, PidMax}; -use cgroups::{Cgroup, PidResources, Resources}; +use cgroups::pid::{PidController}; +use cgroups::{Cgroup, Hierarchy, MaxValue, PidResources, Resources}; #[test] fn pid_resources() { - let hier = cgroups::hierarchies::V1::new(); - let cg = Cgroup::new(&hier, String::from("pid_resources")); + let h = cgroups::hierarchies::auto(); + let h = Box::new(&*h); + let cg = Cgroup::new(h, String::from("pid_resources")); { let res = Resources { pid: PidResources { update_values: true, - maximum_number_of_processes: PidMax::Value(512), + maximum_number_of_processes: MaxValue::Value(512), }, ..Default::default() }; @@ -20,7 +21,7 @@ fn pid_resources() { let pidcontroller: &PidController = cg.controller_of().unwrap(); let pid_max = pidcontroller.get_pid_max(); assert_eq!(pid_max.is_ok(), true); - assert_eq!(pid_max.unwrap(), PidMax::Value(512)); + assert_eq!(pid_max.unwrap(), MaxValue::Value(512)); } cg.delete(); }