change *limit* in memory from u64 to i64

Signed-off-by: bin liu <bin@hyper.sh>
This commit is contained in:
bin liu
2020-08-25 13:28:36 +08:00
parent c702852fd7
commit 9fe6cb58e4
21 changed files with 700 additions and 166 deletions

View File

@@ -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"

View File

@@ -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,
}
}

View File

@@ -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(())
}

View File

@@ -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);

View File

@@ -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,
}
}

View File

@@ -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,
}
}

View File

@@ -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
View 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)
}

View File

@@ -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;
}

View File

@@ -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,
}
}

View File

@@ -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(),
}
}

View File

@@ -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,
}
);
}

View File

@@ -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)),

View File

@@ -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()

View File

@@ -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));

View File

@@ -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();
}

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();
}

View File

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

View File

@@ -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();
}