1
0
mirror of https://github.com/containers/youki synced 2024-11-23 09:21:57 +01:00

Implement systemd unified controller

This commit is contained in:
Furisto 2021-11-15 20:40:57 +01:00
parent 2181e2a896
commit 76bcc3cdc2
7 changed files with 177 additions and 67 deletions

@ -7,9 +7,9 @@ use oci_spec::runtime::LinuxCpu;
use super::controller::Controller;
use crate::common::ControllerOpt;
const CPU_WEIGHT: &str = "CPUWeight";
const CPU_QUOTA: &str = "CPUQuotaPerSecUSec";
const CPU_PERIOD: &str = "CPUQuotaPeriodUSec";
pub const CPU_WEIGHT: &str = "CPUWeight";
pub const CPU_QUOTA: &str = "CPUQuotaPerSecUSec";
pub const CPU_PERIOD: &str = "CPUQuotaPeriodUSec";
pub(crate) struct Cpu {}
@ -36,7 +36,7 @@ impl Cpu {
}
if let Some(mut shares) = cpu.shares() {
shares = Self::convert_shares_to_cgroup2(shares);
shares = convert_shares_to_cgroup2(shares);
if shares != 0 {
properties.insert(CPU_WEIGHT, Box::new(shares));
}
@ -65,14 +65,14 @@ impl Cpu {
fn is_realtime_requested(cpu: &LinuxCpu) -> bool {
cpu.realtime_period().is_some() || cpu.realtime_runtime().is_some()
}
}
fn convert_shares_to_cgroup2(shares: u64) -> u64 {
if shares == 0 {
return 0;
}
1 + ((shares - 2) * 9999) / 262142
pub fn convert_shares_to_cgroup2(shares: u64) -> u64 {
if shares == 0 {
return 0;
}
1 + ((shares - 2) * 9999) / 262142
}
#[cfg(test)]

@ -9,8 +9,8 @@ use crate::common::ControllerOpt;
use super::controller::Controller;
const ALLOWED_CPUS: &str = "AllowedCPUs";
const ALLOWED_NODES: &str = "AllowedMemoryNodes";
pub const ALLOWED_CPUS: &str = "AllowedCPUs";
pub const ALLOWED_NODES: &str = "AllowedMemoryNodes";
pub struct CpuSet {}
@ -36,67 +36,64 @@ impl CpuSet {
systemd_version: u32,
properties: &mut HashMap<&str, Box<dyn RefArg>>,
) -> Result<()> {
if systemd_version < 244 {
bail!(
"systemd version ({}) is too old to support cpuset restrictions",
systemd_version
);
if systemd_version <= 243 {
bail!("setting cpuset restrictions requires systemd version greather than 243");
}
if let Some(cpus) = cpu.cpus() {
let cpu_mask = Self::to_bitmask(cpus).context("could not create bitmask for cpus")?;
let cpu_mask = to_bitmask(cpus).context("could not create bitmask for cpus")?;
properties.insert(ALLOWED_CPUS, Box::new(cpu_mask));
}
if let Some(mems) = cpu.mems() {
let mems_mask =
Self::to_bitmask(mems).context("could not create bitmask for memory nodes")?;
to_bitmask(mems).context("could not create bitmask for memory nodes")?;
properties.insert(ALLOWED_NODES, Box::new(mems_mask));
}
Ok(())
}
}
fn to_bitmask(range: &str) -> Result<Vec<u8>> {
let mut bitset = FixedBitSet::with_capacity(8);
pub fn to_bitmask(range: &str) -> Result<Vec<u8>> {
let mut bitset = FixedBitSet::with_capacity(8);
for cpu_set in range.split_terminator(',') {
let cpu_set = cpu_set.trim();
if cpu_set.is_empty() {
continue;
}
let cpus: Vec<&str> = cpu_set.split('-').map(|s| s.trim()).collect();
if cpus.len() == 1 {
let cpu_index: usize = cpus[0].parse()?;
if cpu_index >= bitset.len() {
bitset.grow(bitset.len() + 8);
}
bitset.set(cpu_index, true);
} else {
let start_index = cpus[0].parse()?;
let end_index = cpus[1].parse()?;
if start_index > end_index {
bail!("invalid cpu range {}", cpu_set);
}
if end_index >= bitset.len() {
bitset.grow(end_index + 1);
}
bitset.set_range(start_index..end_index + 1, true);
}
for cpu_set in range.split_terminator(',') {
let cpu_set = cpu_set.trim();
if cpu_set.is_empty() {
continue;
}
// systemd expects a sequence of bytes with no leading zeros, otherwise the values will not be set
// with no error message
Ok(bitset
.as_slice()
.iter()
.flat_map(|b| b.to_be_bytes())
.skip_while(|b| *b == 0u8)
.collect())
let cpus: Vec<&str> = cpu_set.split('-').map(|s| s.trim()).collect();
if cpus.len() == 1 {
let cpu_index: usize = cpus[0].parse()?;
if cpu_index >= bitset.len() {
bitset.grow(bitset.len() + 8);
}
bitset.set(cpu_index, true);
} else {
let start_index = cpus[0].parse()?;
let end_index = cpus[1].parse()?;
if start_index > end_index {
bail!("invalid cpu range {}", cpu_set);
}
if end_index >= bitset.len() {
bitset.grow(end_index + 1);
}
bitset.set_range(start_index..end_index + 1, true);
}
}
// systemd expects a sequence of bytes with no leading zeros, otherwise the values will not be set
// with no error message
Ok(bitset
.as_slice()
.iter()
.flat_map(|b| b.to_be_bytes())
.skip_while(|b| *b == 0u8)
.collect())
}
#[cfg(test)]
@ -110,7 +107,7 @@ mod tests {
fn to_bitmask_single_value() -> Result<()> {
let cpus = "0"; // 0000 0001
let bitmask = CpuSet::to_bitmask(cpus).context("to bitmask")?;
let bitmask = to_bitmask(cpus).context("to bitmask")?;
assert_eq!(bitmask.len(), 1);
assert_eq!(bitmask[0], 1);
@ -121,7 +118,7 @@ mod tests {
fn to_bitmask_multiple_single_values() -> Result<()> {
let cpus = "0,1,2"; // 0000 0111
let bitmask = CpuSet::to_bitmask(cpus).context("to bitmask")?;
let bitmask = to_bitmask(cpus).context("to bitmask")?;
assert_eq!(bitmask.len(), 1);
assert_eq!(bitmask[0], 7);
@ -132,7 +129,7 @@ mod tests {
fn to_bitmask_range_value() -> Result<()> {
let cpus = "0-2"; // 0000 0111
let bitmask = CpuSet::to_bitmask(cpus).context("to bitmask")?;
let bitmask = to_bitmask(cpus).context("to bitmask")?;
assert_eq!(bitmask.len(), 1);
assert_eq!(bitmask[0], 7);
@ -143,7 +140,7 @@ mod tests {
fn to_bitmask_interchanged_range() -> Result<()> {
let cpus = "2-0";
let result = CpuSet::to_bitmask(cpus).context("to bitmask");
let result = to_bitmask(cpus).context("to bitmask");
assert!(result.is_err());
Ok(())
}
@ -153,7 +150,7 @@ mod tests {
let cpus = vec!["2-", "-2"];
for c in cpus {
let result = CpuSet::to_bitmask(c).context("to bitmask");
let result = to_bitmask(c).context("to bitmask");
assert!(result.is_err());
}
@ -164,7 +161,7 @@ mod tests {
fn to_bitmask_mixed() -> Result<()> {
let cpus = "0,2-4,7,9-10"; // 0000 0110 1001 1101
let bitmask = CpuSet::to_bitmask(cpus).context("to bitmask")?;
let bitmask = to_bitmask(cpus).context("to bitmask")?;
assert_eq!(bitmask.len(), 2);
assert_eq!(bitmask[0], 6);
@ -176,7 +173,7 @@ mod tests {
fn to_bitmask_extra_characters() -> Result<()> {
let cpus = "0, 2- 4,,7 ,,9-10"; // 0000 0110 1001 1101
let bitmask = CpuSet::to_bitmask(cpus).context("to bitmask")?;
let bitmask = to_bitmask(cpus).context("to bitmask")?;
assert_eq!(bitmask.len(), 2);
assert_eq!(bitmask[0], 6);
assert_eq!(bitmask[1], 157);

@ -22,8 +22,11 @@ use super::{
memory::Memory,
pids::Pids,
};
use crate::common::{self, CgroupManager, ControllerOpt, FreezerState, PathBufExt};
use crate::stats::Stats;
use crate::{
common::{self, CgroupManager, ControllerOpt, FreezerState, PathBufExt},
systemd::unified::Unified,
};
const CGROUP_PROCS: &str = "cgroup.procs";
const CGROUP_CONTROLLERS: &str = "cgroup.controllers";
@ -290,6 +293,7 @@ impl CgroupManager for Manager {
};
}
Unified::apply(controller_opt, systemd_version, &mut properties)?;
log::debug!("{:?}", properties);
self.client

@ -8,9 +8,11 @@ use crate::common::ControllerOpt;
use super::controller::Controller;
const MEMORY_LOW: &str = "MemoryLow";
const MEMORY_MAX: &str = "MemoryMax";
const MEMORY_SWAP: &str = "MemorySwapMax";
pub const MEMORY_MIN: &str = "MemoryMin";
pub const MEMORY_LOW: &str = "MemoryLow";
pub const MEMORY_HIGH: &str = "MemoryHigh";
pub const MEMORY_MAX: &str = "MemoryMax";
pub const MEMORY_SWAP: &str = "MemorySwapMax";
pub struct Memory {}

@ -8,6 +8,7 @@ mod dbus;
pub mod manager;
mod memory;
mod pids;
mod unified;
/// Checks if the system was booted with systemd
pub fn booted() -> bool {

@ -8,7 +8,7 @@ use crate::common::ControllerOpt;
use super::controller::Controller;
const TASKS_MAX: &str = "TasksMax";
pub const TASKS_MAX: &str = "TasksMax";
pub struct Pids {}

@ -0,0 +1,106 @@
use anyhow::{bail, Context, Result};
use dbus::arg::RefArg;
use std::collections::HashMap;
use super::{
controller::Controller,
cpu::{self, convert_shares_to_cgroup2},
cpuset::{self, to_bitmask},
memory, pids,
};
use crate::common::ControllerOpt;
pub struct Unified {}
impl Controller for Unified {
fn apply(
options: &ControllerOpt,
systemd_version: u32,
properties: &mut HashMap<&str, Box<dyn RefArg>>,
) -> Result<()> {
if let Some(unified) = options.resources.unified() {
log::debug!("Applying unified resource restrictions");
Self::apply(unified, systemd_version, properties)
.context("failed to apply unified resource restrictions")?;
}
Ok(())
}
}
impl Unified {
fn apply(
unified: &HashMap<String, String>,
systemd_version: u32,
properties: &mut HashMap<&str, Box<dyn RefArg>>,
) -> Result<()> {
for (key, value) in unified {
match key.as_str() {
"cpu.weight" => {
let shares = value
.parse::<u64>()
.with_context(|| format!("failed to parse cpu weight: {}", value))?;
properties.insert(cpu::CPU_WEIGHT, Box::new(convert_shares_to_cgroup2(shares)));
}
"cpu.max" => {
let parts: Vec<&str> = value.split_whitespace().collect();
if parts.is_empty() || parts.len() > 2 {
bail!("invalid format for cpu.max: {}", value);
}
let quota = parts[0]
.parse::<u64>()
.with_context(|| format!("failed to parse cpu quota: {}", parts[0]))?;
properties.insert(cpu::CPU_QUOTA, Box::new(quota));
if parts.len() == 2 {
let period = parts[1].parse::<u64>().with_context(|| {
format!("failed to to parse cpu period: {}", parts[1])
})?;
properties.insert(cpu::CPU_PERIOD, Box::new(period));
}
}
cpuset @ ("cpuset.cpus" | "cpuset.mems") => {
if systemd_version <= 243 {
bail!(
"setting {} requires systemd version greater than 243",
cpuset
);
}
let bitmask = to_bitmask(value)
.with_context(|| format!("invalid value for cpuset.cpus: {}", value))?;
let systemd_cpuset = match cpuset {
"cpuset.cpus" => cpuset::ALLOWED_CPUS,
"cpuset.mems" => cpuset::ALLOWED_NODES,
file_name => unreachable!("{} was not matched", file_name),
};
properties.insert(systemd_cpuset, Box::new(bitmask));
}
memory @ ("memory.min" | "memory.low" | "memory.high" | "memory.max") => {
let value = value
.parse::<u64>()
.with_context(|| format!("failed to parse {}: {}", memory, value))?;
let systemd_memory = match memory {
"memory.min" => memory::MEMORY_MIN,
"memory.low" => memory::MEMORY_LOW,
"memory.high" => memory::MEMORY_HIGH,
"memory.max" => memory::MEMORY_MAX,
file_name => unreachable!("{} was not matched", file_name),
};
properties.insert(systemd_memory, Box::new(value));
}
"pids.max" => {
let pids = value.trim().parse::<i64>()?;
properties.insert(pids::TASKS_MAX, Box::new(pids));
}
unknown => log::warn!("could not apply {}. Unknown property.", unknown),
}
}
Ok(())
}
}