Builder pattern for control groups

Signed-off-by: Levente Kurusa <lkurusa@acm.org>
This commit is contained in:
Levente Kurusa
2018-10-21 17:21:53 +02:00
parent f9ffbe2ba4
commit b4cc91f977
5 changed files with 482 additions and 5 deletions

View File

@@ -279,6 +279,7 @@ impl ControllerInternal for BlkIoController {
for dev in &res.weight_device {
let _ = self.set_weight_for_device(dev.major, dev.minor, dev.weight as u64);
let _ = self.set_leaf_weight_for_device(dev.major, dev.minor, dev.leaf_weight as u64);
}
for dev in &res.throttle_read_bps_device {
@@ -587,14 +588,22 @@ impl BlkIoController {
}
/// Same as `set_leaf_weight()`, but settable per each block device.
pub fn set_leaf_weight_for_device(&self, d: String) -> Result<()> {
pub fn set_leaf_weight_for_device(
&self,
major: u64,
minor: u64,
weight: u64,
) -> Result<()> {
self.open_path("blkio.leaf_weight_device", true)
.and_then(|mut file| file.write_all(d.as_ref()).map_err(|e| Error::with_cause(WriteFailed, e)))
.and_then(|mut file| {
file.write_all(format!("{}:{} {}", major, minor, weight).as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
})
}
/// Reset the statistics the kernel has gathered so far and start fresh.
pub fn reset_stats(&self) -> Result<()> {
self.open_path("blkio.leaf_weight_device", true)
self.open_path("blkio.reset_stats", true)
.and_then(|mut file| {
file.write_all("1".to_string().as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))
@@ -662,7 +671,7 @@ impl BlkIoController {
/// Set the weight of the control group's tasks.
pub fn set_weight(&self, w: u64) -> Result<()> {
self.open_path("blkio.leaf_weight", true)
self.open_path("blkio.weight", true)
.and_then(|mut file| {
file.write_all(w.to_string().as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))

322
src/cgroup_builder.rs Normal file
View File

@@ -0,0 +1,322 @@
//! This module allows the user to create a control group using the Builder pattern.
use error::*;
use {pid, BlkIoDeviceResource, BlkIoDeviceThrottleResource, Cgroup, DeviceResource, Hierarchy, HugePageResource, NetworkPriority, Resources};
// let cgroup: Cgroup = CgroupBuilder::new("hello", V1)
// .memory()
// .kernel_memory_limit(1024 * 1024)
// .memory_hard_limit(1024 * 1024)
// .done()
// .cpu()
// .shares(100)
// .done()
// .devices()
// .device(1000, 10, DeviceType::Block, true,
// vec![Read, Write, MkNod]
// .device(6, 1, DeviceType::Char, false, vec![])
// .done()
// .network()
// .class_id(1337)
// .priority("eth0", 100)
// .priority("wl0", 200)
// .done()
// .hugepages()
// .limit("2M", 0)
// .limit("4M", 4 * 1024 * 1024 * 100)
// .limit("2G", 2 * 1024 * 1024 * 1024)
// .blkio()
// .weight(123)
// .leaf_weight(99)
// .weight_device(6, 1, 100, 55)
// .weight_device(6, 1, 100, 55)
// .throttle_iops()
// .read(6, 1, 10)
// .write(11, 1, 100)
// .throttle_bps()
// .read(6, 1, 10)
// .write(11, 1, 100)
// .done()
// .build();
//
macro_rules! gen_setter {
($res:ident, $name:ident, $ty:ty) => {
pub fn $name(mut self, $name: $ty) -> Self {
self.cgroup.resources.$res.update_values = true;
self.cgroup.resources.$res.$name = $name;
self
}
}
}
/// A control group builder instance:
///
/// # Example
/// Bla bla. TODO.
pub struct CgroupBuilder<'a> {
name: String,
hierarchy: &'a Hierarchy,
/// Internal, unsupported field: use the associated builders instead.
resources: Resources, // XXX: this should not be public.
}
impl<'a> CgroupBuilder<'a> {
/// Start building a control group with the supplied hierarchy and name pair.
///
/// Note that this does not actually create the control group until `build()` is called.
pub fn new(name: &'a str, hierarchy: &'a Hierarchy) -> CgroupBuilder<'a> {
CgroupBuilder {
name: name.to_owned(),
hierarchy: hierarchy,
resources: Resources::default(),
}
}
pub fn memory(self) -> MemoryResourceBuilder<'a> {
MemoryResourceBuilder {
cgroup: self,
}
}
pub fn pid(self) -> PidResourceBuilder<'a> {
PidResourceBuilder {
cgroup: self,
}
}
pub fn cpu(self) -> CpuResourceBuilder<'a> {
CpuResourceBuilder {
cgroup: self,
}
}
pub fn devices(self) -> DeviceResourceBuilder<'a> {
DeviceResourceBuilder {
cgroup: self,
}
}
pub fn network(self) -> NetworkResourceBuilder<'a> {
NetworkResourceBuilder {
cgroup: self,
}
}
pub fn hugepages(self) -> HugepagesResourceBuilder<'a> {
HugepagesResourceBuilder {
cgroup: self,
}
}
pub fn blkio(self) -> BlkIoResourcesBuilder<'a> {
BlkIoResourcesBuilder {
cgroup: self,
throttling_iops: false,
}
}
// Finalize the control group, consuming the builder and creating the control group.
pub fn build(self) -> Cgroup<'a> {
let cg = Cgroup::new(self.hierarchy, self.name);
cg.apply(&self.resources);
cg
}
}
pub struct MemoryResourceBuilder<'a> {
cgroup: CgroupBuilder<'a>,
}
impl<'a> MemoryResourceBuilder<'a> {
gen_setter!(memory, kernel_memory_limit, u64);
gen_setter!(memory, memory_hard_limit, u64);
gen_setter!(memory, memory_soft_limit, u64);
gen_setter!(memory, kernel_tcp_memory_limit, u64);
gen_setter!(memory, memory_swap_limit, u64);
gen_setter!(memory, swappiness, u64);
pub fn done(self) -> CgroupBuilder<'a> {
self.cgroup
}
}
pub struct PidResourceBuilder<'a> {
cgroup: CgroupBuilder<'a>,
}
impl<'a> PidResourceBuilder<'a> {
gen_setter!(pid, maximum_number_of_processes, pid::PidMax);
pub fn done(self) -> CgroupBuilder<'a> {
self.cgroup
}
}
pub struct CpuResourceBuilder<'a> {
cgroup: CgroupBuilder<'a>,
}
impl<'a> CpuResourceBuilder<'a> {
gen_setter!(cpu, cpus, String);
gen_setter!(cpu, mems, String);
gen_setter!(cpu, shares, u64);
gen_setter!(cpu, quota, i64);
gen_setter!(cpu, period, u64);
gen_setter!(cpu, realtime_runtime, i64);
gen_setter!(cpu, realtime_period, u64);
pub fn done(self) -> CgroupBuilder<'a> {
self.cgroup
}
}
pub struct DeviceResourceBuilder<'a> {
cgroup: CgroupBuilder<'a>,
}
impl<'a> DeviceResourceBuilder<'a> {
pub fn device(mut self,
major: i64,
minor: i64,
devtype: ::devices::DeviceType,
allow: bool,
access: Vec<::devices::DevicePermissions>)
-> DeviceResourceBuilder<'a> {
self.cgroup.resources.devices.update_values = true;
self.cgroup.resources.devices.devices.push(DeviceResource {
major,
minor,
devtype,
allow,
access
});
self
}
pub fn done(self) -> CgroupBuilder<'a> {
self.cgroup
}
}
pub struct NetworkResourceBuilder<'a> {
cgroup: CgroupBuilder<'a>,
}
impl<'a> NetworkResourceBuilder<'a> {
gen_setter!(network, class_id, u64);
pub fn priority(mut self, name: String, priority: u64)
-> NetworkResourceBuilder<'a> {
self.cgroup.resources.network.update_values = true;
self.cgroup.resources.network.priorities.push(NetworkPriority {
name,
priority,
});
self
}
pub fn done(self) -> CgroupBuilder<'a> {
self.cgroup
}
}
pub struct HugepagesResourceBuilder<'a> {
cgroup: CgroupBuilder<'a>,
}
impl<'a> HugepagesResourceBuilder<'a> {
pub fn limit(mut self, size: String, limit: u64)
-> HugepagesResourceBuilder<'a> {
self.cgroup.resources.hugepages.update_values = true;
self.cgroup.resources.hugepages.limits.push(HugePageResource {
size,
limit,
});
self
}
pub fn done(self) -> CgroupBuilder<'a> {
self.cgroup
}
}
pub struct BlkIoResourcesBuilder<'a> {
cgroup: CgroupBuilder<'a>,
throttling_iops: bool,
}
impl<'a> BlkIoResourcesBuilder<'a> {
gen_setter!(blkio, weight, u16);
gen_setter!(blkio, leaf_weight, u16);
pub fn weight_device(mut self,
major: u64,
minor: u64,
weight: u16,
leaf_weight: u16)
-> BlkIoResourcesBuilder<'a> {
self.cgroup.resources.blkio.update_values = true;
self.cgroup.resources.blkio.weight_device.push(BlkIoDeviceResource {
major,
minor,
weight,
leaf_weight,
});
self
}
pub fn throttle_iops(mut self) -> BlkIoResourcesBuilder<'a> {
self.throttling_iops = true;
self
}
pub fn throttle_bps(mut self) -> BlkIoResourcesBuilder<'a> {
self.throttling_iops = false;
self
}
pub fn read(mut self, major: u64, minor: u64, rate: u64)
-> BlkIoResourcesBuilder<'a> {
self.cgroup.resources.blkio.update_values = true;
let throttle = BlkIoDeviceThrottleResource {
major,
minor,
rate,
};
if self.throttling_iops {
self.cgroup.resources.blkio.throttle_read_iops_device.push(throttle);
} else {
self.cgroup.resources.blkio.throttle_read_bps_device.push(throttle);
}
self
}
pub fn write(mut self, major: u64, minor: u64, rate: u64)
-> BlkIoResourcesBuilder<'a> {
self.cgroup.resources.blkio.update_values = true;
let throttle = BlkIoDeviceThrottleResource {
major,
minor,
rate,
};
if self.throttling_iops {
self.cgroup.resources.blkio.throttle_write_iops_device.push(throttle);
} else {
self.cgroup.resources.blkio.throttle_write_bps_device.push(throttle);
}
self
}
pub fn done(self) -> CgroupBuilder<'a> {
self.cgroup
}
}

