Initial work

Signed-off-by: Levente Kurusa <lkurusa@acm.org>
This commit is contained in:
Levente Kurusa
2018-08-28 14:52:23 +02:00
parent 6951e922be
commit 9162c5a6ad
18 changed files with 2343 additions and 0 deletions

4
.gitignore vendored
View File

@@ -8,3 +8,7 @@ Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
/target
**/*.rs.bk
Cargo.lock

6
Cargo.toml Normal file
View File

@@ -0,0 +1,6 @@
[package]
name = "cgroups-rs"
version = "0.1.0"
authors = ["Levente Kurusa <lkurusa@acm.org>"]
[dependencies]

274
src/blkio.rs Normal file
View File

@@ -0,0 +1,274 @@
/* block IO controller */
use std::path::PathBuf;
use std::io::{Read, Write};
use std::fs::File;
use {BlkIoResources, Controllers, Controller, Resources, ControllIdentifier, Subsystem};
#[derive(Debug, Clone)]
pub struct BlkIoController {
base: PathBuf,
path: PathBuf,
}
#[derive(Debug)]
pub struct BlkIoThrottle {
pub io_service_bytes: String,
pub io_service_bytes_recursive: String,
pub io_serviced: String,
pub io_serviced_recursive: String,
pub read_bps_device: String,
pub read_iops_device: String,
pub write_bps_device: String,
pub write_iops_device: String,
}
#[derive(Debug)]
pub struct BlkIo {
pub io_merged: String,
pub io_merged_recursive: String,
pub io_queued: String,
pub io_queued_recursive: String,
pub io_service_bytes: String,
pub io_service_bytes_recursive: String,
pub io_serviced: String,
pub io_serviced_recursive: String,
pub io_service_time: String,
pub io_service_time_recursive: String,
pub io_wait_time: String,
pub io_wait_time_recursive: String,
pub leaf_weight: u64,
pub leaf_weight_device: String,
pub sectors: String,
pub sectors_recursive: String,
pub throttle: BlkIoThrottle,
pub time: String,
pub time_recursive: String,
pub weight: u64,
pub weight_device: String,
}
impl Controller for BlkIoController {
fn control_type(self: &Self) -> Controllers { Controllers::BlkIo }
fn get_path<'a>(self: &'a Self) -> &'a PathBuf { &self.path }
fn get_path_mut<'a>(self: &'a mut Self) -> &'a mut PathBuf { &mut self.path }
fn get_base<'a>(self: &'a Self) -> &'a PathBuf { &self.base }
fn apply(self: &Self, res: &Resources) {
/* get the resources that apply to this controller */
let res: &BlkIoResources = &res.blkio;
if res.update_values {
self.set_weight(res.weight as u64);
self.set_leaf_weight(res.leaf_weight as u64);
for dev in &res.weight_device {
self.set_weight_for_device(format!("{}:{} {}",
dev.major, dev.minor, dev.weight));
}
for dev in &res.throttle_read_bps_device {
self.throttle_read_bps_for_device(dev.major, dev.minor, dev.rate);
}
for dev in &res.throttle_write_bps_device {
self.throttle_write_bps_for_device(dev.major, dev.minor, dev.rate);
}
for dev in &res.throttle_read_iops_device {
self.throttle_read_iops_for_device(dev.major, dev.minor, dev.rate);
}
for dev in &res.throttle_write_iops_device {
self.throttle_write_iops_for_device(dev.major, dev.minor, dev.rate);
}
}
}
}
impl ControllIdentifier for BlkIoController {
fn controller_type() -> Controllers {
Controllers::BlkIo
}
}
impl<'a> From<&'a Subsystem> for &'a BlkIoController {
fn from(sub: &'a Subsystem) -> &'a BlkIoController {
unsafe {
match sub {
Subsystem::BlkIo(c) => c,
_ => {
assert_eq!(1, 0);
::std::mem::uninitialized()
},
}
}
}
}
fn read_string_from(mut file: File) -> Option<String> {
let mut string = String::new();
let _ = file.read_to_string(&mut string);
Some(string.trim().to_string())
}
fn read_u64_from(mut file: File) -> Option<u64> {
let mut string = String::new();
let _ = file.read_to_string(&mut string);
string.trim().parse().ok()
}
impl BlkIoController {
pub fn new(oroot: PathBuf) -> Self {
let mut root = oroot;
root.push(Self::controller_type().to_string());
Self {
base: root.clone(),
path: root,
}
}
pub fn blkio(self: &Self) -> BlkIo {
BlkIo {
io_merged: self.open_path("blkio.io_merged", false).and_then(|file| {
read_string_from(file)
}).unwrap_or("".to_string()),
io_merged_recursive: self.open_path("blkio.io_merged_recursive", false).and_then(|file| {
read_string_from(file)
}).unwrap_or("".to_string()),
io_queued: self.open_path("blkio.io_queued", false).and_then(|file| {
read_string_from(file)
}).unwrap_or("".to_string()),
io_queued_recursive: self.open_path("blkio.io_queued_recursive", false).and_then(|file| {
read_string_from(file)
}).unwrap_or("".to_string()),
io_service_bytes: self.open_path("blkio.io_service_bytes", false).and_then(|file| {
read_string_from(file)
}).unwrap_or("".to_string()),
io_service_bytes_recursive: self.open_path("blkio.io_service_bytes_recursive", false).and_then(|file| {
read_string_from(file)
}).unwrap_or("".to_string()),
io_serviced: self.open_path("blkio.io_serviced", false).and_then(|file| {
read_string_from(file)
}).unwrap_or("".to_string()),
io_serviced_recursive: self.open_path("blkio.io_serviced_recursive", false).and_then(|file| {
read_string_from(file)
}).unwrap_or("".to_string()),
io_service_time: self.open_path("blkio.io_service_time", false).and_then(|file| {
read_string_from(file)
}).unwrap_or("".to_string()),
io_service_time_recursive: self.open_path("blkio.io_service_time_recursive", false).and_then(|file| {
read_string_from(file)
}).unwrap_or("".to_string()),
io_wait_time: self.open_path("blkio.io_wait_time", false).and_then(|file| {
read_string_from(file)
}).unwrap_or("".to_string()),
io_wait_time_recursive: self.open_path("blkio.io_wait_time_recursive", false).and_then(|file| {
read_string_from(file)
}).unwrap_or("".to_string()),
leaf_weight: self.open_path("blkio.leaf_weight", false).and_then(|file| {
read_u64_from(file)
}).unwrap_or(0u64),
leaf_weight_device: self.open_path("blkio.leaf_weight_device", false).and_then(|file| {
read_string_from(file)
}).unwrap_or("".to_string()),
sectors: self.open_path("blkio.sectors", false).and_then(|file| {
read_string_from(file)
}).unwrap_or("".to_string()),
sectors_recursive: self.open_path("blkio.sectors_recursive", false).and_then(|file| {
read_string_from(file)
}).unwrap_or("".to_string()),
throttle: BlkIoThrottle {
io_service_bytes: self.open_path("blkio.throttle.io_service_bytes", false).and_then(|file| {
read_string_from(file)
}).unwrap_or("".to_string()),
io_service_bytes_recursive: self.open_path("blkio.throttle.io_service_bytes_recursive", false).and_then(|file| {
read_string_from(file)
}).unwrap_or("".to_string()),
io_serviced: self.open_path("blkio.throttle.io_serviced", false).and_then(|file| {
read_string_from(file)
}).unwrap_or("".to_string()),
io_serviced_recursive: self.open_path("blkio.throttle.io_serviced_recursive", false).and_then(|file| {
read_string_from(file)
}).unwrap_or("".to_string()),
read_bps_device: self.open_path("blkio.throttle.read_bps_device", false).and_then(|file| {
read_string_from(file)
}).unwrap_or("".to_string()),
read_iops_device: self.open_path("blkio.throttle.read_iops_device", false).and_then(|file| {
read_string_from(file)
}).unwrap_or("".to_string()),
write_bps_device: self.open_path("blkio.throttle.write_bps_device", false).and_then(|file| {
read_string_from(file)
}).unwrap_or("".to_string()),
write_iops_device: self.open_path("blkio.throttle.write_iops_device", false).and_then(|file| {
read_string_from(file)
}).unwrap_or("".to_string()),
},
time: self.open_path("blkio.time", false).and_then(|file| {
read_string_from(file)
}).unwrap_or("".to_string()),
time_recursive: self.open_path("blkio.time_recursive", false).and_then(|file| {
read_string_from(file)
}).unwrap_or("".to_string()),
weight: self.open_path("blkio.weight", false).and_then(|file| {
read_u64_from(file)
}).unwrap_or(0u64),
weight_device: self.open_path("blkio.weight_device", false).and_then(|file| {
read_string_from(file)
}).unwrap_or("".to_string()),
}
}
pub fn set_leaf_weight(self: &Self, w: u64) {
self.open_path("blkio.leaf_weight", true).and_then(|mut file| {
file.write_all(w.to_string().as_ref()).ok()
});
}
pub fn set_leaf_weight_for_device(self: &Self, d: String) {
self.open_path("blkio.leaf_weight_device", true).and_then(|mut file| {
file.write_all(d.as_ref()).ok()
});
}
pub fn reset_stats(self: &Self) {
self.open_path("blkio.leaf_weight_device", true).and_then(|mut file| {
file.write_all("1".to_string().as_ref()).ok()
});
}
pub fn throttle_read_bps_for_device(self: &Self, major: u64, minor: u64, bps: u64) {
self.open_path("blkio.throttle.read_bps_device", true).and_then(|mut file| {
file.write_all(format!("{}:{} {}", major, minor, bps).to_string().as_ref()).ok()
});
}
pub fn throttle_read_iops_for_device(self: &Self, major: u64, minor: u64, iops: u64) {
self.open_path("blkio.throttle.read_iops_device", true).and_then(|mut file| {
file.write_all(format!("{}:{} {}", major, minor, iops).to_string().as_ref()).ok()
});
}
pub fn throttle_write_bps_for_device(self: &Self, major: u64, minor: u64, bps: u64) {
self.open_path("blkio.throttle.write_bps_device", true).and_then(|mut file| {
file.write_all(format!("{}:{} {}", major, minor, bps).to_string().as_ref()).ok()
});
}
pub fn throttle_write_iops_for_device(self: &Self, major: u64, minor: u64, iops: u64) {
self.open_path("blkio.throttle.write_iops_device", true).and_then(|mut file| {
file.write_all(format!("{}:{} {}", major, minor, iops).to_string().as_ref()).ok()
});
}
pub fn set_weight(self: &Self, w: u64) {
self.open_path("blkio.leaf_weight", true).and_then(|mut file| {
file.write_all(w.to_string().as_ref()).ok()
});
}
pub fn set_weight_for_device(self: &Self, d: String) {
self.open_path("blkio.weight_device", true).and_then(|mut file| {
file.write_all(d.as_ref()).ok()
});
}
}

