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:
parent
2181e2a896
commit
76bcc3cdc2
@ -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 {}
|
||||
|
||||
|
106
crates/libcgroups/src/systemd/unified.rs
Normal file
106
crates/libcgroups/src/systemd/unified.rs
Normal file
@ -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(())
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user