From 9162c5a6ad1465f0575953a5aea71c32f800c483 Mon Sep 17 00:00:00 2001 From: Levente Kurusa Date: Tue, 28 Aug 2018 14:52:23 +0200 Subject: [PATCH] Initial work Signed-off-by: Levente Kurusa --- .gitignore | 4 + Cargo.toml | 6 + src/blkio.rs | 274 ++++++++++++++++++++++++++++ src/cgroup.rs | 83 +++++++++ src/cpu.rs | 94 ++++++++++ src/cpuacct.rs | 118 ++++++++++++ src/cpuset.rs | 267 ++++++++++++++++++++++++++++ src/devices.rs | 85 +++++++++ src/freezer.rs | 82 +++++++++ src/hierarchies.rs | 130 ++++++++++++++ src/hugetlb.rs | 97 ++++++++++ src/lib.rs | 435 +++++++++++++++++++++++++++++++++++++++++++++ src/memory.rs | 253 ++++++++++++++++++++++++++ src/net_cls.rs | 78 ++++++++ src/net_prio.rs | 93 ++++++++++ src/perf_event.rs | 51 ++++++ src/pid.rs | 122 +++++++++++++ src/rdma.rs | 71 ++++++++ 18 files changed, 2343 insertions(+) create mode 100644 Cargo.toml create mode 100644 src/blkio.rs create mode 100644 src/cgroup.rs create mode 100644 src/cpu.rs create mode 100644 src/cpuacct.rs create mode 100644 src/cpuset.rs create mode 100644 src/devices.rs create mode 100644 src/freezer.rs create mode 100644 src/hierarchies.rs create mode 100644 src/hugetlb.rs create mode 100644 src/lib.rs create mode 100644 src/memory.rs create mode 100644 src/net_cls.rs create mode 100644 src/net_prio.rs create mode 100644 src/perf_event.rs create mode 100644 src/pid.rs create mode 100644 src/rdma.rs diff --git a/.gitignore b/.gitignore index 088ba6b..979d87e 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,7 @@ Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk + +/target +**/*.rs.bk +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..53d59ec --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "cgroups-rs" +version = "0.1.0" +authors = ["Levente Kurusa "] + +[dependencies] diff --git a/src/blkio.rs b/src/blkio.rs new file mode 100644 index 0000000..1a98eea --- /dev/null +++ b/src/blkio.rs @@ -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 { + 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 { + 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() + }); + } +} diff --git a/src/cgroup.rs b/src/cgroup.rs new file mode 100644 index 0000000..5e0aec4 --- /dev/null +++ b/src/cgroup.rs @@ -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, +} + +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::>(); + + let cg = Cgroup { + //name: path, + subsystems: subsystems, + }; + + cg.create(); + cg + } + + pub fn subsystems(self: &Self) -> &Vec { + &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)); + } +} diff --git a/src/cpu.rs b/src/cpu.rs new file mode 100644 index 0000000..4c8d5c3 --- /dev/null +++ b/src/cpu.rs @@ -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() + }); + } +} diff --git a/src/cpuacct.rs b/src/cpuacct.rs new file mode 100644 index 0000000..b91820a --- /dev/null +++ b/src/cpuacct.rs @@ -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 { + 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() + }); + } +} diff --git a/src/cpuset.rs b/src/cpuset.rs new file mode 100644 index 0000000..c58e71f --- /dev/null +++ b/src/cpuset.rs @@ -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, + 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 { + 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() + } + }); + } +} diff --git a/src/devices.rs b/src/devices.rs new file mode 100644 index 0000000..469ff92 --- /dev/null +++ b/src/devices.rs @@ -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()) + } +} diff --git a/src/freezer.rs b/src/freezer.rs new file mode 100644 index 0000000..dffdbe2 --- /dev/null +++ b/src/freezer.rs @@ -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) + } +} diff --git a/src/hierarchies.rs b/src/hierarchies.rs new file mode 100644 index 0000000..86a86d1 --- /dev/null +++ b/src/hierarchies.rs @@ -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 { + 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 { + /* 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::>(); + 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 +} diff --git a/src/hugetlb.rs b/src/hugetlb.rs new file mode 100644 index 0000000..914256d --- /dev/null +++ b/src/hugetlb.rs @@ -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 { + 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 { + 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 { + 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 { + 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 { + 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() + }); + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..557a897 --- /dev/null +++ b/src/lib.rs @@ -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 { + 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; + 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, +} + +#[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, +} + +#[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, +} + +#[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, + pub throttle_read_bps_device: Vec, + pub throttle_read_iops_device: Vec, + pub throttle_write_bps_device: Vec, + pub throttle_write_iops_device: Vec, +} + +#[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 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(); + } + +} diff --git a/src/memory.rs b/src/memory.rs new file mode 100644 index 0000000..13efc24 --- /dev/null +++ b/src/memory.rs @@ -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 { + let mut string = String::new(); + let _ = file.read_to_string(&mut string); + string.trim().parse().ok() +} diff --git a/src/net_cls.rs b/src/net_cls.rs new file mode 100644 index 0000000..db1f7cd --- /dev/null +++ b/src/net_cls.rs @@ -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 { + 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) + } +} diff --git a/src/net_prio.rs b/src/net_prio.rs new file mode 100644 index 0000000..8ab44c4 --- /dev/null +++ b/src/net_prio.rs @@ -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 { + 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 { + 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())) + }); + } +} diff --git a/src/perf_event.rs b/src/perf_event.rs new file mode 100644 index 0000000..26d6e8a --- /dev/null +++ b/src/perf_event.rs @@ -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, + } + } +} diff --git a/src/pid.rs b/src/pid.rs new file mode 100644 index 0000000..3fa3ab8 --- /dev/null +++ b/src/pid.rs @@ -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 { + 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) + }); + } +} diff --git a/src/rdma.rs b/src/rdma.rs new file mode 100644 index 0000000..795c825 --- /dev/null +++ b/src/rdma.rs @@ -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 { + 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() + }); + } +}