83
src/cgroup.rs Normal file
View File

@@ -0,0 +1,83 @@
use {CgroupPid, Resources, ControllIdentifier, Controller, Hierarchy, Subsystem};
use std::convert::From;
/* Describe a cgroup in a simple fashion */
pub struct Cgroup {
/// Name of the cgroup
//name: String,
/// The list of subsystems that control this cgroup
subsystems: Vec<Subsystem>,
}
impl Cgroup {
fn create(self: &Self) {
for subsystem in &self.subsystems {
subsystem.to_controller().create();
}
}
pub fn new(hier: &Hierarchy, path: String, _resources: i64) -> Cgroup {
let mut subsystems = hier.subsystems();
subsystems = subsystems.into_iter().map(|x| x.enter(&path)).collect::<Vec<_>>();
let cg = Cgroup {
//name: path,
subsystems: subsystems,
};
cg.create();
cg
}
pub fn subsystems(self: &Self) -> &Vec<Subsystem> {
&self.subsystems
}
pub fn delete(self: Self) {
self.subsystems.into_iter().for_each(|sub| {
match sub {
Subsystem::Pid(pidc) => pidc.delete(),
Subsystem::Mem(c) => c.delete(),
Subsystem::CpuSet(c) => c.delete(),
Subsystem::CpuAcct(c) => c.delete(),
Subsystem::Cpu(c) => c.delete(),
Subsystem::Devices(c) => c.delete(),
Subsystem::Freezer(c) => c.delete(),
Subsystem::NetCls(c) => c.delete(),
Subsystem::BlkIo(c) => c.delete(),
Subsystem::PerfEvent(c) => c.delete(),
Subsystem::NetPrio(c) => c.delete(),
Subsystem::HugeTlb(c) => c.delete(),
Subsystem::Rdma(c) => c.delete(),
}
});
}
pub fn apply(self: &Self, res: &Resources) {
for subsystem in &self.subsystems {
subsystem.to_controller().apply(res);
}
}
pub fn controller_of<'a, T>(self: &'a Self) -> Option<&'a T>
where &'a T: From<&'a Subsystem>,
T: Controller + ControllIdentifier,
{
for i in &self.subsystems {
if i.to_controller().control_type() == T::controller_type() {
/*
* N.B.:
* https://play.rust-lang.org/?gist=978b2846bacebdaa00be62374f4f4334&version=stable&mode=debug&edition=2015
*/
return Some(i.into());
}
}
None
}
pub fn add_task(self: &Self, pid: CgroupPid) {
self.subsystems().iter().for_each(|sub| sub.to_controller().add_task(&pid));
}
}

94
src/cpu.rs Normal file
View File

@@ -0,0 +1,94 @@
/* CPU controller */
use std::path::PathBuf;
use std::io::{Read, Write};
use {CpuResources, Controllers, Controller, Resources, ControllIdentifier, Subsystem};
#[derive(Debug, Clone)]
pub struct CpuController{
base: PathBuf,
path: PathBuf,
}
#[derive(Debug)]
pub struct Cpu {
pub stat: String,
}
impl Controller for CpuController {
fn control_type(self: &Self) -> Controllers { Controllers::Cpu}
fn get_path<'a>(self: &'a Self) -> &'a PathBuf { &self.path }
fn get_path_mut<'a>(self: &'a mut Self) -> &'a mut PathBuf { &mut self.path }
fn get_base<'a>(self: &'a Self) -> &'a PathBuf { &self.base }
fn apply(self: &Self, res: &Resources) {
/* get the resources that apply to this controller */
let res: &CpuResources = &res.cpu;
if res.update_values {
/* apply pid_max */
self.set_shares(res.shares);
self.set_cfs_period(res.period);
self.set_cfs_quota(res.quota as u64);
/* TODO: rt properties (CONFIG_RT_GROUP_SCHED) are not yet supported */
}
}
}
impl ControllIdentifier for CpuController {
fn controller_type() -> Controllers {
Controllers::Cpu
}
}
impl<'a> From<&'a Subsystem> for &'a CpuController {
fn from(sub: &'a Subsystem) -> &'a CpuController {
unsafe {
match sub {
Subsystem::Cpu(c) => c,
_ => {
assert_eq!(1, 0);
::std::mem::uninitialized()
},
}
}
}
}
impl CpuController {
pub fn new(oroot: PathBuf) -> Self {
let mut root = oroot;
root.push(Self::controller_type().to_string());
Self {
base: root.clone(),
path: root,
}
}
pub fn cpu(self: &Self) -> Cpu {
Cpu {
stat: self.open_path("cpu.stat", false).and_then(|mut file| {
let mut s = String::new();
let _ = file.read_to_string(&mut s);
Some(s)
}).unwrap_or("".to_string()),
}
}
pub fn set_shares(self: &Self, shares: u64) {
self.open_path("cpu.shares", true).and_then(|mut file| {
file.write_all(shares.to_string().as_ref()).ok()
});
}
pub fn set_cfs_period(self: &Self, us: u64) {
self.open_path("cpu.cfs_period_us", true).and_then(|mut file| {
file.write_all(us.to_string().as_ref()).ok()
});
}
pub fn set_cfs_quota(self: &Self, us: u64) {
self.open_path("cpu.cfs_quota_us", true).and_then(|mut file| {
file.write_all(us.to_string().as_ref()).ok()
});
}
}

118
src/cpuacct.rs Normal file
View File

