Skip to content

Commit 45929ed

Browse files
committed
Add support for rsvd hugetlb cgroup
Signed-off-by: omprakaash <omsuseela@gmail.com> Signed-off-by: om <omsuseela@gmail.com> Signed-off-by: Om Prakaash <omsuseela@gmail.com>
1 parent ed2c08d commit 45929ed

3 files changed

Lines changed: 199 additions & 12 deletions

File tree

crates/libcgroups/src/v1/hugetlb.rs

Lines changed: 72 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
use std::{collections::HashMap, num::ParseIntError, path::Path};
2-
31
use crate::{
42
common::{self, ControllerOpt, EitherError, MustBePowerOfTwo, WrappedIoError},
53
stats::{supported_page_sizes, HugeTlbStats, StatsProvider, SupportedPageSizesError},
64
};
5+
use std::{collections::HashMap, num::ParseIntError, path::Path};
76

7+
use crate::common::read_cgroup_file;
88
use oci_spec::runtime::LinuxHugepageLimit;
99

1010
use super::controller::Controller;
@@ -109,6 +109,15 @@ impl HugeTlb {
109109
root_path.join(format!("hugetlb.{}.limit_in_bytes", hugetlb.page_size())),
110110
hugetlb.limit(),
111111
)?;
112+
113+
let rsvd_file_path = root_path.join(format!(
114+
"hugetlb.{}.rsvd.limit_in_bytes",
115+
hugetlb.page_size()
116+
));
117+
if rsvd_file_path.exists() {
118+
common::write_cgroup_file(rsvd_file_path, hugetlb.limit())?;
119+
}
120+
112121
Ok(())
113122
}
114123

