mirror of
https://github.com/kata-containers/cgroups-rs.git
synced 2026-08-05 02:13:23 +00:00
change *limit* in memory from u64 to i64
Signed-off-by: bin liu <bin@hyper.sh>
This commit is contained in:
@@ -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"
|
||||
|
||||
12
src/blkio.rs
12
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<u64> {
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Subsystem>,
|
||||
|
||||
/// 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<P: AsRef<Path>>(hier: &Hierarchy, path: P) -> Cgroup {
|
||||
pub fn new<P: AsRef<Path>>(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<P: AsRef<Path>>(hier: &Hierarchy, path: P) -> Cgroup {
|
||||
pub fn load<P: AsRef<Path>>(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<String>{
|
||||
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::<Vec<String>>()
|
||||
}
|
||||
|
||||
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::<Vec<&str>>();
|
||||
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(())
|
||||
}
|
||||
@@ -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<String>);
|
||||
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);
|
||||
|
||||
12
src/cpu.rs
12
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<u64> {
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Vec<(u64, u64)>> {
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
27
src/error.rs
27
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,
|
||||
|
||||
83
src/events.rs
Normal file
83
src/events.rs
Normal file
@@ -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<Receiver<String>> {
|
||||
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<Receiver<String>> {
|
||||
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<Receiver<String>> {
|
||||
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<Receiver<String>> {
|
||||
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)
|
||||
}
|
||||
@@ -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<Subsystem> {
|
||||
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<Subsystem> {
|
||||
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<dyn Hierarchy> {
|
||||
if is_cgroup2_unified_mode() {
|
||||
Box::new(V2::new())
|
||||
}else{
|
||||
Box::new(V1::new())
|
||||
}
|
||||
}
|
||||
|
||||
fn find_v1_mount() -> Option<String> {
|
||||
// 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<String> {
|
||||
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::<Vec<_>>();
|
||||
let more_fields = line[index + 3..].split_whitespace().collect::<Vec<_>>();
|
||||
if more_fields.len() == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ pub struct HugeTlbController {
|
||||
base: PathBuf,
|
||||
path: PathBuf,
|
||||
sizes: Vec<String>,
|
||||
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<u64> {
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
75
src/lib.rs
75
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<MaxValue> {
|
||||
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<CgroupPid>;
|
||||
|
||||
fn v2(&self) -> bool;
|
||||
}
|
||||
|
||||
impl<T> Controller for T where T: ControllerInternal {
|
||||
@@ -256,6 +274,11 @@ impl<T> 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<String>,
|
||||
/// 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<MaxValue> {
|
||||
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(),
|
||||
}
|
||||
}
|
||||
117
src/memory.rs
117
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<MaxValue>,
|
||||
pub high: Option<MaxValue>,
|
||||
pub min: Option<MaxValue>,
|
||||
pub max: Option<MaxValue>,
|
||||
}
|
||||
|
||||
/// 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<MemoryStat> {
|
||||
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<SetMemory> {
|
||||
let mut m: SetMemory = Default::default();
|
||||
self.get_max_value("memory.high").map(|x| m.high = Some(x));
|
||||
self.get_max_value("memory.low").map(|x| m.low = Some(x));
|
||||
self.get_max_value("memory.max").map(|x| m.max = Some(x));
|
||||
self.get_max_value("memory.min").map(|x| m.min = Some(x));
|
||||
|
||||
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<Receiver<String>>{
|
||||
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<u64> {
|
||||
}
|
||||
}
|
||||
|
||||
fn read_i64_from(mut file: File) -> Result<i64> {
|
||||
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<String> {
|
||||
let mut string = String::new();
|
||||
match file.read_to_string(&mut string) {
|
||||
@@ -727,6 +797,7 @@ fn read_string_from(mut file: File) -> Result<String> {
|
||||
|
||||
#[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,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
48
src/pid.rs
48
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<u64> {
|
||||
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<PidMax> {
|
||||
pub fn get_pid_max(&self) -> Result<MaxValue> {
|
||||
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)),
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user