@@ -0,0 +1,118 @@
/* cpuacct controller */
use std::path::PathBuf;
use std::io::{Read, Write};
use std::fs::File;
use {Controllers, Resources, Subsystem, ControllIdentifier, Controller};
#[derive(Debug, Clone)]
pub struct CpuAcctController {
base: PathBuf,
path: PathBuf,
}
pub struct CpuAcct {
pub stat: String,
pub usage: u64,
pub usage_all: String,
pub usage_percpu: String,
pub usage_percpu_sys: String,
pub usage_percpu_user: String,
pub usage_sys: u64,
pub usage_user: u64,
}
impl Controller for CpuAcctController {
fn control_type(self: &Self) -> Controllers { Controllers::CpuAcct }
fn get_path<'a>(self: &'a Self) -> &'a PathBuf { &self.path }
fn get_path_mut<'a>(self: &'a mut Self) -> &'a mut PathBuf { &mut self.path }
fn get_base<'a>(self: &'a Self) -> &'a PathBuf { &self.base }
fn apply(self: &Self, _res: &Resources) {
}
}
impl ControllIdentifier for CpuAcctController {
fn controller_type() -> Controllers {
Controllers::CpuAcct
}
}
impl<'a> From<&'a Subsystem> for &'a CpuAcctController {
fn from(sub: &'a Subsystem) -> &'a CpuAcctController {
unsafe {
match sub {
Subsystem::CpuAcct(c) => c,
_ => {
assert_eq!(1, 0);
::std::mem::uninitialized()
},
}
}
}
}
fn read_u64_from(mut file: File) -> Option<u64> {
let mut string = String::new();
let _ = file.read_to_string(&mut string);
string.trim().parse().ok()
}
impl CpuAcctController {
pub fn new(oroot: PathBuf) -> Self {
let mut root = oroot;
root.push(Self::controller_type().to_string());
Self {
base: root.clone(),
path: root,
}
}
pub fn cpuacct(self: &Self) -> CpuAcct {
CpuAcct {
stat: self.open_path("cpuacct.stat", false)
.and_then(|mut file| {
let mut string = String::new();
let _ = file.read_to_string(&mut string);
Some(string.trim().to_string())
}).unwrap_or("".to_string()),
usage: self.open_path("cpuacct.usage", false)
.and_then(|file| read_u64_from(file))
.unwrap_or(0),
usage_all: self.open_path("cpuacct.usage_all", false)
.and_then(|mut file| {
let mut string = String::new();
let _ = file.read_to_string(&mut string);
Some(string.trim().to_string())
}).unwrap_or("".to_string()),
usage_percpu: self.open_path("cpuacct.usage_percpu", false)
.and_then(|mut file| {
let mut string = String::new();
let _ = file.read_to_string(&mut string);
Some(string.trim().to_string())
}).unwrap_or("".to_string()),
usage_percpu_sys: self.open_path("cpuacct.usage_percpu_sys", false)
.and_then(|mut file| {
let mut string = String::new();
let _ = file.read_to_string(&mut string);
Some(string.trim().to_string())
}).unwrap_or("".to_string()),
usage_percpu_user: self.open_path("cpuacct.usage_percpu_user", false)
.and_then(|mut file| {
let mut string = String::new();
let _ = file.read_to_string(&mut string);
Some(string.trim().to_string())
}).unwrap_or("".to_string()),
usage_sys: self.open_path("cpuacct.usage_sys", false)
.and_then(|file| read_u64_from(file))
.unwrap_or(0),
usage_user: self.open_path("cpuacct.usage_user", false)
.and_then(|file| read_u64_from(file))
.unwrap_or(0),
}
}
pub fn reset(self: &Self) {
self.open_path("cpuacct.usage", true).and_then(|mut file| {
file.write_all(b"0").ok()
});
}
}

267
src/cpuset.rs Normal file
View File

@@ -0,0 +1,267 @@
/* cpuset controller */
use std::path::PathBuf;
use std::io::{Read, Write};
use std::fs::File;
use {CpuResources, Resources, Controller, ControllIdentifier, Subsystem, Controllers};
#[derive(Debug, Clone)]
pub struct CpuSetController {
base: PathBuf,
path: PathBuf,
}
pub struct CpuSet {
pub cpu_exclusive: bool,
pub cpus: String,
pub effective_cpus: String,
pub effective_mems: String,
pub mem_exclusive: bool,
pub mem_hardwall: bool,
pub memory_migrate: bool,
pub memory_pressure: u64,
pub memory_pressure_enabled: Option<bool>,
pub memory_spread_page: bool,
pub memory_spread_slab: bool,
pub mems: String,
pub sched_load_balance: bool,
pub sched_relax_domain_level: u64,
}
impl Controller for CpuSetController {
fn control_type(self: &Self) -> Controllers { Controllers::CpuSet }
fn get_path<'a>(self: &'a Self) -> &'a PathBuf { &self.path }
fn get_path_mut<'a>(self: &'a mut Self) -> &'a mut PathBuf { &mut self.path }
fn get_base<'a>(self: &'a Self) -> &'a PathBuf { &self.base }
fn apply(self: &Self, res: &Resources) {
/* get the resources that apply to this controller */
let res: &CpuResources = &res.cpu;
if res.update_values {
/* apply pid_max */
self.set_cpus(&res.cpus);
self.set_mems(&res.mems);
}
}
}
impl ControllIdentifier for CpuSetController {
fn controller_type() -> Controllers {
Controllers::CpuSet
}
}
impl<'a> From<&'a Subsystem> for &'a CpuSetController {
fn from(sub: &'a Subsystem) -> &'a CpuSetController {
unsafe {
match sub {
Subsystem::CpuSet(c) => c,
_ => {
assert_eq!(1, 0);
::std::mem::uninitialized()
},
}
}
}
}
fn read_u64_from(mut file: File) -> Option<u64> {
let mut string = String::new();
let _ = file.read_to_string(&mut string);
string.trim().parse().ok()
}
impl CpuSetController {
pub fn new(oroot: PathBuf) -> Self {
let mut root = oroot;
root.push(Self::controller_type().to_string());
Self {
base: root.clone(),
path: root,
}
}
pub fn cpuset(self: &Self) -> CpuSet {
CpuSet {
cpu_exclusive: {
self.open_path("cpuset.cpu_exclusive", false).and_then(|file| {
read_u64_from(file)
}).map(|x| x == 1).unwrap_or(false)
},
cpus: {
self.open_path("cpuset.cpus", false).and_then(|mut file| {
let mut string = String::new();
let _ = file.read_to_string(&mut string);
Some(string.trim().to_string())
}).unwrap()
},
effective_cpus: {
self.open_path("cpuset.effective_cpus", false).and_then(|mut file| {
let mut string = String::new();
let _ = file.read_to_string(&mut string);
Some(string.trim().to_string())
}).unwrap()
},
effective_mems: {
self.open_path("cpuset.effective_mems", false).and_then(|mut file| {
let mut string = String::new();
let _ = file.read_to_string(&mut string);
Some(string.trim().to_string())
}).unwrap()
},
mem_exclusive: {
self.open_path("cpuset.mem_exclusive", false).and_then(|file| {
read_u64_from(file)
}).map(|x| x == 1).unwrap_or(false)
},
mem_hardwall: {
self.open_path("cpuset.mem_hardwall", false).and_then(|file| {
read_u64_from(file)
}).map(|x| x == 1).unwrap_or(false)
},
memory_migrate: {
self.open_path("cpuset.memory_migrate", false).and_then(|file| {
read_u64_from(file)
}).map(|x| x == 1).unwrap_or(false)
},
memory_pressure: {
self.open_path("cpuset.memory_pressure", false).and_then(|file| {
read_u64_from(file)
}).unwrap_or(0)
},
memory_pressure_enabled: {
self.open_path("cpuset.memory_pressure_enabled", false).and_then(|file| {
read_u64_from(file)
}).map(|x| x == 1)
},
memory_spread_page: {
self.open_path("cpuset.memory_spread_page", false).and_then(|file| {
read_u64_from(file)
}).map(|x| x == 1).unwrap_or(false)
},
memory_spread_slab: {
self.open_path("cpuset.memory_spread_slab", false).and_then(|file| {
read_u64_from(file)
}).map(|x| x == 1).unwrap_or(false)
},
mems: {
self.open_path("cpuset.mems", false).and_then(|mut file| {
let mut string = String::new();
let _ = file.read_to_string(&mut string);
Some(string.trim().to_string())
}).unwrap()
},
sched_load_balance: {
self.open_path("cpuset.sched_load_balance", false).and_then(|file| {
read_u64_from(file)
}).map(|x| x == 1).unwrap_or(false)
},
sched_relax_domain_level: {
self.open_path("cpuset.sched_relax_domain_level", false).and_then(|file| {
read_u64_from(file)
}).unwrap_or(0)
},
}
}
pub fn set_cpu_exclusive(self: &Self, b: bool) {
self.open_path("cpuset.cpu_exclusive", true).and_then(|mut file| {
if b {
file.write_all(b"1").ok()
} else {
file.write_all(b"0").ok()
}
});
}
pub fn set_mem_exclusive(self: &Self, b: bool) {
self.open_path("cpuset.mem_exclusive", true).and_then(|mut file| {
if b {
file.write_all(b"1").ok()
} else {
file.write_all(b"0").ok()
}
});
}
pub fn set_cpus(self: &Self, cpus: &String) {
self.open_path("cpuset.cpus", true).and_then(|mut file| {
file.write_all(cpus.as_ref()).ok()
});
}
pub fn set_mems(self: &Self, mems: &String) {
self.open_path("cpuset.mems", true).and_then(|mut file| {
file.write_all(mems.as_ref()).ok()
});
}
pub fn set_hardwall(self: &Self, b: bool) {
self.open_path("cpuset.mem_hardwall", true).and_then(|mut file| {
if b {
file.write_all(b"1").ok()
} else {
file.write_all(b"0").ok()
}
});
}
pub fn set_load_balancing(self: &Self, b: bool) {
self.open_path("cpuset.sched_load_balance", true).and_then(|mut file| {
if b {
file.write_all(b"1").ok()
} else {
file.write_all(b"0").ok()
}
});
}
pub fn set_rebalance_relax_domain_level(self: &Self, i: i64) {
self.open_path("cpuset.sched_relax_domain_level", true).and_then(|mut file| {
file.write_all(i.to_string().as_ref()).ok()
});
}
pub fn set_memory_migration(self: &Self, b: bool) {
self.open_path("cpuset.memory_migrate", true).and_then(|mut file| {
if b {
file.write_all(b"1").ok()
} else {
file.write_all(b"0").ok()
}
});
}
pub fn set_memory_spread_page(self: &Self, b: bool) {
self.open_path("cpuset.memory_spread_page", true).and_then(|mut file| {
if b {
file.write_all(b"1").ok()
} else {
file.write_all(b"0").ok()
}
});
}
pub fn set_memory_spread_slab(self: &Self, b: bool) {
self.open_path("cpuset.memory_spread_slab", true).and_then(|mut file| {
if b {
file.write_all(b"1").ok()
} else {
file.write_all(b"0").ok()
}
});
}
pub fn set_enable_memory_pressure(self: &Self, b: bool) {
/* XXX: this file should only be present in the root cpuset cg */
self.open_path("cpuset.memory_pressure_enabled", true).and_then(|mut file| {
if b {
file.write_all(b"1").ok()
} else {
file.write_all(b"0").ok()
}
});
}
}

