1
0
Fork 0
mirror of https://github.com/containers/youki synced 2024-05-18 21:46:13 +02:00
youki/cgroups/src/v2/hugetlb.rs

168 lines
5.6 KiB
Rust
Raw Normal View History

2021-08-03 23:07:57 +02:00
use anyhow::{bail, Context, Result};
use std::{collections::HashMap, path::Path};
2021-05-28 22:37:43 +02:00
2021-05-28 20:42:33 +02:00
use super::controller::Controller;
2021-08-01 22:49:31 +02:00
use crate::{
common::{self, ControllerOpt},
2021-08-03 23:07:57 +02:00
stats::{parse_single_value, supported_page_sizes, HugeTlbStats, StatsProvider},
};
2021-08-01 22:49:31 +02:00
2021-09-07 04:47:26 +02:00
use oci_spec::runtime::LinuxHugepageLimit;
2021-05-28 20:42:33 +02:00
pub struct HugeTlb {}
impl Controller for HugeTlb {
fn apply(controller_opt: &ControllerOpt, cgroup_root: &std::path::Path) -> Result<()> {
2021-07-13 00:26:48 +02:00
log::debug!("Apply hugetlb cgroup v2 config");
2021-09-28 00:46:57 +02:00
if let Some(hugepage_limits) = controller_opt.resources.hugepage_limits() {
2021-07-13 00:26:48 +02:00
for hugetlb in hugepage_limits {
2021-08-16 19:45:14 +02:00
Self::apply(cgroup_root, hugetlb)
.context("failed to apply hugetlb resource restrictions")?
2021-07-13 00:26:48 +02:00
}
}
2021-05-28 20:42:33 +02:00
Ok(())
}
2021-05-29 17:15:16 +02:00
}
2021-07-13 00:26:48 +02:00
2021-08-03 23:07:57 +02:00
impl StatsProvider for HugeTlb {
type Stats = HashMap<String, HugeTlbStats>;
fn stats(cgroup_path: &Path) -> Result<Self::Stats> {
let page_sizes = supported_page_sizes()?;
let mut hugetlb_stats = HashMap::with_capacity(page_sizes.len());
for page_size in page_sizes {
hugetlb_stats.insert(
page_size.clone(),
Self::stats_for_page_size(cgroup_path, &page_size)?,
);
}
Ok(hugetlb_stats)
}
}
2021-07-13 00:26:48 +02:00
impl HugeTlb {
fn apply(root_path: &Path, hugetlb: &LinuxHugepageLimit) -> Result<()> {
2021-07-13 01:00:38 +02:00
let page_size: String = hugetlb
.page_size()
2021-07-13 01:00:38 +02:00
.chars()
.take_while(|c| c.is_digit(10))
.collect();
let page_size: u64 = page_size.parse()?;
if !Self::is_power_of_two(page_size) {
bail!("page size must be in the format of 2^(integer)");
2021-07-13 00:26:48 +02:00
}
common::write_cgroup_file(
root_path.join(format!("hugetlb.{}.limit_in_bytes", hugetlb.page_size())),
hugetlb.limit(),
2021-07-13 00:26:48 +02:00
)?;
Ok(())
}
fn is_power_of_two(number: u64) -> bool {
(number != 0) && (number & (number - 1)) == 0
}
2021-08-03 23:07:57 +02:00
fn stats_for_page_size(cgroup_path: &Path, page_size: &str) -> Result<HugeTlbStats> {
let events_file = format!("hugetlb.{}.events", page_size);
let events = common::read_cgroup_file(cgroup_path.join(&events_file))?;
let fail_count: u64 = events
.lines()
.find(|l| l.starts_with("max"))
.map(|l| l[3..].trim().parse())
.transpose()
.with_context(|| format!("failed to parse max value for {}", events_file))?
.unwrap_or_default();
Ok(HugeTlbStats {
usage: parse_single_value(&cgroup_path.join(format!("hugetlb.{}.current", page_size)))?,
fail_count,
..Default::default()
})
}
2021-07-13 00:26:48 +02:00
}
#[cfg(test)]
mod tests {
use super::*;
2021-08-01 22:49:31 +02:00
use crate::test::{create_temp_dir, set_fixture};
use oci_spec::runtime::LinuxHugepageLimitBuilder;
2021-07-13 00:26:48 +02:00
use std::fs::read_to_string;
#[test]
fn test_set_hugetlb() {
let page_file_name = "hugetlb.2MB.limit_in_bytes";
let tmp = create_temp_dir("test_set_hugetlbv2").expect("create temp directory for test");
2021-07-13 00:26:48 +02:00
set_fixture(&tmp, page_file_name, "0").expect("Set fixture for 2 MB page size");
let hugetlb = LinuxHugepageLimitBuilder::default()
.page_size("2MB")
.limit(16384)
.build()
.unwrap();
2021-07-13 00:26:48 +02:00
HugeTlb::apply(&tmp, &hugetlb).expect("apply hugetlb");
let content = read_to_string(tmp.join(page_file_name)).expect("Read hugetlb file content");
assert_eq!(hugetlb.limit().to_string(), content);
2021-07-13 00:26:48 +02:00
}
#[test]
fn test_set_hugetlb_with_invalid_page_size() {
let tmp = create_temp_dir("test_set_hugetlbv2_with_invalid_page_size")
2021-07-13 00:26:48 +02:00
.expect("create temp directory for test");
let hugetlb = LinuxHugepageLimitBuilder::default()
.page_size("3MB")
.limit(16384)
.build()
.unwrap();
2021-07-13 00:26:48 +02:00
let result = HugeTlb::apply(&tmp, &hugetlb);
assert!(
result.is_err(),
"page size that is not a power of two should be an error"
);
}
quickcheck! {
fn property_test_set_hugetlb(hugetlb: LinuxHugepageLimit) -> bool {
let page_file_name = format!("hugetlb.{:?}.limit_in_bytes", hugetlb.page_size());
2021-07-13 01:00:38 +02:00
let tmp = create_temp_dir("property_test_set_hugetlbv2").expect("create temp directory for test");
2021-07-13 00:26:48 +02:00
set_fixture(&tmp, &page_file_name, "0").expect("Set fixture for page size");
let result = HugeTlb::apply(&tmp, &hugetlb);
2021-07-13 01:00:38 +02:00
let page_size: String = hugetlb
.page_size()
2021-07-13 01:00:38 +02:00
.chars()
.take_while(|c| c.is_digit(10))
.collect();
let page_size: u64 = page_size.parse().expect("parse page size");
2021-07-13 00:26:48 +02:00
if HugeTlb::is_power_of_two(page_size) && page_size != 1 {
let content =
read_to_string(tmp.join(page_file_name)).expect("Read hugetlb file content");
hugetlb.limit().to_string() == content
2021-07-13 00:26:48 +02:00
} else {
result.is_err()
}
}
}
2021-08-04 18:03:49 +02:00
#[test]
fn test_stat_hugetbl() {
let tmp = create_temp_dir("test_stat_hugetlb").expect("create temp directory for test");
set_fixture(&tmp, "hugetlb.2MB.current", "1024\n").expect("set hugetlb current");
set_fixture(&tmp, "hugetlb.2MB.events", "max 5\n").expect("set hugetlb events");
let actual = HugeTlb::stats_for_page_size(&tmp, "2MB").expect("get cgroup stats");
let expected = HugeTlbStats {
usage: 1024,
max_usage: 0,
fail_count: 5,
};
assert_eq!(actual, expected);
}
2021-07-13 00:26:48 +02:00
}