@@ -121,16 +130,20 @@ impl HugeTlb {
121130
page_size: &str,
122131
) -> Result<HugeTlbStats, V1HugeTlbStatsError> {
123132
let mut stats = HugeTlbStats::default();
124-
125-
let usage_file = format!("hugetlb.{page_size}.usage_in_bytes");
126-
let usage_content = common::read_cgroup_file(cgroup_path.join(usage_file))?;
133+
let mut file_prefix = format!("hugetlb.{page_size}.rsvd");
134+
let mut usage_file = format!("{file_prefix}.usage_in_bytes");
135+
let usage_content = read_cgroup_file(cgroup_path.join(&usage_file)).or_else(|_| {
136+
file_prefix = format!("hugetlb.{page_size}");
137+
usage_file = format!("{file_prefix}.usage_in_bytes");
138+
read_cgroup_file(cgroup_path.join(&usage_file))
139+
})?;
127140
stats.usage = usage_content.trim().parse()?;
128141

129-
let max_file = format!("hugetlb.{page_size}.max_usage_in_bytes");
142+
let max_file = format!("{file_prefix}.max_usage_in_bytes");
130143
let max_content = common::read_cgroup_file(cgroup_path.join(max_file))?;
131144
stats.max_usage = max_content.trim().parse()?;
132145

133-
let failcnt_file = format!("hugetlb.{page_size}.failcnt");
146+
let failcnt_file = format!("{file_prefix}.failcnt");
134147
let failcnt_content = common::read_cgroup_file(cgroup_path.join(failcnt_file))?;
135148
stats.fail_count = failcnt_content.trim().parse()?;
136149

@@ -163,6 +176,32 @@ mod tests {
163176
assert_eq!(hugetlb.limit().to_string(), content);
164177
}
165178

179+
#[test]
180+
fn test_set_rsvd_hugetlb() {
181+
let page_file_name = "hugetlb.2MB.limit_in_bytes";
182+
let rsvd_page_file_name = "hugetlb.2MB.rsvd.limit_in_bytes";
183+
let tmp = tempfile::tempdir().unwrap();
184+
set_fixture(tmp.path(), page_file_name, "0").expect("Set fixture for 2 MB page size");
185+
set_fixture(tmp.path(), rsvd_page_file_name, "0")
186+
.expect("Set fixture for 2 MB rsvd page size");
187+
188+
let hugetlb = LinuxHugepageLimitBuilder::default()
189+
.page_size("2MB")
190+
.limit(16384)
191+
.build()
192+
.unwrap();
193+
194+
HugeTlb::apply(tmp.path(), &hugetlb).expect("apply hugetlb");
195+
let content =
196+
read_to_string(tmp.path().join(page_file_name)).expect("Read hugetlb file content");
197+
let rsvd_content = read_to_string(tmp.path().join(rsvd_page_file_name))
198+
.expect("Read rsvd hugetlb file content");
199+
200+
// Both files should have been written to
201+
assert_eq!(hugetlb.limit().to_string(), content);
202+
assert_eq!(hugetlb.limit().to_string(), rsvd_content);
203+
}
204+
166205
#[test]
167206
fn test_set_hugetlb_with_invalid_page_size() {
168207
let tmp = tempfile::tempdir().unwrap();
@@ -222,4 +261,30 @@ mod tests {
222261
};
223262
assert_eq!(actual, expected);
224263
}
264+
265+
#[test]
266+
fn test_stat_rsvd_hugetlb() {
267+
let tmp = tempfile::tempdir().unwrap();
268+
269+
set_fixture(tmp.path(), "hugetlb.2MB.rsvd.usage_in_bytes", "1024\n")
270+
.expect("set hugetlb usage");
271+
set_fixture(tmp.path(), "hugetlb.2MB.rsvd.max_usage_in_bytes", "4096\n")
272+
.expect("set hugetlb max usage");
273+
set_fixture(tmp.path(), "hugetlb.2MB.rsvd.failcnt", "5").expect("set hugetlb fail count");
274+
275+
set_fixture(tmp.path(), "hugetlb.2MB.usage_in_bytes", "2048\n").expect("set hugetlb usage");
276+
set_fixture(tmp.path(), "hugetlb.2MB.max_usage_in_bytes", "8192\n")
277+
.expect("set hugetlb max usage");
278+
set_fixture(tmp.path(), "hugetlb.2MB.failcnt", "10").expect("set hugetlb fail count");
279+
280+
let actual = HugeTlb::stats_for_page_size(tmp.path(), "2MB").expect("get cgroup stats");
281+
282+
// Should prefer rsvd stats over non-rsvd stats
283+
let expected = HugeTlbStats {
284+
usage: 1024,
285+
max_usage: 4096,
286+
fail_count: 5,
287+
};
288+
assert_eq!(actual, expected);
289+
}
225290
}

crates/libcgroups/src/v2/hugetlb.rs

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use crate::{
1313
},
1414
};
1515

16+
use crate::common::read_cgroup_file;
1617
use oci_spec::runtime::LinuxHugepageLimit;
1718

1819
#[derive(thiserror::Error, Debug)]
@@ -104,6 +105,12 @@ impl HugeTlb {
104105
root_path.join(format!("hugetlb.{}.max", hugetlb.page_size())),
105106
hugetlb.limit(),
106107
)?;
108+
109+
let rsvd_file_path = root_path.join(format!("hugetlb.{}.rsvd.max", hugetlb.page_size()));
110+
if rsvd_file_path.exists() {
111+
common::write_cgroup_file(rsvd_file_path, hugetlb.limit())?;
112+
}
113+
107114
Ok(())
108115
}
109116