85
src/devices.rs Normal file
View File

@@ -0,0 +1,85 @@
/* Devices controller */
use std::path::PathBuf;
use std::io::{Read, Write};
use {DeviceResources, Controllers, Controller, Resources, ControllIdentifier, Subsystem};
#[derive(Debug, Clone)]
pub struct DevicesController{
base: PathBuf,
path: PathBuf,
}
impl Controller for DevicesController {
fn control_type(self: &Self) -> Controllers { Controllers::Devices }
fn get_path<'a>(self: &'a Self) -> &'a PathBuf { &self.path }
fn get_path_mut<'a>(self: &'a mut Self) -> &'a mut PathBuf { &mut self.path }
fn get_base<'a>(self: &'a Self) -> &'a PathBuf { &self.base }
fn apply(self: &Self, res: &Resources) {
/* get the resources that apply to this controller */
let res: &DeviceResources = &res.devices;
if res.update_values {
for i in &res.devices {
let wstr = format!("{} {}:{} {}",
i.devtype, i.major, i.minor, i.access);
if i.allow {
self.allow_device(&wstr);
} else {
self.deny_device(&wstr);
}
}
}
}
}
impl ControllIdentifier for DevicesController {
fn controller_type() -> Controllers {
Controllers::Devices
}
}
impl<'a> From<&'a Subsystem> for &'a DevicesController {
fn from(sub: &'a Subsystem) -> &'a DevicesController {
unsafe {
match sub {
Subsystem::Devices(c) => c,
_ => {
assert_eq!(1, 0);
::std::mem::uninitialized()
},
}
}
}
}
impl DevicesController {
pub fn new(oroot: PathBuf) -> Self {
let mut root = oroot;
root.push(Self::controller_type().to_string());
Self {
base: root.clone(),
path: root,
}
}
pub fn allow_device(self: &Self, dev: &String) {
self.open_path("devices.allow", true).and_then(|mut file| {
file.write_all(dev.as_ref()).ok()
});
}
pub fn deny_device(self: &Self, dev: &String) {
self.open_path("devices.deny", true).and_then(|mut file| {
file.write_all(dev.as_ref()).ok()
});
}
pub fn allowed_devices(self: &Self) -> String {
self.open_path("devices.list", false).and_then(|mut file| {
let mut s = String::new();
let _ = file.read_to_string(&mut s);
Some(s)
}).unwrap_or("".to_string())
}
}

82
src/freezer.rs Normal file
View File

@@ -0,0 +1,82 @@
/* Freezer controller */
use std::path::PathBuf;
use std::io::{Read, Write};
use {Controllers, Controller, Resources, ControllIdentifier, Subsystem};
#[derive(Debug, Clone)]
pub struct FreezerController{
base: PathBuf,
path: PathBuf,
}
pub enum FreezerState {
Thawed,
Freezing,
Frozen,
}
impl Controller for FreezerController {
fn control_type(self: &Self) -> Controllers { Controllers::Freezer }
fn get_path<'a>(self: &'a Self) -> &'a PathBuf { &self.path }
fn get_path_mut<'a>(self: &'a mut Self) -> &'a mut PathBuf { &mut self.path }
fn get_base<'a>(self: &'a Self) -> &'a PathBuf { &self.base }
fn apply(self: &Self, _res: &Resources) {
}
}
impl ControllIdentifier for FreezerController {
fn controller_type() -> Controllers {
Controllers::Freezer
}
}
impl<'a> From<&'a Subsystem> for &'a FreezerController {
fn from(sub: &'a Subsystem) -> &'a FreezerController {
unsafe {
match sub {
Subsystem::Freezer(c) => c,
_ => {
assert_eq!(1, 0);
::std::mem::uninitialized()
},
}
}
}
}
impl FreezerController {
pub fn new(oroot: PathBuf) -> Self {
let mut root = oroot;
root.push(Self::controller_type().to_string());
Self {
base: root.clone(),
path: root,
}
}
pub fn freeze(self: &Self) {
self.open_path("freezer.state", true).and_then(|mut file| {
file.write_all("FROZEN".to_string().as_ref()).ok()
});
}
pub fn thaw(self: &Self) {
self.open_path("freezer.state", true).and_then(|mut file| {
file.write_all("THAWED".to_string().as_ref()).ok()
});
}
pub fn state(self: &Self) -> FreezerState {
self.open_path("freezer.state", false).and_then(|mut file| {
let mut s = String::new();
let _ = file.read_to_string(&mut s);
match s.as_ref() {
"FROZEN" => Some(FreezerState::Frozen),
"THAWED" => Some(FreezerState::Thawed),
"FREEZING" => Some(FreezerState::Freezing),
_ => None,
}
}).unwrap_or(FreezerState::Thawed)
}
}

130
src/hierarchies.rs Normal file
View File

@@ -0,0 +1,130 @@
use std::io::BufRead;
use std::io::BufReader;
use std::fs::File;
use std::path::{Path, PathBuf};
use {Controllers, Hierarchy, Subsystem};
use ::pid::PidController;
use ::memory::MemController;
use ::cpuset::CpuSetController;
use ::cpuacct::CpuAcctController;
use ::cpu::CpuController;
use ::freezer::FreezerController;
use ::devices::DevicesController;
use ::net_cls::NetClsController;
use ::blkio::BlkIoController;
use ::perf_event::PerfEventController;
use ::net_prio::NetPrioController;
use ::hugetlb::HugeTlbController;
use ::rdma::RdmaController;
pub struct V1 {
mount_point: String,
}
impl Hierarchy for V1 {
fn subsystems(self: &Self) -> Vec<Subsystem> {
let mut subs = vec![];
if self.check_support(Controllers::Pids) {
subs.push(Subsystem::Pid(PidController::new(self.root())));
}
if self.check_support(Controllers::Mem) {
subs.push(Subsystem::Mem(MemController::new(self.root())));
}
if self.check_support(Controllers::CpuSet) {
subs.push(Subsystem::CpuSet(CpuSetController::new(self.root())));
}
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())));
}
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::BlkIo) {
subs.push(Subsystem::BlkIo(BlkIoController::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())));
}
if self.check_support(Controllers::Rdma) {
subs.push(Subsystem::Rdma(RdmaController::new(self.root())));
}
subs
}
fn check_support(self: &Self, sub: Controllers) -> bool {
let root = self.root().read_dir().unwrap();
for entry in root {
if let Ok(entry) = entry {
if entry.file_name().into_string().unwrap() == sub.to_string() {
return true;
}
}
}
return false;
}
fn root(self: &Self) -> PathBuf {
PathBuf::from(self.mount_point.clone())
}
fn can_create_cgroup(self: &Self) -> bool {
/*
* V1 hierarchies do not support creating cgroups,
* they have to be created in a subsystem
*/
false
}
}
impl V1 {
pub fn new() -> Self {
let mount_point = find_v1_mount().unwrap();
V1 {
mount_point: mount_point,
}
}
}
fn find_v1_mount() -> Option<String> {
/* Open mountinfo so we can get a parseable mount list */
let mountinfo_path = Path::new("/proc/self/mountinfo");
/* If /proc isn't mounted, or something else happens, then bail out */
if mountinfo_path.exists() == false {
return None;
}
let mountinfo_file = File::open(mountinfo_path).unwrap();
let mountinfo_reader = BufReader::new(&mountinfo_file);
for _line in mountinfo_reader.lines() {
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 fstype = more_fields[0];
if fstype == "tmpfs" && more_fields[2].contains("ro") {
let cgroups_mount = fields.nth(4).unwrap();
println!("found cgroups at {:?}", cgroups_mount);
return Some(cgroups_mount.to_string());
}
}
None
}

