2021-08-04 18:16:32 +02:00
|
|
|
use std::{collections::HashMap, path::Path};
|
2021-05-21 23:35:02 +02:00
|
|
|
|
2021-08-16 19:45:14 +02:00
|
|
|
use anyhow::{bail, Context, Result};
|
2021-05-21 23:35:02 +02:00
|
|
|
|
2021-08-01 22:49:31 +02:00
|
|
|
use crate::{
|
2021-09-05 09:55:13 +02:00
|
|
|
common::{self, ControllerOpt},
|
2021-08-03 23:07:57 +02:00
|
|
|
stats::{supported_page_sizes, HugeTlbStats, StatsProvider},
|
2021-07-26 22:13:57 +02:00
|
|
|
};
|
2021-08-01 22:49:31 +02:00
|
|
|
|
|
|
|
use super::Controller;
|
2021-09-05 09:55:13 +02:00
|
|
|
use oci_spec::runtime::LinuxHugepageLimit;
|
2021-05-21 23:35:02 +02:00
|
|
|
|
2021-08-04 18:03:49 +02:00
|
|
|
pub struct HugeTlb {}
|
2021-05-21 23:35:02 +02:00
|
|
|
|
2021-08-04 18:03:49 +02:00
|
|
|
impl Controller for HugeTlb {
|
2021-06-28 20:06:53 +02:00
|
|
|
type Resource = Vec<LinuxHugepageLimit>;
|
|
|
|
|
2021-09-05 09:55:13 +02:00
|
|
|
fn apply(controller_opt: &ControllerOpt, cgroup_root: &std::path::Path) -> Result<()> {
|
2021-05-24 18:52:34 +02:00
|
|
|
log::debug!("Apply Hugetlb cgroup config");
|
2021-05-21 23:35:02 +02:00
|
|
|
|
2021-09-12 09:39:36 +02:00
|
|
|
if let Some(hugepage_limits) = Self::needs_to_handle(controller_opt) {
|
2021-06-28 20:06:53 +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-06-28 20:06:53 +02:00
|
|
|
}
|
2021-05-21 23:35:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2021-06-28 20:06:53 +02:00
|
|
|
|
2021-09-22 15:36:35 +02:00
|
|
|
fn needs_to_handle<'a>(controller_opt: &'a ControllerOpt) -> Option<&'a Self::Resource> {
|
2021-09-27 03:08:56 +02:00
|
|
|
if let Some(hugepage_limits) = controller_opt.resources.hugepage_limits().as_ref() {
|
2021-08-04 13:41:00 +02:00
|
|
|
if !hugepage_limits.is_empty() {
|
2021-09-27 03:08:56 +02:00
|
|
|
return controller_opt.resources.hugepage_limits().as_ref();
|
2021-08-04 13:41:00 +02:00
|
|
|
}
|
2021-06-28 20:06:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
2021-05-21 23:35:02 +02:00
|
|
|
}
|
|
|
|
|
2021-08-04 18:03:49 +02:00
|
|
|
impl StatsProvider for HugeTlb {
|
2021-07-26 22:13:57 +02:00
|
|
|
type Stats = HashMap<String, HugeTlbStats>;
|
|
|
|
|
|
|
|
fn stats(cgroup_path: &Path) -> Result<Self::Stats> {
|
2021-08-03 23:07:57 +02:00
|
|
|
let page_sizes = supported_page_sizes()?;
|
2021-07-26 22:13:57 +02:00
|
|
|
let mut hugetlb_stats = HashMap::with_capacity(page_sizes.len());
|
|
|
|
|
|
|
|
for page_size in &page_sizes {
|
|
|
|
let stats = Self::stats_for_page_size(cgroup_path, page_size)?;
|
|
|
|
hugetlb_stats.insert(page_size.to_owned(), stats);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(hugetlb_stats)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-04 18:03:49 +02:00
|
|
|
impl HugeTlb {
|
2021-06-26 01:26:24 +02:00
|
|
|
fn apply(root_path: &Path, hugetlb: &LinuxHugepageLimit) -> Result<()> {
|
2021-07-06 18:06:16 +02:00
|
|
|
let page_size: String = hugetlb
|
2021-09-27 03:08:56 +02:00
|
|
|
.page_size()
|
2021-07-06 18:06:16 +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-05-21 23:35:02 +02:00
|
|
|
}
|
|
|
|
|
2021-06-01 16:16:16 +02:00
|
|
|
common::write_cgroup_file(
|
2021-09-27 03:08:56 +02:00
|
|
|
root_path.join(format!("hugetlb.{}.limit_in_bytes", hugetlb.page_size())),
|
|
|
|
hugetlb.limit(),
|
2021-05-24 18:52:34 +02:00
|
|
|
)?;
|
2021-05-21 23:35:02 +02:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_power_of_two(number: u64) -> bool {
|
2021-05-24 18:52:34 +02:00
|
|
|
(number != 0) && (number & (number - 1)) == 0
|
2021-05-21 23:35:02 +02:00
|
|
|
}
|
2021-07-26 22:13:57 +02:00
|
|
|
|
|
|
|
fn stats_for_page_size(cgroup_path: &Path, page_size: &str) -> Result<HugeTlbStats> {
|
|
|
|
let mut stats = HugeTlbStats::default();
|
|
|
|
|
|
|
|
let usage_file = format!("hugetlb.{}.usage_in_bytes", page_size);
|
|
|
|
let usage_content = common::read_cgroup_file(cgroup_path.join(usage_file))?;
|
|
|
|
stats.usage = usage_content.trim().parse()?;
|
|
|
|
|
|
|
|
let max_file = format!("hugetlb.{}.max_usage_in_bytes", page_size);
|
|
|
|
let max_content = common::read_cgroup_file(cgroup_path.join(max_file))?;
|
|
|
|
stats.max_usage = max_content.trim().parse()?;
|
|
|
|
|
|
|
|
let failcnt_file = format!("hugetlb.{}.failcnt", page_size);
|
|
|
|
let failcnt_content = common::read_cgroup_file(cgroup_path.join(failcnt_file))?;
|
|
|
|
stats.fail_count = failcnt_content.trim().parse()?;
|
|
|
|
|
|
|
|
Ok(stats)
|
|
|
|
}
|
2021-05-22 17:40:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
2021-08-11 19:54:21 +02:00
|
|
|
use crate::test::{create_temp_dir, set_fixture};
|
2021-09-27 03:08:56 +02:00
|
|
|
use oci_spec::runtime::LinuxHugepageLimitBuilder;
|
2021-06-10 01:04:37 +02:00
|
|
|
use std::fs::read_to_string;
|
2021-05-22 17:40:52 +02:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_set_hugetlb() {
|
|
|
|
let page_file_name = "hugetlb.2MB.limit_in_bytes";
|
|
|
|
let tmp = create_temp_dir("test_set_hugetlb").expect("create temp directory for test");
|
|
|
|
set_fixture(&tmp, page_file_name, "0").expect("Set fixture for 2 MB page size");
|
|
|
|
|
2021-09-27 03:08:56 +02:00
|
|
|
let hugetlb = LinuxHugepageLimitBuilder::default()
|
|
|
|
.page_size("2MB")
|
|
|
|
.limit(16384)
|
|
|
|
.build()
|
|
|
|
.unwrap();
|
|
|
|
|
2021-08-04 18:03:49 +02:00
|
|
|
HugeTlb::apply(&tmp, &hugetlb).expect("apply hugetlb");
|
2021-06-10 01:04:37 +02:00
|
|
|
let content = read_to_string(tmp.join(page_file_name)).expect("Read hugetlb file content");
|
2021-09-27 03:08:56 +02:00
|
|
|
assert_eq!(hugetlb.limit().to_string(), content);
|
2021-05-22 17:40:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_set_hugetlb_with_invalid_page_size() {
|
2021-05-24 18:52:34 +02:00
|
|
|
let tmp = create_temp_dir("test_set_hugetlb_with_invalid_page_size")
|
|
|
|
.expect("create temp directory for test");
|
2021-05-22 17:40:52 +02:00
|
|
|
|
2021-09-27 03:08:56 +02:00
|
|
|
let hugetlb = LinuxHugepageLimitBuilder::default()
|
|
|
|
.page_size("3MB")
|
|
|
|
.limit(16384)
|
|
|
|
.build()
|
|
|
|
.unwrap();
|
2021-05-22 17:40:52 +02:00
|
|
|
|
2021-08-04 18:03:49 +02:00
|
|
|
let result = HugeTlb::apply(&tmp, &hugetlb);
|
2021-05-24 18:52:34 +02:00
|
|
|
assert!(
|
|
|
|
result.is_err(),
|
|
|
|
"page size that is not a power of two should be an error"
|
|
|
|
);
|
2021-05-22 17:40:52 +02:00
|
|
|
}
|
2021-06-06 06:48:22 +02:00
|
|
|
|
|
|
|
quickcheck! {
|
|
|
|
fn property_test_set_hugetlb(hugetlb: LinuxHugepageLimit) -> bool {
|
2021-09-27 03:08:56 +02:00
|
|
|
let page_file_name = format!("hugetlb.{:?}.limit_in_bytes", hugetlb.page_size());
|
2021-06-06 06:48:22 +02:00
|
|
|
let tmp = create_temp_dir("property_test_set_hugetlb").expect("create temp directory for test");
|
|
|
|
set_fixture(&tmp, &page_file_name, "0").expect("Set fixture for page size");
|
|
|
|
|
2021-08-04 18:03:49 +02:00
|
|
|
let result = HugeTlb::apply(&tmp, &hugetlb);
|
2021-06-06 06:48:22 +02:00
|
|
|
|
2021-07-06 18:06:16 +02:00
|
|
|
let page_size: String = hugetlb
|
2021-09-27 03:08:56 +02:00
|
|
|
.page_size()
|
2021-07-06 18:06:16 +02:00
|
|
|
.chars()
|
|
|
|
.take_while(|c| c.is_digit(10))
|
|
|
|
.collect();
|
|
|
|
let page_size: u64 = page_size.parse().expect("parse page size");
|
2021-06-06 06:48:22 +02:00
|
|
|
|
2021-08-04 18:03:49 +02:00
|
|
|
if HugeTlb::is_power_of_two(page_size) && page_size != 1 {
|
2021-06-06 06:48:22 +02:00
|
|
|
let content =
|
2021-06-10 01:04:37 +02:00
|
|
|
read_to_string(tmp.join(page_file_name)).expect("Read hugetlb file content");
|
2021-09-27 03:08:56 +02:00
|
|
|
hugetlb.limit().to_string() == content
|
2021-06-06 06:48:22 +02:00
|
|
|
} else {
|
|
|
|
result.is_err()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-07-26 23:29:53 +02:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_stat_hugetlb() {
|
|
|
|
let tmp = create_temp_dir("test_stat_hugetlb").expect("create temp directory for test");
|
|
|
|
set_fixture(&tmp, "hugetlb.2MB.usage_in_bytes", "1024\n").expect("set hugetlb usage");
|
2021-07-27 17:15:42 +02:00
|
|
|
set_fixture(&tmp, "hugetlb.2MB.max_usage_in_bytes", "4096\n")
|
|
|
|
.expect("set hugetlb max usage");
|
2021-07-26 23:29:53 +02:00
|
|
|
set_fixture(&tmp, "hugetlb.2MB.failcnt", "5").expect("set hugetlb fail count");
|
|
|
|
|
2021-08-04 18:03:49 +02:00
|
|
|
let actual = HugeTlb::stats_for_page_size(&tmp, "2MB").expect("get cgroup stats");
|
2021-07-26 23:29:53 +02:00
|
|
|
|
|
|
|
let expected = HugeTlbStats {
|
|
|
|
usage: 1024,
|
|
|
|
max_usage: 4096,
|
|
|
|
fail_count: 5,
|
|
|
|
};
|
|
|
|
assert_eq!(actual, expected);
|
|
|
|
}
|
2021-05-24 18:52:34 +02:00
|
|
|
}
|