@@ -115,9 +122,14 @@ impl HugeTlb {
115122
cgroup_path: &Path,
116123
page_size: &str,
117124
) -> Result<HugeTlbStats, V2HugeTlbStatsError> {
118-
let events_file = format!("hugetlb.{page_size}.events");
119-
let path = cgroup_path.join(events_file);
120-
let events = common::read_cgroup_file(&path)?;
125+
let mut file_prefix = format!("hugetlb.{page_size}.rsvd");
126+
let mut path = cgroup_path.join(format!("{file_prefix}.events"));
127+
let events = read_cgroup_file(&path).or_else(|_| {
128+
file_prefix = format!("hugetlb.{page_size}");
129+
path = cgroup_path.join(format!("{file_prefix}.events"));
130+
read_cgroup_file(&path)
131+
})?;
132+
121133
let fail_count: u64 = events
122134
.lines()
123135
.find(|l| l.starts_with("max"))
@@ -130,7 +142,7 @@ impl HugeTlb {
130142
.unwrap_or_default();
131143

132144
Ok(HugeTlbStats {
133-
usage: parse_single_value(&cgroup_path.join(format!("hugetlb.{page_size}.current")))?,
145+
usage: parse_single_value(&cgroup_path.join(format!("{file_prefix}.current")))?,
134146
fail_count,
135147
..Default::default()
136148
})
@@ -178,6 +190,31 @@ mod tests {
178190
);
179191
}
180192

193+
#[test]
194+
fn test_set_rsvd_hugetlb() {
195+
let page_file_name = "hugetlb.2MB.max";
196+
let rsvd_page_file_name = "hugetlb.2MB.rsvd.max";
197+
let tmp = tempfile::tempdir().unwrap();
198+
set_fixture(tmp.path(), page_file_name, "0").expect("Set fixture for 2 MB page size");
199+
set_fixture(tmp.path(), rsvd_page_file_name, "0")
200+
.expect("Set fixture for 2 MB rsvd page size");
201+
202+
let hugetlb = LinuxHugepageLimitBuilder::default()
203+
.page_size("2MB")
204+
.limit(16384)
205+
.build()
206+
.unwrap();
207+
HugeTlb::apply(tmp.path(), &hugetlb).expect("apply hugetlb");
208+
209+
let content =
210+
read_to_string(tmp.path().join(page_file_name)).expect("Read hugetlb file content");
211+
let rsvd_content = read_to_string(tmp.path().join(rsvd_page_file_name))
212+
.expect("Read hugetlb file content");
213+
214+
assert_eq!(hugetlb.limit().to_string(), content);
215+
assert_eq!(hugetlb.limit().to_string(), rsvd_content);
216+
}
217+
181218
quickcheck! {
182219
fn property_test_set_hugetlb(hugetlb: LinuxHugepageLimit) -> bool {
183220
let page_file_name = format!("hugetlb.{:?}.max", hugetlb.page_size());
@@ -217,4 +254,25 @@ mod tests {
217254
};
218255
assert_eq!(actual, expected);
219256
}
257+
258+
#[test]
259+
fn test_stat_rsvd_hugetbl() {
260+
let tmp = tempfile::tempdir().unwrap();
261+
set_fixture(tmp.path(), "hugetlb.2MB.current", "2048\n").expect("set hugetlb current");
262+
set_fixture(tmp.path(), "hugetlb.2MB.events", "max 5\n").expect("set hugetlb events");
263+
set_fixture(tmp.path(), "hugetlb.2MB.rsvd.current", "1024\n")
264+
.expect("set hugetlb rsvd current");
265+
set_fixture(tmp.path(), "hugetlb.2MB.rsvd.events", "max 5\n")
266+
.expect("set hugetlb rsvd events");
267+
268+
let actual = HugeTlb::stats_for_page_size(tmp.path(), "2MB").expect("get cgroup stats");
269+
270+
// Should prefer rsvd stats over non-rsvd stats if available
271+
let expected = HugeTlbStats {
272+
usage: 1024,
273+
max_usage: 0,
274+
fail_count: 5,
275+
};
276+
assert_eq!(actual, expected);
277+
}
220278
}

tests/contest/contest/src/tests/tlb/tlb_test.rs

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,20 @@ fn check_hugetlb() -> bool {
1111
PathBuf::from("/sys/fs/cgroup/hugetlb").exists()
1212
}
1313

14+
fn check_hugetlb_rsvd() -> bool {
15+
let sizes = get_tlb_sizes();
16+
for size in sizes.iter() {
17+
let rsvd_path = format!(
18+
"/sys/fs/cgroup/hugetlb/hugetlb.{}.rsvd.limit_in_bytes",
19+
size
20+
);
21+
if !PathBuf::from(rsvd_path).exists() {
22+
return false;
23+
}
24+
}
25+
true
26+
}
27+
1428
fn make_hugetlb_spec(page_size: &str, limit: i64) -> Spec {
1529
SpecBuilder::default()
1630
.linux(
@@ -110,6 +124,23 @@ fn validate_tlb(id: &str, size: &str, limit: i64) -> TestResult {
110124
}
111125
}
112126

127+
fn validate_rsvd_tlb(id: &str, size: &str, limit: i64) -> TestResult {
128+
let root = "/sys/fs/cgroup/hugetlb";
129+
let path = format!("{root}/{id}/hugetlb.{size}.rsvd.limit_in_bytes");
130+
let val_str = std::fs::read_to_string(path).unwrap();
131+
let val: i64 = val_str.trim().parse().unwrap();
132+
if val == limit {
133+
TestResult::Passed
134+
} else {
135+
TestResult::Failed(anyhow!(
136+
"page limit not set correctly : for size {}, expected {}, got {}",
137+
size,
138+
limit,
139+
val
140+
))
141+
}
142+
}
143+
113144
fn test_valid_tlb() -> TestResult {
114145
// When setting the limit just for checking if writing works, the amount of memory
115146
// requested does not matter, as all insigned integers will be accepted.
@@ -134,6 +165,30 @@ fn test_valid_tlb() -> TestResult {
134165
TestResult::Passed
135166
}
136167

168+
fn test_valid_rsvd_tlb() -> TestResult {
169+
let limit: i64 = 1 << 30;
170+
let tlb_sizes = get_tlb_sizes();
171+
for size in tlb_sizes.iter() {
172+
let spec = make_hugetlb_spec(size, limit);
173+
let res = test_outside_container(spec, &|data| {
174+
test_result!(check_container_created(&data));
175+
// Currentle, we write the same value to both limit_in_bytes and rsvd.limit_in_bytes
176+
let non_rsvd = validate_tlb(&data.id, size, limit);
177+
let rsvd = validate_rsvd_tlb(&data.id, size, limit);
178+
if matches!(non_rsvd, TestResult::Failed(_)) {
179+
return non_rsvd;
180+
} else if matches!(rsvd, TestResult::Failed(_)) {
181+
return rsvd;
182+
}
183+
TestResult::Passed
184+
});
185+
if matches!(res, TestResult::Failed(_)) {
186+
return res;
187+
}
188+
}
189+
TestResult::Passed
190+
}
191+
137192
pub fn get_tlb_test() -> TestGroup {
138193
let wrong_tlb = ConditionalTest::new(
139194
"invalid_tlb",
@@ -145,7 +200,16 @@ pub fn get_tlb_test() -> TestGroup {
145200
Box::new(check_hugetlb),
146201
Box::new(test_valid_tlb),
147202
);
203+
let valid_rsvd_tlb = ConditionalTest::new(
204+
"valid_rsvd_tlb",
205+
Box::new(check_hugetlb_rsvd),
206+
Box::new(test_valid_rsvd_tlb),
207+
);
148208
let mut tg = TestGroup::new("huge_tlb");
149-
tg.add(vec![Box::new(wrong_tlb), Box::new(valid_tlb)]);
209+
tg.add(vec![
210+
Box::new(wrong_tlb),
211+
Box::new(valid_tlb),
212+
Box::new(valid_rsvd_tlb),
213+
]);
150214
tg
151215
}

0 commit comments

Comments
 (0)