97
src/hugetlb.rs Normal file
View File

@@ -0,0 +1,97 @@
/* Hugetlb controller */
use std::path::PathBuf;
use std::fs::File;
use std::io::{Write, Read};
use {HugePageResources, Controllers, Controller, Resources, ControllIdentifier, Subsystem};
#[derive(Debug, Clone)]
pub struct HugeTlbController {
base: PathBuf,
path: PathBuf,
}
impl Controller for HugeTlbController {
fn control_type(self: &Self) -> Controllers { Controllers::HugeTlb }
fn get_path<'a>(self: &'a Self) -> &'a PathBuf { &self.path }
fn get_path_mut<'a>(self: &'a mut Self) -> &'a mut PathBuf { &mut self.path }
fn get_base<'a>(self: &'a Self) -> &'a PathBuf { &self.base }
fn apply(self: &Self, res: &Resources) {
/* get the resources that apply to this controller */
let res: &HugePageResources = &res.hugepages;
if res.update_values {
for i in &res.limits {
self.set_limit_in_bytes(&i.size, i.limit);
}
}
}
}
impl ControllIdentifier for HugeTlbController {
fn controller_type() -> Controllers {
Controllers::HugeTlb
}
}
impl<'a> From<&'a Subsystem> for &'a HugeTlbController {
fn from(sub: &'a Subsystem) -> &'a HugeTlbController {
unsafe {
match sub {
Subsystem::HugeTlb(c) => c,
_ => {
assert_eq!(1, 0);
::std::mem::uninitialized()
},
}
}
}
}
fn read_u64_from(mut file: File) -> Option<u64> {
let mut string = String::new();
let _ = file.read_to_string(&mut string);
string.trim().parse().ok()
}
impl HugeTlbController {
pub fn new(oroot: PathBuf) -> Self {
let mut root = oroot;
root.push(Self::controller_type().to_string());
Self {
base: root.clone(),
path: root,
}
}
pub fn size_supported(self: &Self, _hugetlb_size: String) -> bool {
/* TODO */
true
}
pub fn failcnt(self: &Self, hugetlb_size: &String) -> Option<u64> {
self.open_path(&format!("hugetlb.{}.failcnt", hugetlb_size), false)
.and_then(read_u64_from)
}
pub fn limit_in_bytes(self: &Self, hugetlb_size: &String) -> Option<u64> {
self.open_path(&format!("hugetlb.{}.limit_in_bytes", hugetlb_size), false)
.and_then(read_u64_from)
}
pub fn usage_in_bytes(self: &Self, hugetlb_size: &String) -> Option<u64> {
self.open_path(&format!("hugetlb.{}.usage_in_bytes", hugetlb_size), false)
.and_then(read_u64_from)
}
pub fn max_usage_in_bytes(self: &Self, hugetlb_size: &String) -> Option<u64> {
self.open_path(&format!("hugetlb.{}.max_usage_in_bytes", hugetlb_size), false)
.and_then(read_u64_from)
}
pub fn set_limit_in_bytes(self: &Self, hugetlb_size: &String, limit: u64) {
self.open_path(&format!("hugetlb.{}.limit_in_bytes", hugetlb_size), false)
.and_then(|mut file| {
file.write_all(limit.to_string().as_ref()).ok()
});
}
}

435
src/lib.rs Normal file
View File

