1
0
mirror of https://github.com/containers/youki synced 2025-04-30 13:20:17 +02:00

Seperate adding tasks and applying resource restrictions

This commit is contained in:
Furisto 2021-06-26 01:26:24 +02:00
parent ee4e3a6a36
commit b43aae070b
18 changed files with 213 additions and 225 deletions

@ -19,7 +19,8 @@ pub const CGROUP_PROCS: &str = "cgroup.procs";
pub const DEFAULT_CGROUP_ROOT: &str = "/sys/fs/cgroup";
pub trait CgroupManager {
fn apply(&self, linux_resources: &LinuxResources, pid: Pid) -> Result<()>;
fn add_task(&self, pid: Pid) -> Result<()>;
fn apply(&self, linux_resources: &LinuxResources) -> Result<()>;
fn remove(&self) -> Result<()>;
}

@ -1,12 +1,7 @@
use std::{
fs::{self},
path::Path,
};
use std::path::Path;
use crate::cgroups::{
common::{self, CGROUP_PROCS},
v1::Controller,
};
use crate::cgroups::{common, v1::Controller};
use anyhow::Result;
use oci_spec::{LinuxBlockIo, LinuxResources};
const CGROUP_BLKIO_THROTTLE_READ_BPS: &str = "blkio.throttle.read_bps_device";
@ -17,25 +12,19 @@ const CGROUP_BLKIO_THROTTLE_WRITE_IOPS: &str = "blkio.throttle.write_iops_device
pub struct Blkio {}
impl Controller for Blkio {
fn apply(
linux_resources: &LinuxResources,
cgroup_root: &Path,
pid: nix::unistd::Pid,
) -> anyhow::Result<()> {
fn apply(linux_resources: &LinuxResources, cgroup_root: &Path) -> Result<()> {
log::debug!("Apply blkio cgroup config");
fs::create_dir_all(cgroup_root)?;
if let Some(blkio) = &linux_resources.block_io {
Self::apply(cgroup_root, blkio)?;
}
common::write_cgroup_file(cgroup_root.join(CGROUP_PROCS), pid)?;
Ok(())
}
}
impl Blkio {
fn apply(root_path: &Path, blkio: &LinuxBlockIo) -> anyhow::Result<()> {
fn apply(root_path: &Path, blkio: &LinuxBlockIo) -> Result<()> {
for trbd in &blkio.blkio_throttle_read_bps_device {
common::write_cgroup_file_str(
&root_path.join(CGROUP_BLKIO_THROTTLE_READ_BPS),
@ -70,6 +59,8 @@ impl Blkio {
#[cfg(test)]
mod tests {
use std::fs;
use super::*;
use crate::cgroups::test::setup;
use oci_spec::{LinuxBlockIo, LinuxThrottleDevice};

@ -1,10 +1,18 @@
use std::path::Path;
use std::{fs, path::Path};
use anyhow::Result;
use nix::unistd::Pid;
use oci_spec::LinuxResources;
use crate::cgroups::common::{self, CGROUP_PROCS};
pub trait Controller {
fn apply(linux_resources: &LinuxResources, cgroup_root: &Path, pid: Pid) -> Result<()>;
fn add_task(pid: Pid, cgroup_path: &Path) -> Result<()> {
fs::create_dir_all(cgroup_path)?;
common::write_cgroup_file(cgroup_path.join(CGROUP_PROCS), pid)?;
Ok(())
}
fn apply(linux_resources: &LinuxResources, cgroup_root: &Path) -> Result<()>;
}

@ -1,5 +1,6 @@
use std::fmt::Display;
#[derive(Hash, PartialEq, Eq, Debug, Clone)]
pub enum ControllerType {
Cpu,
CpuAcct,

@ -1,10 +1,9 @@
use std::{fs, path::Path};
use std::path::Path;
use anyhow::Result;
use nix::unistd::Pid;
use oci_spec::{LinuxCpu, LinuxResources};
use crate::cgroups::common::{self, CGROUP_PROCS};
use crate::cgroups::common;
use super::Controller;
@ -17,14 +16,13 @@ const CGROUP_CPU_RT_PERIOD: &str = "cpu.rt_period_us";
pub struct Cpu {}
impl Controller for Cpu {
fn apply(linux_resources: &LinuxResources, cgroup_root: &Path, pid: Pid) -> Result<()> {
fn apply(linux_resources: &LinuxResources, cgroup_root: &Path) -> Result<()> {
log::debug!("Apply Cpu cgroup config");
fs::create_dir_all(cgroup_root)?;
if let Some(cpu) = &linux_resources.cpu {
Self::apply(cgroup_root, cpu)?;
}
common::write_cgroup_file(cgroup_root.join(CGROUP_PROCS), pid)?;
Ok(())
}
}

@ -1,37 +1,33 @@
use std::{fs, path::Path};
use std::path::Path;
use anyhow::Result;
use nix::unistd::Pid;
use oci_spec::LinuxResources;
use crate::cgroups::common::{self, CGROUP_PROCS};
use super::Controller;
pub struct CpuAcct {}
impl Controller for CpuAcct {
fn apply(_linux_resources: &LinuxResources, cgroup_path: &Path, pid: Pid) -> Result<()> {
log::debug!("Apply cpuacct cgroup config");
fs::create_dir_all(cgroup_path)?;
common::write_cgroup_file(cgroup_path.join(CGROUP_PROCS), pid)?;
fn apply(_linux_resources: &LinuxResources, _cgroup_path: &Path) -> Result<()> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use std::fs;
use nix::unistd::Pid;
use super::*;
use crate::cgroups::test::setup;
use crate::cgroups::{common::CGROUP_PROCS, test::setup};
#[test]
fn test_apply() {
fn test_add_task() {
let (tmp, procs) = setup("test_cpuacct_apply", CGROUP_PROCS);
let resource = LinuxResources::default();
let pid = Pid::from_raw(1000);
CpuAcct::apply(&resource, &tmp, pid).expect("apply cpuacct");
CpuAcct::add_task(pid, &tmp).expect("apply cpuacct");
let content = fs::read_to_string(&procs)
.unwrap_or_else(|_| panic!("read {} file content", CGROUP_PROCS));

@ -1,8 +1,9 @@
use std::{fs, path::Path};
use anyhow::{bail, Result};
use nix::unistd::Pid;
use nix::unistd;
use oci_spec::{LinuxCpu, LinuxResources};
use unistd::Pid;
use crate::cgroups::common::{self, CGROUP_PROCS};
@ -14,18 +15,23 @@ const CGROUP_CPUSET_MEMS: &str = "cpuset.mems";
pub struct CpuSet {}
impl Controller for CpuSet {
fn apply(linux_resources: &LinuxResources, cgroup_path: &Path, pid: Pid) -> Result<()> {
log::debug!("Apply CpuSet cgroup config");
fn add_task(pid: Pid, cgroup_path: &Path) -> Result<()> {
fs::create_dir_all(cgroup_path)?;
Self::ensure_not_empty(cgroup_path, CGROUP_CPUSET_CPUS)?;
Self::ensure_not_empty(cgroup_path, CGROUP_CPUSET_MEMS)?;
common::write_cgroup_file(cgroup_path.join(CGROUP_PROCS), pid)?;
Ok(())
}
fn apply(linux_resources: &LinuxResources, cgroup_path: &Path) -> Result<()> {
log::debug!("Apply CpuSet cgroup config");
if let Some(cpuset) = &linux_resources.cpu {
Self::apply(cgroup_path, cpuset)?;
}
common::write_cgroup_file(cgroup_path.join(CGROUP_PROCS), pid)?;
Ok(())
}
}

@ -1,18 +1,16 @@
use std::{fs::create_dir_all, path::Path};
use std::path::Path;
use anyhow::Result;
use nix::unistd::Pid;
use crate::cgroups::common::{self, CGROUP_PROCS};
use crate::cgroups::common;
use crate::{cgroups::v1::Controller, rootfs::default_devices};
use oci_spec::{LinuxDeviceCgroup, LinuxDeviceType, LinuxResources};
pub struct Devices {}
impl Controller for Devices {
fn apply(linux_resources: &LinuxResources, cgroup_root: &Path, pid: Pid) -> Result<()> {
fn apply(linux_resources: &LinuxResources, cgroup_root: &Path) -> Result<()> {
log::debug!("Apply Devices cgroup config");
create_dir_all(&cgroup_root)?;
for d in &linux_resources.devices {
Self::apply_device(d, cgroup_root)?;
@ -27,7 +25,6 @@ impl Controller for Devices {
Self::apply_device(&d, &cgroup_root)?;
}
common::write_cgroup_file(cgroup_root.join(CGROUP_PROCS), pid)?;
Ok(())
}
}

@ -6,9 +6,8 @@ use std::{
};
use anyhow::{Result, *};
use nix::unistd::Pid;
use crate::cgroups::common::{self, CGROUP_PROCS};
use crate::cgroups::common;
use crate::cgroups::v1::Controller;
use oci_spec::{FreezerState, LinuxResources};
@ -20,7 +19,7 @@ const FREEZER_STATE_FREEZING: &str = "FREEZING";
pub struct Freezer {}
impl Controller for Freezer {
fn apply(linux_resources: &LinuxResources, cgroup_root: &Path, pid: Pid) -> Result<()> {
fn apply(linux_resources: &LinuxResources, cgroup_root: &Path) -> Result<()> {
log::debug!("Apply Freezer cgroup config");
create_dir_all(&cgroup_root)?;
@ -28,7 +27,6 @@ impl Controller for Freezer {
Self::apply(freezer_state, cgroup_root)?;
}
common::write_cgroup_file(cgroup_root.join(CGROUP_PROCS), pid)?;
Ok(())
}
}
@ -116,8 +114,10 @@ impl Freezer {
#[cfg(test)]
mod tests {
use super::*;
use crate::cgroups::common::CGROUP_PROCS;
use crate::cgroups::test::set_fixture;
use crate::utils::create_temp_dir;
use nix::unistd::Pid;
use oci_spec::FreezerState;
#[test]
@ -160,10 +160,9 @@ mod tests {
}
#[test]
fn test_apply() {
let tmp =
create_temp_dir("test_set_freezer_state").expect("create temp directory for test");
set_fixture(&tmp, CGROUP_FREEZER_STATE, "").expect("Set fixure for freezer state");
fn test_add_and_apply() {
let tmp = create_temp_dir("test_add_task").expect("create temp directory for test");
set_fixture(&tmp, CGROUP_FREEZER_STATE, "").expect("set fixure for freezer state");
set_fixture(&tmp, CGROUP_PROCS, "").expect("set fixture for proc file");
// set Thawed state.
@ -182,13 +181,13 @@ mod tests {
};
let pid = Pid::from_raw(1000);
let _ =
<Freezer as Controller>::apply(&linux_resources, &tmp, pid).expect("freezer apply");
Freezer::add_task(pid, &tmp).expect("freezer add task");
<Freezer as Controller>::apply(&linux_resources, &tmp).expect("freezer apply");
let state_content =
std::fs::read_to_string(tmp.join(CGROUP_FREEZER_STATE)).expect("Read to string");
std::fs::read_to_string(tmp.join(CGROUP_FREEZER_STATE)).expect("read to string");
assert_eq!(FREEZER_STATE_THAWED, state_content);
let pid_content =
std::fs::read_to_string(tmp.join(CGROUP_PROCS)).expect("Read to string");
std::fs::read_to_string(tmp.join(CGROUP_PROCS)).expect("read to string");
assert_eq!(pid_content, "1000");
}
@ -208,13 +207,13 @@ mod tests {
};
let pid = Pid::from_raw(1001);
let _ =
<Freezer as Controller>::apply(&linux_resources, &tmp, pid).expect("freezer apply");
Freezer::add_task(pid, &tmp).expect("freezer add task");
<Freezer as Controller>::apply(&linux_resources, &tmp).expect("freezer apply");
let state_content =
std::fs::read_to_string(tmp.join(CGROUP_FREEZER_STATE)).expect("Read to string");
std::fs::read_to_string(tmp.join(CGROUP_FREEZER_STATE)).expect("read to string");
assert_eq!(FREEZER_STATE_FROZEN, state_content);
let pid_content =
std::fs::read_to_string(tmp.join(CGROUP_PROCS)).expect("Read to string");
std::fs::read_to_string(tmp.join(CGROUP_PROCS)).expect("read to string");
assert_eq!(pid_content, "1001");
}
@ -233,16 +232,16 @@ mod tests {
freezer: Some(FreezerState::Undefined),
};
let old_state_content =
std::fs::read_to_string(tmp.join(CGROUP_FREEZER_STATE)).expect("Read to string");
let pid = Pid::from_raw(1002);
let _ =
<Freezer as Controller>::apply(&linux_resources, &tmp, pid).expect("freezer apply");
let old_state_content =
std::fs::read_to_string(tmp.join(CGROUP_FREEZER_STATE)).expect("read to string");
Freezer::add_task(pid, &tmp).expect("freezer add task");
<Freezer as Controller>::apply(&linux_resources, &tmp).expect("freezer apply");
let state_content =
std::fs::read_to_string(tmp.join(CGROUP_FREEZER_STATE)).expect("Read to string");
std::fs::read_to_string(tmp.join(CGROUP_FREEZER_STATE)).expect("read to string");
assert_eq!(old_state_content, state_content);
let pid_content =
std::fs::read_to_string(tmp.join(CGROUP_PROCS)).expect("Read to string");
std::fs::read_to_string(tmp.join(CGROUP_PROCS)).expect("read to string");
assert_eq!(pid_content, "1002");
}
}

@ -1,36 +1,27 @@
use std::{fs, path::Path};
use std::path::Path;
use anyhow::bail;
use anyhow::{bail, Result};
use regex::Regex;
use crate::cgroups::{
common::{self, CGROUP_PROCS},
v1::Controller,
};
use crate::cgroups::{common, v1::Controller};
use oci_spec::{LinuxHugepageLimit, LinuxResources};
pub struct Hugetlb {}
impl Controller for Hugetlb {
fn apply(
linux_resources: &LinuxResources,
cgroup_root: &std::path::Path,
pid: nix::unistd::Pid,
) -> anyhow::Result<()> {
fn apply(linux_resources: &LinuxResources, cgroup_root: &std::path::Path) -> Result<()> {
log::debug!("Apply Hugetlb cgroup config");
fs::create_dir_all(cgroup_root)?;
for hugetlb in &linux_resources.hugepage_limits {
Self::apply(cgroup_root, hugetlb)?
}
common::write_cgroup_file(cgroup_root.join(CGROUP_PROCS), pid)?;
Ok(())
}
}
impl Hugetlb {
fn apply(root_path: &Path, hugetlb: &LinuxHugepageLimit) -> anyhow::Result<()> {
fn apply(root_path: &Path, hugetlb: &LinuxHugepageLimit) -> Result<()> {
let re = Regex::new(r"(?P<pagesize>[0-9]+)[KMG]B")?;
let caps = re.captures(&hugetlb.page_size);
match caps {
@ -44,8 +35,8 @@ impl Hugetlb {
}
common::write_cgroup_file(
&root_path.join(format!("hugetlb.{}.limit_in_bytes", hugetlb.page_size)),
&hugetlb.limit,
root_path.join(format!("hugetlb.{}.limit_in_bytes", hugetlb.page_size)),
hugetlb.limit,
)?;
Ok(())
}

@ -7,6 +7,7 @@ use nix::unistd::Pid;
use procfs::process::Process;
use super::ControllerType;
use super::{
blkio::Blkio, controller_type::CONTROLLERS, cpu::Cpu, cpuacct::CpuAcct, cpuset::CpuSet,
devices::Devices, freezer::Freezer, hugetlb::Hugetlb, memory::Memory,
@ -19,16 +20,16 @@ use crate::utils;
use crate::{cgroups::common::CgroupManager, utils::PathBufExt};
use oci_spec::LinuxResources;
pub struct Manager {
subsystems: HashMap<String, PathBuf>,
subsystems: HashMap<ControllerType, PathBuf>,
}
impl Manager {
pub fn new(cgroup_path: PathBuf) -> Result<Self> {
let mut subsystems = HashMap::<String, PathBuf>::new();
for subsystem in CONTROLLERS.iter().map(|c| c.to_string()) {
let mut subsystems = HashMap::<ControllerType, PathBuf>::new();
for subsystem in CONTROLLERS {
subsystems.insert(
subsystem.to_owned(),
Self::get_subsystem_path(&cgroup_path, &subsystem)?,
subsystem.clone(),
Self::get_subsystem_path(&cgroup_path, &subsystem.to_string())?,
);
}
@ -58,21 +59,44 @@ impl Manager {
}
impl CgroupManager for Manager {
fn apply(&self, linux_resources: &LinuxResources, pid: Pid) -> Result<()> {
fn add_task(&self, pid: Pid) -> Result<()> {
for subsys in &self.subsystems {
match subsys.0.as_str() {
"cpu" => Cpu::apply(linux_resources, &subsys.1, pid)?,
"cpuacct" => CpuAcct::apply(linux_resources, &subsys.1, pid)?,
"cpuset" => CpuSet::apply(linux_resources, &subsys.1, pid)?,
"devices" => Devices::apply(linux_resources, &subsys.1, pid)?,
"hugetlb" => Hugetlb::apply(linux_resources, &subsys.1, pid)?,
"memory" => Memory::apply(linux_resources, &subsys.1, pid)?,
"pids" => Pids::apply(linux_resources, &subsys.1, pid)?,
"blkio" => Blkio::apply(linux_resources, &subsys.1, pid)?,
"net_prio" => NetworkPriority::apply(linux_resources, &subsys.1, pid)?,
"net_cls" => NetworkClassifier::apply(linux_resources, &subsys.1, pid)?,
"freezer" => Freezer::apply(linux_resources, &subsys.1, pid)?,
_ => unreachable!("every subsystem should have an associated controller"),
match subsys.0 {
ControllerType::Cpu => Cpu::add_task(pid, subsys.1)?,
ControllerType::CpuAcct => CpuAcct::add_task(pid, subsys.1)?,
ControllerType::CpuSet => CpuSet::add_task(pid, subsys.1)?,
ControllerType::Devices => Devices::add_task(pid, subsys.1)?,
ControllerType::HugeTlb => Hugetlb::add_task(pid, subsys.1)?,
ControllerType::Memory => Memory::add_task(pid, subsys.1)?,
ControllerType::Pids => Pids::add_task(pid, subsys.1)?,
ControllerType::Blkio => Blkio::add_task(pid, subsys.1)?,
ControllerType::NetworkPriority => NetworkPriority::add_task(pid, subsys.1)?,
ControllerType::NetworkClassifier => NetworkClassifier::add_task(pid, subsys.1)?,
_ => continue,
}
}
Ok(())
}
fn apply(&self, linux_resources: &LinuxResources) -> Result<()> {
for subsys in &self.subsystems {
match subsys.0 {
ControllerType::Cpu => Cpu::apply(linux_resources, &subsys.1)?,
ControllerType::CpuAcct => CpuAcct::apply(linux_resources, &subsys.1)?,
ControllerType::CpuSet => CpuSet::apply(linux_resources, &subsys.1)?,
ControllerType::Devices => Devices::apply(linux_resources, &subsys.1)?,
ControllerType::HugeTlb => Hugetlb::apply(linux_resources, &subsys.1)?,
ControllerType::Memory => Memory::apply(linux_resources, &subsys.1)?,
ControllerType::Pids => Pids::apply(linux_resources, &subsys.1)?,
ControllerType::Blkio => Blkio::apply(linux_resources, &subsys.1)?,
ControllerType::NetworkPriority => {
NetworkPriority::apply(linux_resources, &subsys.1)?
}
ControllerType::NetworkClassifier => {
NetworkClassifier::apply(linux_resources, &subsys.1)?
}
ControllerType::Freezer => Freezer::apply(linux_resources, &subsys.1)?,
}
}

@ -1,13 +1,10 @@
use std::io::{prelude::*, Write};
use std::{
fs::{create_dir_all, OpenOptions},
path::Path,
};
use std::{fs::OpenOptions, path::Path};
use anyhow::{Result, *};
use nix::{errno::Errno, unistd::Pid};
use nix::errno::Errno;
use crate::cgroups::common::{self, CGROUP_PROCS};
use crate::cgroups::common::{self};
use crate::cgroups::v1::Controller;
use oci_spec::{LinuxMemory, LinuxResources};
@ -25,9 +22,8 @@ const CGROUP_KERNEL_TCP_MEMORY_LIMIT: &str = "memory.kmem.tcp.limit_in_bytes";
pub struct Memory {}
impl Controller for Memory {
fn apply(linux_resources: &LinuxResources, cgroup_root: &Path, pid: Pid) -> Result<()> {
fn apply(linux_resources: &LinuxResources, cgroup_root: &Path) -> Result<()> {
log::debug!("Apply Memory cgroup config");
create_dir_all(&cgroup_root)?;
if let Some(memory) = &linux_resources.memory {
let reservation = memory.reservation.unwrap_or(0);
@ -76,7 +72,6 @@ impl Controller for Memory {
}
}
common::write_cgroup_file(cgroup_root.join(CGROUP_PROCS), pid)?;
Ok(())
}
}
@ -239,6 +234,7 @@ impl Memory {
#[cfg(test)]
mod tests {
use super::*;
use crate::cgroups::common::CGROUP_PROCS;
use crate::cgroups::test::set_fixture;
use crate::utils::create_temp_dir;
use oci_spec::LinuxMemory;
@ -368,8 +364,7 @@ mod tests {
freezer: None,
};
let pid = Pid::from_raw(pid_int);
let result = <Memory as Controller>::apply(&linux_resources, &tmp, pid);
let result = <Memory as Controller>::apply(&linux_resources, &tmp);
if result.is_err() {
if let Some(swappiness) = memory_limits.swappiness {
@ -455,10 +450,6 @@ mod tests {
}
};
// check procs file
let procs_content = std::fs::read_to_string(tmp.join(CGROUP_PROCS)).expect("read procs file");
let procs_check = procs_content == pid.to_string();
// useful for debugging
println!("reservation_check: {:?}", reservation_check);
println!("kernel_check: {:?}", kernel_check);
@ -467,7 +458,7 @@ mod tests {
println!("limit_swap_check: {:?}", limit_swap_check);
// combine all the checks
reservation_check && kernel_check && kernel_tcp_check && swappiness_check && limit_swap_check && procs_check
reservation_check && kernel_check && kernel_tcp_check && swappiness_check && limit_swap_check
}
}
}

@ -1,25 +1,21 @@
use std::{fs::create_dir_all, path::Path};
use std::path::Path;
use anyhow::Result;
use nix::unistd::Pid;
use crate::cgroups::common;
use crate::cgroups::common::CGROUP_PROCS;
use crate::cgroups::v1::Controller;
use oci_spec::{LinuxNetwork, LinuxResources};
pub struct NetworkClassifier {}
impl Controller for NetworkClassifier {
fn apply(linux_resources: &LinuxResources, cgroup_root: &Path, pid: Pid) -> Result<()> {
fn apply(linux_resources: &LinuxResources, cgroup_root: &Path) -> Result<()> {
log::debug!("Apply NetworkClassifier cgroup config");
create_dir_all(&cgroup_root)?;
if let Some(network) = linux_resources.network.as_ref() {
Self::apply(cgroup_root, network)?;
}
common::write_cgroup_file(cgroup_root.join(CGROUP_PROCS), pid)?;
Ok(())
}
}

@ -1,25 +1,21 @@
use std::{fs::create_dir_all, path::Path};
use std::path::Path;
use anyhow::Result;
use nix::unistd::Pid;
use crate::cgroups::common;
use crate::cgroups::common::CGROUP_PROCS;
use crate::cgroups::v1::Controller;
use oci_spec::{LinuxNetwork, LinuxResources};
pub struct NetworkPriority {}
impl Controller for NetworkPriority {
fn apply(linux_resources: &LinuxResources, cgroup_root: &Path, pid: Pid) -> Result<()> {
fn apply(linux_resources: &LinuxResources, cgroup_root: &Path) -> Result<()> {
log::debug!("Apply NetworkPriority cgroup config");
create_dir_all(&cgroup_root)?;
if let Some(network) = linux_resources.network.as_ref() {
Self::apply(cgroup_root, network)?;
}
common::write_cgroup_file(cgroup_root.join(CGROUP_PROCS), pid)?;
Ok(())
}
}

@ -1,14 +1,8 @@
use std::{
fs::{self},
path::Path,
};
use std::path::Path;
use anyhow::Result;
use crate::cgroups::{
common::{self, CGROUP_PROCS},
v1::Controller,
};
use crate::cgroups::{common, v1::Controller};
use oci_spec::{LinuxPids, LinuxResources};
pub struct Pids {}
@ -17,16 +11,13 @@ impl Controller for Pids {
fn apply(
linux_resources: &LinuxResources,
cgroup_root: &std::path::Path,
pid: nix::unistd::Pid,
) -> anyhow::Result<()> {
log::debug!("Apply pids cgroup config");
fs::create_dir_all(cgroup_root)?;
if let Some(pids) = &linux_resources.pids {
Self::apply(cgroup_root, pids)?;
}
common::write_cgroup_file(cgroup_root.join(CGROUP_PROCS), pid)?;
Ok(())
}
}

@ -34,28 +34,31 @@ const CONTROLLER_TYPES: &[ControllerType] = &[
pub struct Manager {
root_path: PathBuf,
cgroup_path: PathBuf,
full_path: PathBuf,
}
impl Manager {
pub fn new(root_path: PathBuf, cgroup_path: PathBuf) -> Result<Self> {
let full_path = root_path.join_absolute_path(&cgroup_path)?;
Ok(Self {
root_path,
cgroup_path,
full_path,
})
}
fn create_unified_cgroup(&self, cgroup_path: &Path, pid: Pid) -> Result<PathBuf> {
let full_path = self.root_path.join_absolute_path(cgroup_path)?;
fn create_unified_cgroup(&self, pid: Pid) -> Result<()> {
let controllers: Vec<String> = self
.get_available_controllers(&self.root_path)?
.into_iter()
.get_available_controllers()?
.iter()
.map(|c| format!("{}{}", "+", c.to_string()))
.collect();
Self::write_controllers(&self.root_path, &controllers)?;
let mut current_path = self.root_path.clone();
let mut components = cgroup_path.components().skip(1).peekable();
let mut components = self.cgroup_path.components().skip(1).peekable();
while let Some(component) = components.next() {
current_path = current_path.join(component);
if !current_path.exists() {
@ -70,15 +73,12 @@ impl Manager {
}
}
common::write_cgroup_file(&full_path.join(CGROUP_PROCS), pid)?;
Ok(full_path)
common::write_cgroup_file(&self.full_path.join(CGROUP_PROCS), pid)?;
Ok(())
}
fn get_available_controllers<P: AsRef<Path>>(
&self,
cgroup_path: P,
) -> Result<Vec<ControllerType>> {
let controllers_path = self.root_path.join(cgroup_path).join(CGROUP_CONTROLLERS);
fn get_available_controllers(&self) -> Result<Vec<ControllerType>> {
let controllers_path = self.root_path.join(CGROUP_CONTROLLERS);
if !controllers_path.exists() {
bail!(
"cannot get available controllers. {:?} does not exist",
@ -112,17 +112,20 @@ impl Manager {
}
impl CgroupManager for Manager {
fn apply(&self, linux_resources: &LinuxResources, pid: Pid) -> Result<()> {
let full_cgroup_path = self.create_unified_cgroup(&self.cgroup_path, pid)?;
fn add_task(&self, pid: Pid) -> Result<()> {
self.create_unified_cgroup(pid)?;
Ok(())
}
fn apply(&self, linux_resources: &LinuxResources) -> Result<()> {
for controller in CONTROLLER_TYPES {
match controller {
ControllerType::Cpu => Cpu::apply(linux_resources, &full_cgroup_path)?,
ControllerType::CpuSet => CpuSet::apply(linux_resources, &full_cgroup_path)?,
ControllerType::HugeTlb => HugeTlb::apply(linux_resources, &&full_cgroup_path)?,
ControllerType::Io => Io::apply(linux_resources, &&full_cgroup_path)?,
ControllerType::Memory => Memory::apply(linux_resources, &full_cgroup_path)?,
ControllerType::Pids => Pids::apply(linux_resources, &&full_cgroup_path)?,
ControllerType::Cpu => Cpu::apply(linux_resources, &self.full_path)?,
ControllerType::CpuSet => CpuSet::apply(linux_resources, &self.full_path)?,
ControllerType::HugeTlb => HugeTlb::apply(linux_resources, &self.full_path)?,
ControllerType::Io => Io::apply(linux_resources, &self.full_path)?,
ControllerType::Memory => Memory::apply(linux_resources, &self.full_path)?,
ControllerType::Pids => Pids::apply(linux_resources, &self.full_path)?,
}
}
@ -130,9 +133,8 @@ impl CgroupManager for Manager {
}
fn remove(&self) -> Result<()> {
let full_path = self.root_path.join_absolute_path(&self.cgroup_path)?;
log::debug!("remove cgroup {:?}", full_path);
fs::remove_dir_all(full_path)?;
log::debug!("remove cgroup {:?}", self.full_path);
fs::remove_dir_all(&self.full_path)?;
Ok(())
}

@ -30,7 +30,8 @@ const CONTROLLER_TYPES: &[ControllerType] = &[
/// SystemDCGroupManager is a driver for managing cgroups via systemd.
pub struct SystemDCGroupManager {
root_path: PathBuf,
cgroups_path: CgroupsPath,
cgroups_path: PathBuf,
full_path: PathBuf,
}
/// Represents the systemd cgroups path:
@ -45,6 +46,19 @@ struct CgroupsPath {
impl SystemDCGroupManager {
pub fn new(root_path: PathBuf, cgroups_path: PathBuf) -> Result<Self> {
// TODO: create the systemd unit using a dbus client.
let cgroups_path = Self::new_cgroups_path(cgroups_path)?;
let cgroups_path = Self::get_cgroups_path(cgroups_path)?;
let full_path = root_path.join_absolute_path(&cgroups_path)?;
Ok(SystemDCGroupManager {
root_path,
cgroups_path,
full_path,
})
}
fn new_cgroups_path(cgroups_path: PathBuf) -> Result<CgroupsPath> {
// cgroups path may never be empty as it is defaulted to `/youki`
// see 'get_cgroup_path' under utils.rs.
// if cgroups_path was provided it should be of the form [slice]:[scope_prefix]:[name],
@ -69,35 +83,27 @@ impl SystemDCGroupManager {
name = parts[2];
}
// TODO: create the systemd unit using a dbus client.
Ok(SystemDCGroupManager {
root_path,
cgroups_path: CgroupsPath {
parent: parent.to_owned(),
scope: scope.to_owned(),
name: name.to_owned(),
},
Ok(CgroupsPath {
parent: parent.to_owned(),
scope: scope.to_owned(),
name: name.to_owned(),
})
}
/// get_unit_name returns the unit (scope) name from the path provided by the user
/// for example: foo:docker:bar returns in '/docker-bar.scope'
fn get_unit_name(&self) -> String {
fn get_unit_name(cgroups_path: CgroupsPath) -> String {
// By default we create a scope unless specified explicitly.
if !self.cgroups_path.name.ends_with(".slice") {
return format!(
"{}-{}.scope",
self.cgroups_path.scope, self.cgroups_path.name
);
if !cgroups_path.name.ends_with(".slice") {
return format!("{}-{}.scope", cgroups_path.scope, cgroups_path.name);
}
self.cgroups_path.name.clone()
cgroups_path.name.clone()
}
// systemd represents slice hierarchy using `-`, so we need to follow suit when
// generating the path of slice. For example, 'test-a-b.slice' becomes
// '/test.slice/test-a.slice/test-a-b.slice'.
fn expand_slice(&self, slice: &str) -> Result<PathBuf> {
fn expand_slice(slice: &str) -> Result<PathBuf> {
let suffix = ".slice";
if slice.len() <= suffix.len() || !slice.ends_with(suffix) {
bail!("invalid slice name: {}", slice);
@ -125,15 +131,15 @@ impl SystemDCGroupManager {
// get_cgroups_path generates a cgroups path from the one provided by the user via cgroupsPath.
// an example of the final path: "/machine.slice/docker-foo.scope"
fn get_cgroups_path(&self) -> Result<PathBuf> {
fn get_cgroups_path(cgroups_path: CgroupsPath) -> Result<PathBuf> {
// the root slice is under 'machine.slice'.
let mut slice = Path::new("/machine.slice").to_path_buf();
// if the user provided a '.slice' (as in a branch of a tree)
// we need to "unpack it".
if !self.cgroups_path.parent.is_empty() {
slice = self.expand_slice(&self.cgroups_path.parent)?;
if !cgroups_path.parent.is_empty() {
slice = Self::expand_slice(&cgroups_path.parent)?;
}
let unit_name = self.get_unit_name();
let unit_name = Self::get_unit_name(cgroups_path);
let cgroups_path = slice.join(unit_name);
Ok(cgroups_path)
}
@ -141,9 +147,7 @@ impl SystemDCGroupManager {
/// create_unified_cgroup verifies sure that *each level* in the downward path from the root cgroup
/// down to the cgroup_path provided by the user is a valid cgroup hierarchy,
/// containing the attached controllers and that it contains the container pid.
fn create_unified_cgroup(&self, pid: Pid) -> Result<PathBuf> {
let cgroups_path = self.get_cgroups_path()?;
let full_path = self.root_path.join_absolute_path(&cgroups_path)?;
fn create_unified_cgroup(&self, pid: Pid) -> Result<()> {
let controllers: Vec<String> = self
.get_available_controllers(&self.root_path)?
.into_iter()
@ -154,7 +158,7 @@ impl SystemDCGroupManager {
Self::write_controllers(&self.root_path, &controllers)?;
let mut current_path = self.root_path.clone();
let mut components = cgroups_path.components().skip(1).peekable();
let mut components = self.cgroups_path.components().skip(1).peekable();
// Verify that *each level* in the downward path from the root cgroup
// down to the cgroup_path provided by the user is a valid cgroup hierarchy.
// containing the attached controllers.
@ -172,8 +176,8 @@ impl SystemDCGroupManager {
}
}
common::write_cgroup_file(full_path.join(CGROUP_PROCS), &pid)?;
Ok(full_path)
common::write_cgroup_file(self.full_path.join(CGROUP_PROCS), &pid)?;
Ok(())
}
fn get_available_controllers<P: AsRef<Path>>(
@ -212,21 +216,25 @@ impl SystemDCGroupManager {
}
impl CgroupManager for SystemDCGroupManager {
fn apply(&self, linux_resources: &LinuxResources, pid: Pid) -> Result<()> {
fn add_task(&self, pid: Pid) -> Result<()> {
// Dont attach any pid to the cgroup if -1 is specified as a pid
if pid.as_raw() == -1 {
return Ok(());
}
let full_cgroup_path = self.create_unified_cgroup(pid)?;
self.create_unified_cgroup(pid)?;
Ok(())
}
fn apply(&self, linux_resources: &LinuxResources) -> Result<()> {
for controller in CONTROLLER_TYPES {
match controller {
ControllerType::Cpu => Cpu::apply(linux_resources, &full_cgroup_path)?,
ControllerType::CpuSet => CpuSet::apply(linux_resources, &full_cgroup_path)?,
ControllerType::HugeTlb => HugeTlb::apply(linux_resources, &&full_cgroup_path)?,
ControllerType::Io => Io::apply(linux_resources, &&full_cgroup_path)?,
ControllerType::Memory => Memory::apply(linux_resources, &full_cgroup_path)?,
ControllerType::Pids => Pids::apply(linux_resources, &&full_cgroup_path)?,
ControllerType::Cpu => Cpu::apply(linux_resources, &self.full_path)?,
ControllerType::CpuSet => CpuSet::apply(linux_resources, &self.full_path)?,
ControllerType::HugeTlb => HugeTlb::apply(linux_resources, &self.full_path)?,
ControllerType::Io => Io::apply(linux_resources, &self.full_path)?,
ControllerType::Memory => Memory::apply(linux_resources, &self.full_path)?,
ControllerType::Pids => Pids::apply(linux_resources, &self.full_path)?,
}
}
@ -244,13 +252,8 @@ mod tests {
#[test]
fn expand_slice_works() -> Result<()> {
let manager = SystemDCGroupManager::new(
PathBuf::from("/sys/fs/cgroup"),
PathBuf::from("test-a-b.slice:docker:foo"),
)?;
assert_eq!(
manager.expand_slice("test-a-b.slice")?,
SystemDCGroupManager::expand_slice("test-a-b.slice")?,
PathBuf::from("/test.slice/test-a.slice/test-a-b.slice"),
);
@ -259,13 +262,12 @@ mod tests {
#[test]
fn get_cgroups_path_works_with_a_complex_slice() -> Result<()> {
let manager = SystemDCGroupManager::new(
PathBuf::from("/sys/fs/cgroup"),
PathBuf::from("test-a-b.slice:docker:foo"),
)?;
let cgroups_path =
SystemDCGroupManager::new_cgroups_path(PathBuf::from("test-a-b.slice:docker:foo"))
.expect("");
assert_eq!(
manager.get_cgroups_path()?,
SystemDCGroupManager::get_cgroups_path(cgroups_path)?,
PathBuf::from("/test.slice/test-a.slice/test-a-b.slice/docker-foo.scope"),
);
@ -274,13 +276,12 @@ mod tests {
#[test]
fn get_cgroups_path_works_with_a_simple_slice() -> Result<()> {
let manager = SystemDCGroupManager::new(
PathBuf::from("/sys/fs/cgroup"),
PathBuf::from("machine.slice:libpod:foo"),
)?;
let cgroups_path =
SystemDCGroupManager::new_cgroups_path(PathBuf::from("machine.slice:libpod:foo"))
.expect("");
assert_eq!(
manager.get_cgroups_path()?,
SystemDCGroupManager::get_cgroups_path(cgroups_path)?,
PathBuf::from("/machine.slice/libpod-foo.scope"),
);
@ -289,13 +290,11 @@ mod tests {
#[test]
fn get_cgroups_path_works_with_scope() -> Result<()> {
let manager = SystemDCGroupManager::new(
PathBuf::from("/sys/fs/cgroup"),
PathBuf::from(":docker:foo"),
)?;
let cgroups_path =
SystemDCGroupManager::new_cgroups_path(PathBuf::from(":docker:foo")).expect("");
assert_eq!(
manager.get_cgroups_path()?,
SystemDCGroupManager::get_cgroups_path(cgroups_path)?,
PathBuf::from("/machine.slice/docker-foo.scope"),
);

@ -69,7 +69,8 @@ pub fn fork_first<P: AsRef<Path>>(
let init_pid = parent.wait_for_child_ready(child)?;
log::debug!("init pid is {:?}", init_pid);
if rootless.is_none() && linux.resources.is_some() {
cmanager.apply(&linux.resources.as_ref().unwrap(), Pid::from_raw(init_pid))?;
cmanager.add_task(Pid::from_raw(init_pid))?;
cmanager.apply(&linux.resources.as_ref().unwrap())?;
}
// update status and pid of the container process