View File

@@ -131,7 +131,7 @@ impl HugeTlbController {
/// Set the limit (in bytes) of how much memory can be backed by hugepages of a certain size
/// (`hugetlb_size`).
pub fn set_limit_in_bytes(&self, hugetlb_size: &String, limit: u64) -> Result<()> {
self.open_path(&format!("hugetlb.{}.limit_in_bytes", hugetlb_size), false)
self.open_path(&format!("hugetlb.{}.limit_in_bytes", hugetlb_size), true)
.and_then(|mut file| {
file.write_all(limit.to_string().as_ref())
.map_err(|e| Error::with_cause(WriteFailed, e))

View File

@@ -21,6 +21,7 @@ pub mod net_prio;
pub mod perf_event;
pub mod pid;
pub mod rdma;
pub mod cgroup_builder;
use blkio::BlkIoController;
use cpu::CpuController;

145
tests/builder.rs Normal file
View File

@@ -0,0 +1,145 @@
//! Some simple tests covering the builder pattern for control groups.
extern crate cgroups;
use cgroups::*;
use cgroups::cpu::*;
use cgroups::devices::*;
use cgroups::pid::*;
use cgroups::memory::*;
use cgroups::net_cls::*;
use cgroups::hugetlb::*;
use cgroups::blkio::*;
use cgroups::cgroup_builder::*;
#[test]
pub fn test_cpu_res_build() {
let v1 = ::hierarchies::V1::new();
let cg: Cgroup = CgroupBuilder::new("test_cpu_res_build", &v1)
.cpu()
.shares(85)
.done()
.build();
{
let cpu: &CpuController = cg.controller_of().unwrap();
assert!(cpu.shares().is_ok());
assert_eq!(cpu.shares().unwrap(), 85);
}
cg.delete();
}
#[test]
pub fn test_memory_res_build() {
let v1 = ::hierarchies::V1::new();
let cg: Cgroup = CgroupBuilder::new("test_memory_res_build", &v1)
.memory()
.kernel_memory_limit(128 * 1024 * 1024)
.swappiness(70)
.memory_hard_limit(1024 * 1024 * 1024)
.done()
.build();
{
let c: &MemController = cg.controller_of().unwrap();
assert_eq!(c.kmem_stat().limit_in_bytes, 128 * 1024 * 1024);
assert_eq!(c.memory_stat().swappiness, 70);
assert_eq!(c.memory_stat().limit_in_bytes, 1024 * 1024 * 1024);
}
cg.delete();
}
#[test]
pub fn test_pid_res_build() {
let v1 = ::hierarchies::V1::new();
let cg: Cgroup = CgroupBuilder::new("test_pid_res_build", &v1)
.pid()
.maximum_number_of_processes(PidMax::Value(123))
.done()
.build();
{
let c: &PidController = cg.controller_of().unwrap();
assert!(c.get_pid_max().is_ok());
assert_eq!(c.get_pid_max().unwrap(), PidMax::Value(123));
}
cg.delete();
}
#[test]
#[ignore] // ignore this test for now, not sure why my kernel doesn't like it
pub fn test_devices_res_build() {
let v1 = ::hierarchies::V1::new();
let cg: Cgroup = CgroupBuilder::new("test_devices_res_build", &v1)
.devices()
.device(1, 6, DeviceType::Char, true,
vec![DevicePermissions::Read])
.done()
.build();
{
let c: &DevicesController = cg.controller_of().unwrap();
assert!(c.allowed_devices().is_ok());
assert_eq!(c.allowed_devices().unwrap(), vec![
DeviceResource {
allow: true,
devtype: DeviceType::Char,
major: 1,
minor: 6,
access: vec![DevicePermissions::Read],
}
]);
}
cg.delete();
}
#[test]
pub fn test_network_res_build() {
let v1 = ::hierarchies::V1::new();
let cg: Cgroup = CgroupBuilder::new("test_network_res_build", &v1)
.network()
.class_id(1337)
.done()
.build();
{
let c: &NetClsController = cg.controller_of().unwrap();
assert!(c.get_class().is_ok());
assert_eq!(c.get_class().unwrap(), 1337);
}
cg.delete();
}
#[test]
pub fn test_hugepages_res_build() {
let v1 = ::hierarchies::V1::new();
let cg: Cgroup = CgroupBuilder::new("test_hugepages_res_build", &v1)
.hugepages()
.limit("2MB".to_string(), 4 * 2 * 1024 * 1024)
.done()
.build();
{
let c: &HugeTlbController = cg.controller_of().unwrap();
assert!(c.limit_in_bytes(&"2MB".to_string()).is_ok());
assert_eq!(c.limit_in_bytes(&"2MB".to_string()).unwrap(), 4 * 2 * 1024 * 1024);
}
cg.delete();
}
#[test]
pub fn test_blkio_res_build() {
let v1 = ::hierarchies::V1::new();
let cg: Cgroup = CgroupBuilder::new("test_blkio_res_build", &v1)
.blkio()
.weight(100)
.done()
.build();
{
let c: &BlkIoController = cg.controller_of().unwrap();
assert_eq!(c.blkio().weight, 100);
}
cg.delete();
}