@@ -0,0 +1,435 @@
use std::path::PathBuf;
use std::fs::File;
use std::io::Write;
pub mod hierarchies;
pub mod pid;
pub mod memory;
pub mod cpuset;
pub mod cpuacct;
pub mod cpu;
pub mod devices;
pub mod cgroup;
pub mod freezer;
pub mod net_cls;
pub mod blkio;
pub mod perf_event;
pub mod net_prio;
pub mod hugetlb;
pub mod rdma;
use pid::PidController;
use memory::MemController;
use cpuset::CpuSetController;
use cpuacct::CpuAcctController;
use cpu::CpuController;
use freezer::FreezerController;
use devices::DevicesController;
use net_cls::NetClsController;
use blkio::BlkIoController;
use perf_event::PerfEventController;
use net_prio::NetPrioController;
use hugetlb::HugeTlbController;
use rdma::RdmaController;
#[derive(Debug)]
pub enum Subsystem {
Pid(PidController),
Mem(MemController),
CpuSet(CpuSetController),
CpuAcct(CpuAcctController),
Cpu(CpuController),
Devices(DevicesController),
Freezer(FreezerController),
NetCls(NetClsController),
BlkIo(BlkIoController),
PerfEvent(PerfEventController),
NetPrio(NetPrioController),
HugeTlb(HugeTlbController),
Rdma(RdmaController),
}
#[derive(Eq, PartialEq, Debug)]
pub enum Controllers {
Pids,
Mem,
CpuSet,
CpuAcct,
Cpu,
Devices,
Freezer,
NetCls,
BlkIo,
PerfEvent,
NetPrio,
HugeTlb,
Rdma,
}
impl Controllers {
pub fn to_string(self: &Self) -> String {
match self {
Controllers::Pids => return "pids".to_string(),
Controllers::Mem => return "memory".to_string(),
Controllers::CpuSet => return "cpuset".to_string(),
Controllers::CpuAcct => return "cpuacct".to_string(),
Controllers::Cpu => return "cpu".to_string(),
Controllers::Devices => return "devices".to_string(),
Controllers::Freezer => return "freezer".to_string(),
Controllers::NetCls => return "net_cls".to_string(),
Controllers::BlkIo => return "blkio".to_string(),
Controllers::PerfEvent => return "perf_event".to_string(),
Controllers::NetPrio => return "net_prio".to_string(),
Controllers::HugeTlb => return "hugetlb".to_string(),
Controllers::Rdma => return "rdma".to_string(),
}
}
}
pub trait Controller {
/* actual API */
fn apply(self: &Self, res: &Resources);
/* meta stuff */
fn control_type(self: &Self) -> Controllers;
fn get_path<'a>(self: &'a Self) -> &'a PathBuf;
fn get_path_mut<'a>(self: &'a mut Self) -> &'a mut PathBuf;
fn get_base<'a>(self: &'a Self) -> &'a PathBuf;
fn verify_path(self: &Self) -> bool {
self.get_path().starts_with(self.get_base())
}
fn create(self: &Self) {
if self.verify_path() {
match ::std::fs::create_dir(self.get_path()) {
Ok(_) => (),
Err(e) => println!("error create_dir {:?}", e),
}
}
}
fn exists(self: &Self) -> bool {
self.get_path().exists()
}
fn delete(self: &Self) {
if self.get_path().exists() {
let _ = ::std::fs::remove_dir(self.get_path());
}
}
fn open_path(self: &Self, p: &str, w: bool) -> Option<File> {
let mut path = self.get_path().clone();
path.push(p);
if !self.verify_path() {
return None;
}
if w {
match File::create(&path) {
Err(_) => return None,
Ok(file) => return Some(file),
}
} else {
match File::open(&path) {
Err(_) => return None,
Ok(file) => return Some(file),
}
}
}
fn add_task(self: &Self, pid: &CgroupPid) {
self.open_path("tasks", true).and_then(|mut file| {
file.write_all(pid.pid.to_string().as_ref()).ok()
});
}
}
pub trait ControllIdentifier {
fn controller_type() -> Controllers;
}
pub trait Hierarchy {
fn subsystems(self: &Self) -> Vec<Subsystem>;
fn can_create_cgroup(self: &Self) -> bool;
fn root(self: &Self) -> PathBuf;
fn check_support(self: &Self, sub: Controllers) -> bool;
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct MemoryResources {
pub update_values: bool,
pub kernel_memory_limit: u64,
pub memory_hard_limit: u64,
pub memory_soft_limit: u64,
pub kernel_tcp_memory_limit: u64,
pub memory_swap_limit: u64,
pub swappiness: u64,
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct PidResources {
pub update_values: bool,
pub maximum_number_of_processes: pid::PidMax,
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct CpuResources {
pub update_values: bool,
/* cpuset */
pub cpus: String,
pub mems: String,
/* cpu */
pub shares: u64,
pub quota: i64,
pub period: u64,
pub realtime_runtime: i64,
pub realtime_period: u64,
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct DeviceResource {
pub allow: bool,
pub devtype: String,
pub major: u64,
pub minor: u64,
pub access: String,
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct DeviceResources {
pub update_values: bool,
pub devices: Vec<DeviceResource>,
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct NetworkPriority {
pub name: String,
pub priority: u64,
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct NetworkResources {
pub update_values: bool,
pub class_id: u64,
pub priorities: Vec<NetworkPriority>,
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct HugePageResource {
pub size: String,
pub limit: u64,
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct HugePageResources {
pub update_values: bool,
pub limits: Vec<HugePageResource>,
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct BlkIoDeviceResource {
pub major: u64,
pub minor: u64,
pub weight: u16,
pub leaf_weight: u16,
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct BlkIoDeviceThrottleResource {
pub major: u64,
pub minor: u64,
pub rate: u64,
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct BlkIoResources {
pub update_values: bool,
pub weight: u16,
pub leaf_weight: u16,
pub weight_device: Vec<BlkIoDeviceResource>,
pub throttle_read_bps_device: Vec<BlkIoDeviceThrottleResource>,
pub throttle_read_iops_device: Vec<BlkIoDeviceThrottleResource>,
pub throttle_write_bps_device: Vec<BlkIoDeviceThrottleResource>,
pub throttle_write_iops_device: Vec<BlkIoDeviceThrottleResource>,
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct Resources {
pub memory: MemoryResources,
pub pid: PidResources,
pub cpu: CpuResources,
pub devices: DeviceResources,
pub network: NetworkResources,
pub hugepages: HugePageResources,
pub blkio: BlkIoResources,
}
pub struct CgroupPid {
pub pid: u64,
}
impl From<u64> for CgroupPid {
fn from(u: u64) -> CgroupPid {
CgroupPid {
pid: u,
}
}
}
impl Subsystem {
fn enter(self: Self, path: &String) -> Self {
match self {
Subsystem::Pid(cont) => Subsystem::Pid({
let mut c = cont.clone();
c.get_path_mut().push(path);
c
}),
Subsystem::Mem(cont) => Subsystem::Mem({
let mut c = cont.clone();
c.get_path_mut().push(path);
c
}),
Subsystem::CpuSet(cont) => Subsystem::CpuSet({
let mut c = cont.clone();
c.get_path_mut().push(path);
c
}),
Subsystem::CpuAcct(cont) => Subsystem::CpuAcct({
let mut c = cont.clone();
c.get_path_mut().push(path);
c
}),
Subsystem::Cpu(cont) => Subsystem::Cpu({
let mut c = cont.clone();
c.get_path_mut().push(path);
c
}),
Subsystem::Devices(cont) => Subsystem::Devices({
let mut c = cont.clone();
c.get_path_mut().push(path);
c
}),
Subsystem::Freezer(cont) => Subsystem::Freezer({
let mut c = cont.clone();
c.get_path_mut().push(path);
c
}),
Subsystem::NetCls(cont) => Subsystem::NetCls({
let mut c = cont.clone();
c.get_path_mut().push(path);
c
}),
Subsystem::BlkIo(cont) => Subsystem::BlkIo({
let mut c = cont.clone();
c.get_path_mut().push(path);
c
}),
Subsystem::PerfEvent(cont) => Subsystem::PerfEvent({
let mut c = cont.clone();
c.get_path_mut().push(path);
c
}),
Subsystem::NetPrio(cont) => Subsystem::NetPrio({
let mut c = cont.clone();
c.get_path_mut().push(path);
c
}),
Subsystem::HugeTlb(cont) => Subsystem::HugeTlb({
let mut c = cont.clone();
c.get_path_mut().push(path);
c
}),
Subsystem::Rdma(cont) => Subsystem::Rdma({
let mut c = cont.clone();
c.get_path_mut().push(path);
c
}),
}
}
fn to_controller(self: &Self) -> &dyn Controller {
match self {
Subsystem::Pid(cont) => cont,
Subsystem::Mem(cont) => cont,
Subsystem::CpuSet(cont) => cont,
Subsystem::CpuAcct(cont) => cont,
Subsystem::Cpu(cont) => cont,
Subsystem::Devices(cont) => cont,
Subsystem::Freezer(cont) => cont,
Subsystem::NetCls(cont) => cont,
Subsystem::BlkIo(cont) => cont,
Subsystem::PerfEvent(cont) => cont,
Subsystem::NetPrio(cont) => cont,
Subsystem::HugeTlb(cont) => cont,
Subsystem::Rdma(cont) => cont,
}
}
}
#[cfg(test)]
mod tests {
use {Resources, PidResources, Hierarchy, Controller, Controllers, Subsystem};
use pid::{PidMax, PidController};
use cgroup::Cgroup;
#[test]
fn create_and_delete_cgroup() {
let hier = ::hierarchies::V1::new();
let cg = Cgroup::new(&hier, String::from("ltest2"), 0);
{
let pidcontroller: &PidController = cg.controller_of().unwrap();
pidcontroller.set_pid_max(PidMax::Value(1337));
assert_eq!(pidcontroller.get_pid_max(), Some(PidMax::Value(1337)));
}
cg.delete();
}
#[test]
fn test_pid_pids_current_is_zero() {
let hier = ::hierarchies::V1::new();
let cg = Cgroup::new(&hier, String::from("ltest3"), 0);
{
let pidcontroller: &PidController = cg.controller_of().unwrap();
assert_eq!(pidcontroller.get_pid_current(), 0);
}
cg.delete();
}
#[test]
fn test_pid_pids_events_is_zero() {
let hier = ::hierarchies::V1::new();
let cg = Cgroup::new(&hier, String::from("ltest4"), 0);
{
let pidcontroller: &PidController = cg.controller_of().unwrap();
assert_eq!(pidcontroller.get_pid_events(), 0);
}
cg.delete();
}
#[test]
fn test_setting_resources() {
let hier = ::hierarchies::V1::new();
let cg = Cgroup::new(&hier, String::from("ltest5"), 0);
{
let res = Resources {
pid: PidResources {
update_values: true,
maximum_number_of_processes: PidMax::Value(512),
},
..Default::default()
};
cg.apply(&res);
/* verify */
let pidcontroller: &PidController = cg.controller_of().unwrap();
assert_eq!(pidcontroller.get_pid_max(), Some(PidMax::Value(512)));
}
cg.delete();
}
}

253
src/memory.rs Normal file
View File

@@ -0,0 +1,253 @@
/* Memory controller */
use std::path::PathBuf;
use std::io::{Write, Read};
use std::fs::File;
use {Resources, MemoryResources, Controller, Controllers, Subsystem, ControllIdentifier};
#[derive(Debug, Clone)]
pub struct MemController{
base: PathBuf,
path: PathBuf,
}
#[derive(Debug)]
pub struct MemSwap {
pub fail_cnt: u64,
pub limit_in_bytes: u64,
pub usage_in_bytes: u64,
pub max_usage_in_bytes: u64,
}
#[derive(Debug)]
pub struct Memory {
pub fail_cnt: u64,
pub limit_in_bytes: u64,
pub usage_in_bytes: u64,
pub max_usage_in_bytes: u64,
pub move_charge_at_immigrate: u64,
/* TODO: parse this */
pub numa_stat: String,
/* TODO: parse this */
pub oom_control: String,
pub soft_limit_in_bytes: u64,
/* TODO: parse this */
pub stat: String,
pub swappiness: u64,
pub use_hierarchy: u64,
}
#[derive(Debug)]
pub struct Tcp {
pub fail_cnt: u64,
pub limit_in_bytes: u64,
pub usage_in_bytes: u64,
pub max_usage_in_bytes: u64,
}
#[derive(Debug)]
pub struct Kmem {
pub fail_cnt: u64,
pub limit_in_bytes: u64,
pub usage_in_bytes: u64,
pub max_usage_in_bytes: u64,
pub slabinfo: String,
}
impl Controller for MemController {
fn control_type(self: &Self) -> Controllers { Controllers::Mem }
fn get_path<'a>(self: &'a Self) -> &'a PathBuf { &self.path }
fn get_path_mut<'a>(self: &'a mut Self) -> &'a mut PathBuf { &mut self.path }
fn get_base<'a>(self: &'a Self) -> &'a PathBuf { &self.base }
fn apply(self: &Self, res: &Resources) {
/* get the resources that apply to this controller */
let memres: &MemoryResources = &res.memory;
if memres.update_values {
self.set_limit(memres.memory_hard_limit);
self.set_soft_limit(memres.memory_soft_limit);
self.set_kmem_limit(memres.kernel_memory_limit);
self.set_memswap_limit(memres.memory_swap_limit);
self.set_tcp_limit(memres.kernel_tcp_memory_limit);
self.set_swappiness(memres.swappiness);
}
}
}
impl MemController {
pub fn new(oroot: PathBuf) -> Self {
let mut root = oroot;
root.push(Self::controller_type().to_string());
Self {
base: root.clone(),
path: root,
}
}
pub fn memory_stat(self: &Self) -> Memory {
Memory {
fail_cnt: self.open_path("memory.failcnt", false)
.and_then(|file| read_u64_from(file))
.unwrap_or(0),
limit_in_bytes: self.open_path("memory.limit_in_bytes", false)
.and_then(|file| read_u64_from(file))
.unwrap_or(0),
usage_in_bytes: self.open_path("memory.usage_in_bytes", false)
.and_then(|file| read_u64_from(file))
.unwrap_or(0),
max_usage_in_bytes: self.open_path("memory.max_usage_in_bytes", false)
.and_then(|file| read_u64_from(file))
.unwrap_or(0),
move_charge_at_immigrate: self.open_path("memory.move_charge_at_immigrate", false)
.and_then(|file| read_u64_from(file))
.unwrap_or(0),
numa_stat: self.open_path("memory.numa_stat", false)
.and_then(|mut file| {
let mut string = String::new();
let _ = file.read_to_string(&mut string);
Some(string.trim().to_string())
}).unwrap_or("".to_string()),
oom_control: self.open_path("memory.oom_control", false)
.and_then(|mut file| {
let mut string = String::new();
let _ = file.read_to_string(&mut string);
Some(string.trim().to_string())
}).unwrap_or("".to_string()),
soft_limit_in_bytes: self.open_path("memory.soft_limit_in_bytes", false)
.and_then(|file| read_u64_from(file))
.unwrap_or(0),
stat: self.open_path("memory.stat", false)
.and_then(|mut file| {
let mut string = String::new();
let _ = file.read_to_string(&mut string);
Some(string.trim().to_string())
}).unwrap_or("".to_string()),
swappiness: self.open_path("memory.swappiness", false)
.and_then(|file| read_u64_from(file))
.unwrap_or(0),
use_hierarchy: self.open_path("memory.use_hierarchy", false)
.and_then(|file| read_u64_from(file))
.unwrap_or(0)
}
}
pub fn kmem_stat(self: &Self) -> Kmem {
Kmem {
fail_cnt: self.open_path("memory.kmem.failcnt", false)
.and_then(|file| read_u64_from(file))
.unwrap_or(0),
limit_in_bytes: self.open_path("memory.kmem.limit_in_bytes", false)
.and_then(|file| read_u64_from(file))
.unwrap_or(0),
usage_in_bytes: self.open_path("memory.kmem.usage_in_bytes", false)
.and_then(|file| read_u64_from(file))
.unwrap_or(0),
max_usage_in_bytes: self.open_path("memory.kmem.max_usage_in_bytes", false)
.and_then(|file| read_u64_from(file))
.unwrap_or(0),
slabinfo: self.open_path("memory.kmem.slabinfo", false)
.and_then(|mut file| {
let mut string = String::new();
let _ = file.read_to_string(&mut string);
Some(string.trim().to_string())
}).unwrap_or("".to_string()),
}
}
pub fn kmem_tcp_stat(self: &Self) -> Tcp {
Tcp {
fail_cnt: self.open_path("memory.kmem.tcp.failcnt", false)
.and_then(|file| read_u64_from(file))
.unwrap_or(0),
limit_in_bytes: self.open_path("memory.kmem.tcp.limit_in_bytes", false)
.and_then(|file| read_u64_from(file))
.unwrap_or(0),
usage_in_bytes: self.open_path("memory.kmem.tcp.usage_in_bytes", false)
.and_then(|file| read_u64_from(file))
.unwrap_or(0),
max_usage_in_bytes: self.open_path("memory.kmem.tcp.max_usage_in_bytes", false)
.and_then(|file| read_u64_from(file))
.unwrap_or(0),
}
}
pub fn memswap(self: &Self) -> MemSwap {
MemSwap {
fail_cnt: self.open_path("memory.memsw.failcnt", false)
.and_then(|file| read_u64_from(file))
.unwrap_or(0),
limit_in_bytes: self.open_path("memory.memsw.limit_in_bytes", false)
.and_then(|file| read_u64_from(file))
.unwrap_or(0),
usage_in_bytes: self.open_path("memory.memsw.usage_in_bytes", false)
.and_then(|file| read_u64_from(file))
.unwrap_or(0),
max_usage_in_bytes: self.open_path("memory.memsw.max_usage_in_bytes", false)
.and_then(|file| read_u64_from(file))
.unwrap_or(0),
}
}
pub fn set_limit(self: &Self, limit: u64) {
self.open_path("memory.limit_in_bytes", true).and_then(|mut file| {
file.write_all(limit.to_string().as_ref()).ok()
});
}
pub fn set_kmem_limit(self: &Self, limit: u64) {
self.open_path("memory.kmem.limit_in_bytes", true).and_then(|mut file| {
file.write_all(limit.to_string().as_ref()).ok()
});
}
pub fn set_memswap_limit(self: &Self, limit: u64) {
self.open_path("memory.memsw.limit_in_bytes", true).and_then(|mut file| {
file.write_all(limit.to_string().as_ref()).ok()
});
}
pub fn set_tcp_limit(self: &Self, limit: u64) {
self.open_path("memory.kmem.tcp.limit_in_bytes", true).and_then(|mut file| {
file.write_all(limit.to_string().as_ref()).ok()
});
}
pub fn set_soft_limit(self: &Self, limit: u64) {
self.open_path("memory.soft_limit_in_bytes", true).and_then(|mut file| {
file.write_all(limit.to_string().as_ref()).ok()
});
}
pub fn set_swappiness(self: &Self, swp: u64) {
self.open_path("memory.swappiness", true).and_then(|mut file| {
file.write_all(swp.to_string().as_ref()).ok()
});
}
}
impl ControllIdentifier for MemController {
fn controller_type() -> Controllers {
Controllers::Mem
}
}
impl<'a> From<&'a Subsystem> for &'a MemController {
fn from(sub: &'a Subsystem) -> &'a MemController {
unsafe {
match sub {
Subsystem::Mem(c) => c,
_ => {
assert_eq!(1, 0);
::std::mem::uninitialized()
},
}
}
}
}
fn read_u64_from(mut file: File) -> Option<u64> {
let mut string = String::new();
let _ = file.read_to_string(&mut string);
string.trim().parse().ok()
}

78
src/net_cls.rs Normal file
View File

@@ -0,0 +1,78 @@
/* Network classifier controller */
use std::path::PathBuf;
use std::io::{Read, Write};
use std::fs::File;
use {NetworkResources, Controllers, Controller, Resources, ControllIdentifier, Subsystem};
#[derive(Debug, Clone)]
pub struct NetClsController {
base: PathBuf,
path: PathBuf,
}
impl Controller for NetClsController {
fn control_type(self: &Self) -> Controllers { Controllers::NetCls }
fn get_path<'a>(self: &'a Self) -> &'a PathBuf { &self.path }
fn get_path_mut<'a>(self: &'a mut Self) -> &'a mut PathBuf { &mut self.path }
fn get_base<'a>(self: &'a Self) -> &'a PathBuf { &self.base }
fn apply(self: &Self, res: &Resources) {
/* get the resources that apply to this controller */
let res: &NetworkResources = &res.network;
if res.update_values {
self.set_class(res.class_id);
}
}
}
impl ControllIdentifier for NetClsController {
fn controller_type() -> Controllers {
Controllers::NetCls
}
}
impl<'a> From<&'a Subsystem> for &'a NetClsController {
fn from(sub: &'a Subsystem) -> &'a NetClsController {
unsafe {
match sub {
Subsystem::NetCls(c) => c,
_ => {
assert_eq!(1, 0);
::std::mem::uninitialized()
},
}
}
}
}
fn read_u64_from(mut file: File) -> Option<u64> {
let mut string = String::new();
let _ = file.read_to_string(&mut string);
string.trim().parse().ok()
}
impl NetClsController {
pub fn new(oroot: PathBuf) -> Self {
let mut root = oroot;
root.push(Self::controller_type().to_string());
Self {
base: root.clone(),
path: root,
}
}
pub fn set_class(self: &Self, class: u64) {
self.open_path("net_cls.classid", true).and_then(|mut file| {
let s = format!("{:#08X}", class);
file.write_all(s.as_ref()).ok()
});
}
pub fn get_class(self: &Self) -> u64 {
self.open_path("net_cls.classid", false).and_then(|file| {
read_u64_from(file)
}).unwrap_or(0u64)
}
}

93
src/net_prio.rs Normal file
View File

@@ -0,0 +1,93 @@
/* Network priority controller */
use std::path::PathBuf;
use std::io::{BufReader, BufRead, Write, Read};
use std::fs::File;
use std::collections::HashMap;
use {NetworkResources, Controllers, Controller, Resources, ControllIdentifier, Subsystem};
#[derive(Debug, Clone)]
pub struct NetPrioController {
base: PathBuf,
path: PathBuf,
}
impl Controller for NetPrioController {
fn control_type(self: &Self) -> Controllers { Controllers::NetPrio }
fn get_path<'a>(self: &'a Self) -> &'a PathBuf { &self.path }
fn get_path_mut<'a>(self: &'a mut Self) -> &'a mut PathBuf { &mut self.path }
fn get_base<'a>(self: &'a Self) -> &'a PathBuf { &self.base }
fn apply(self: &Self, res: &Resources) {
/* get the resources that apply to this controller */
let res: &NetworkResources = &res.network;
if res.update_values {
for i in &res.priorities {
self.set_if_prio(&i.name, i.priority);
}
}
}
}
impl ControllIdentifier for NetPrioController {
fn controller_type() -> Controllers {
Controllers::NetPrio
}
}
impl<'a> From<&'a Subsystem> for &'a NetPrioController {
fn from(sub: &'a Subsystem) -> &'a NetPrioController {
unsafe {
match sub {
Subsystem::NetPrio(c) => c,
_ => {
assert_eq!(1, 0);
::std::mem::uninitialized()
},
}
}
}
}
fn read_u64_from(mut file: File) -> Option<u64> {
let mut string = String::new();
let _ = file.read_to_string(&mut string);
string.trim().parse().ok()
}
impl NetPrioController {
pub fn new(oroot: PathBuf) -> Self {
let mut root = oroot;
root.push(Self::controller_type().to_string());
Self {
base: root.clone(),
path: root,
}
}
pub fn prio_idx(self: &Self) -> u64 {
self.open_path("net_prio.prioidx", false)
.and_then(read_u64_from)
.unwrap_or(0)
}
pub fn ifpriomap(self: &Self) -> HashMap<String, u64> {
self.open_path("net_prio.ifpriomap", false)
.and_then(|file| {
let bf = BufReader::new(file);
Some(bf.lines().map(|line| {
let l = line.unwrap();
let mut sp = l.split_whitespace();
(sp.nth(0).unwrap().to_string(),
sp.nth(1).unwrap().trim().parse().unwrap())
}).collect())
}).unwrap_or(HashMap::new())
}
pub fn set_if_prio(self: &Self, eif: &String, prio: u64) {
self.open_path("net_prio.ifpriomap", true)
.and_then(|mut file| {
Some(file.write_all(format!("{} {}", eif, prio).as_ref()))
});
}
}

51
src/perf_event.rs Normal file
View File

@@ -0,0 +1,51 @@
/* Perf event controller */
use std::path::PathBuf;
use {Controllers, Controller, Resources, ControllIdentifier, Subsystem};
#[derive(Debug, Clone)]
pub struct PerfEventController {
base: PathBuf,
path: PathBuf,
}
impl Controller for PerfEventController {
fn control_type(self: &Self) -> Controllers { Controllers::PerfEvent }
fn get_path<'a>(self: &'a Self) -> &'a PathBuf { &self.path }
fn get_path_mut<'a>(self: &'a mut Self) -> &'a mut PathBuf { &mut self.path }
fn get_base<'a>(self: &'a Self) -> &'a PathBuf { &self.base }
fn apply(self: &Self, _res: &Resources) {
}
}
impl ControllIdentifier for PerfEventController {
fn controller_type() -> Controllers {
Controllers::PerfEvent
}
}
impl<'a> From<&'a Subsystem> for &'a PerfEventController {
fn from(sub: &'a Subsystem) -> &'a PerfEventController {
unsafe {
match sub {
Subsystem::PerfEvent(c) => c,
_ => {
assert_eq!(1, 0);
::std::mem::uninitialized()
},
}
}
}
}
impl PerfEventController {
pub fn new(oroot: PathBuf) -> Self {
let mut root = oroot;
root.push(Self::controller_type().to_string());
Self {
base: root.clone(),
path: root,
}
}
}

122
src/pid.rs Normal file
View File

@@ -0,0 +1,122 @@
/* PID controller */
use std::path::PathBuf;
use std::io::{Write, Read};
use {Resources, PidResources, Controller, ControllIdentifier, Subsystem, Controllers};
#[derive(Debug, Clone)]
pub struct PidController {
base: PathBuf,
path: PathBuf,
}
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
pub enum PidMax {
Max,
Value(i64),
}
impl Default for PidMax {
fn default() -> Self {
PidMax::Max
}
}
impl Controller for PidController {
fn control_type(self: &Self) -> Controllers { Controllers::Pids }
fn get_path<'a>(self: &'a Self) -> &'a PathBuf { &self.path }
fn get_path_mut<'a>(self: &'a mut Self) -> &'a mut PathBuf { &mut self.path }
fn get_base<'a>(self: &'a Self) -> &'a PathBuf { &self.base }
fn apply(self: &Self, res: &Resources) {
/* get the resources that apply to this controller */
let pidres: &PidResources = &res.pid;
if pidres.update_values {
/* apply pid_max */
self.set_pid_max(pidres.maximum_number_of_processes);
}
}
}
/*impl<'a> ControllIdentifier for &'a PidController {
fn controller_type() -> Controllers {
Controllers::Pids
}
}*/
impl ControllIdentifier for PidController {
fn controller_type() -> Controllers {
Controllers::Pids
}
}
impl<'a> From<&'a Subsystem> for &'a PidController {
fn from(sub: &'a Subsystem) -> &'a PidController {
unsafe {
match sub {
Subsystem::Pid(c) => c,
_ => {
assert_eq!(1, 0);
::std::mem::uninitialized()
},
}
}
}
}
impl PidController {
pub fn supported_at(_path: PathBuf) -> bool {
true
}
pub fn new(oroot: PathBuf) -> Self {
let mut root = oroot;
root.push(Self::controller_type().to_string());
Self {
base: root.clone(),
path: root,
}
}
pub fn get_pid_events(self: &Self) -> i64 {
self.open_path("pids.events", false).and_then(|mut file| {
let mut string = String::new();
let _ = file.read_to_string(&mut string);
Some(string.split_whitespace().nth(1).unwrap().parse().unwrap_or(0))
}).unwrap()
}
pub fn get_pid_current(self: &Self) -> i64 {
self.open_path("pids.current", false).and_then(|mut file| {
let mut string = String::new();
let _ = file.read_to_string(&mut string);
Some(string.trim().parse().unwrap_or(0))
}).unwrap()
}
pub fn get_pid_max(self: &Self) -> Option<PidMax> {
self.open_path("pids.max", false).and_then(|mut file| {
let mut string = String::new();
let _ = file.read_to_string(&mut string);
if string.trim() == "max" {
Some(PidMax::Max)
} else {
Some(PidMax::Value(string.trim().parse().unwrap_or(0)))
}
})
}
pub fn set_pid_max(self: &Self, max_pid: PidMax) {
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(),
};
match file.write_all(string_to_write.as_ref()) {
Ok(_) => (),
Err(e) => println!("error {:?}", e),
}
Some(0i64)
});
}
}

71
src/rdma.rs Normal file
View File

@@ -0,0 +1,71 @@
/* RDMA controller */
use std::path::PathBuf;
use std::io::{Write, Read};
use std::fs::File;
use {Controllers, Controller, Resources, ControllIdentifier, Subsystem};
#[derive(Debug, Clone)]
pub struct RdmaController {
base: PathBuf,
path: PathBuf,
}
impl Controller for RdmaController {
fn control_type(self: &Self) -> Controllers { Controllers::Rdma }
fn get_path<'a>(self: &'a Self) -> &'a PathBuf { &self.path }
fn get_path_mut<'a>(self: &'a mut Self) -> &'a mut PathBuf { &mut self.path }
fn get_base<'a>(self: &'a Self) -> &'a PathBuf { &self.base }
fn apply(self: &Self, _res: &Resources) {
}
}
impl ControllIdentifier for RdmaController {
fn controller_type() -> Controllers {
Controllers::Rdma
}
}
impl<'a> From<&'a Subsystem> for &'a RdmaController {
fn from(sub: &'a Subsystem) -> &'a RdmaController {
unsafe {
match sub {
Subsystem::Rdma(c) => c,
_ => {
assert_eq!(1, 0);
::std::mem::uninitialized()
},
}
}
}
}
fn read_string_from(mut file: File) -> Option<String> {
let mut string = String::new();
let _ = file.read_to_string(&mut string);
Some(string.trim().to_string())
}
impl RdmaController {
pub fn new(oroot: PathBuf) -> Self {
let mut root = oroot;
root.push(Self::controller_type().to_string());
Self {
base: root.clone(),
path: root,
}
}
pub fn current(self: &Self) -> String {
self.open_path("rdma.current", false)
.and_then(read_string_from)
.unwrap_or("".to_string())
}
pub fn set_max(self: &Self, max: &String) {
self.open_path("rdma.max", true)
.and_then(|mut file| {
file.write_all(max.as_ref()).ok()
});
}
}