1use log::warn;
12use std::io::Write;
13use std::path::PathBuf;
14
15use crate::fs::error::ErrorKind::*;
16use crate::fs::error::*;
17use crate::fs::{flat_keyed_to_vec, read_u64_from};
18
19use crate::fs::{
20 ControllIdentifier, ControllerInternal, Controllers, HugePageResources, Resources, Subsystem,
21};
22
23#[derive(Debug, Clone)]
28pub struct HugeTlbController {
29 base: PathBuf,
30 path: PathBuf,
31 sizes: Vec<String>,
32 v2: bool,
33}
34
35impl ControllerInternal for HugeTlbController {
36 fn control_type(&self) -> Controllers {
37 Controllers::HugeTlb
38 }
39 fn get_path(&self) -> &PathBuf {
40 &self.path
41 }
42 fn get_path_mut(&mut self) -> &mut PathBuf {
43 &mut self.path
44 }
45 fn get_base(&self) -> &PathBuf {
46 &self.base
47 }
48
49 fn is_v2(&self) -> bool {
50 self.v2
51 }
52
53 fn apply(&self, res: &Resources) -> Result<()> {
54 let res: &HugePageResources = &res.hugepages;
56
57 for i in &res.limits {
58 let _ = self.set_limit_in_bytes(&i.size, i.limit);
59 if self.limit_in_bytes(&i.size)? != i.limit {
60 return Err(Error::new(Other));
61 }
62 }
63
64 Ok(())
65 }
66}
67
68impl ControllIdentifier for HugeTlbController {
69 fn controller_type() -> Controllers {
70 Controllers::HugeTlb
71 }
72}
73
74impl<'a> From<&'a Subsystem> for &'a HugeTlbController {
75 fn from(sub: &'a Subsystem) -> &'a HugeTlbController {
76 unsafe {
77 match sub {
78 Subsystem::HugeTlb(c) => c,
79 _ => {
80 assert_eq!(1, 0);
81 let v = std::mem::MaybeUninit::uninit();
82 v.assume_init()
83 }
84 }
85 }
86 }
87}
88
89impl HugeTlbController {
90 pub fn new(point: PathBuf, root: PathBuf, v2: bool) -> Self {
92 let sizes = get_hugepage_sizes();
93 Self {
94 base: root,
95 path: point,
96 sizes,
97 v2,
98 }
99 }
100
101 pub fn size_supported(&self, hugetlb_size: &str) -> bool {
103 for s in &self.sizes {
104 if s == hugetlb_size {
105 return true;
106 }
107 }
108 false
109 }
110
111 pub fn get_sizes(&self) -> Vec<String> {
112 self.sizes.clone()
113 }
114
115 fn failcnt_v2(&self, hugetlb_size: &str) -> Result<u64> {
116 self.open_path(&format!("hugetlb.{}.events", hugetlb_size), false)
117 .and_then(flat_keyed_to_vec)
118 .and_then(|x| {
119 if x.is_empty() {
120 return Err(Error::from_string(format!(
121 "get empty from hugetlb.{}.events",
122 hugetlb_size
123 )));
124 }
125 Ok(x[0].1 as u64)
126 })
127 }
128
129 pub fn failcnt(&self, hugetlb_size: &str) -> Result<u64> {
131 if self.v2 {
132 return self.failcnt_v2(hugetlb_size);
133 }
134 self.open_path(&format!("hugetlb.{}.failcnt", hugetlb_size), false)
135 .and_then(read_u64_from)
136 }
137
138 pub fn limit_in_bytes(&self, hugetlb_size: &str) -> Result<u64> {
141 let mut file_name = format!("hugetlb.{}.limit_in_bytes", hugetlb_size);
142 if self.v2 {
143 file_name = format!("hugetlb.{}.max", hugetlb_size);
144 }
145 self.open_path(&file_name, false).and_then(read_u64_from)
146 }
147
148 pub fn usage_in_bytes(&self, hugetlb_size: &str) -> Result<u64> {
151 let mut file = format!("hugetlb.{}.usage_in_bytes", hugetlb_size);
152 if self.v2 {
153 file = format!("hugetlb.{}.current", hugetlb_size);
154 }
155 self.open_path(&file, false).and_then(read_u64_from)
156 }
157
158 pub fn max_usage_in_bytes(&self, hugetlb_size: &str) -> Result<u64> {
161 self.open_path(
162 &format!("hugetlb.{}.max_usage_in_bytes", hugetlb_size),
163 false,
164 )
165 .and_then(read_u64_from)
166 }
167
168 pub fn set_limit_in_bytes(&self, hugetlb_size: &str, limit: u64) -> Result<()> {
171 let mut file_name = format!("hugetlb.{}.limit_in_bytes", hugetlb_size);
172 if self.v2 {
173 file_name = format!("hugetlb.{}.max", hugetlb_size);
174 }
175 self.open_path(&file_name, true).and_then(|mut file| {
176 file.write_all(limit.to_string().as_ref()).map_err(|e| {
177 Error::with_cause(WriteFailed(file_name.to_string(), limit.to_string()), e)
178 })
179 })
180 }
181}
182
183pub const HUGEPAGESIZE_DIR: &str = "/sys/kernel/mm/hugepages";
184use std::collections::HashMap;
185use std::fs;
186
187fn get_hugepage_sizes() -> Vec<String> {
188 let dirs = fs::read_dir(HUGEPAGESIZE_DIR);
189 if dirs.is_err() {
190 return Vec::new();
191 }
192
193 dirs.unwrap()
194 .filter_map(|e| {
195 let entry = e.map_err(|e| warn!("readdir error: {:?}", e)).ok()?;
196 let name = entry.file_name().into_string().unwrap();
197 let parts: Vec<&str> = name.split('-').collect();
198 if parts.len() != 2 {
199 return None;
200 }
201 let bmap = get_binary_size_map();
202 let size = parse_size(parts[1], &bmap)
203 .map_err(|e| warn!("parse_size error: {:?}", e))
204 .ok()?;
205 let dabbrs = get_decimal_abbrs();
206
207 Some(custom_size(size as f64, 1024.0, &dabbrs))
208 })
209 .collect()
210}
211
212pub const KB: u128 = 1000;
213pub const MB: u128 = 1000 * KB;
214pub const GB: u128 = 1000 * MB;
215pub const TB: u128 = 1000 * GB;
216pub const PB: u128 = 1000 * TB;
217
218#[allow(non_upper_case_globals)]
219pub const KiB: u128 = 1024;
220#[allow(non_upper_case_globals)]
221pub const MiB: u128 = 1024 * KiB;
222#[allow(non_upper_case_globals)]
223pub const GiB: u128 = 1024 * MiB;
224#[allow(non_upper_case_globals)]
225pub const TiB: u128 = 1024 * GiB;
226#[allow(non_upper_case_globals)]
227pub const PiB: u128 = 1024 * TiB;
228
229pub fn get_binary_size_map() -> HashMap<String, u128> {
230 let mut m = HashMap::new();
231 m.insert("k".to_string(), KiB);
232 m.insert("m".to_string(), MiB);
233 m.insert("g".to_string(), GiB);
234 m.insert("t".to_string(), TiB);
235 m.insert("p".to_string(), PiB);
236 m
237}
238
239pub fn get_decimal_size_map() -> HashMap<String, u128> {
240 let mut m = HashMap::new();
241 m.insert("k".to_string(), KB);
242 m.insert("m".to_string(), MB);
243 m.insert("g".to_string(), GB);
244 m.insert("t".to_string(), TB);
245 m.insert("p".to_string(), PB);
246 m
247}
248
249pub fn get_decimal_abbrs() -> Vec<String> {
250 let m = vec![
251 "B".to_string(),
252 "KB".to_string(),
253 "MB".to_string(),
254 "GB".to_string(),
255 "TB".to_string(),
256 "PB".to_string(),
257 "EB".to_string(),
258 "ZB".to_string(),
259 "YB".to_string(),
260 ];
261 m
262}
263
264fn parse_size(s: &str, m: &HashMap<String, u128>) -> Result<u128> {
265 let s = s.trim();
267
268 let s = if let Some(stripped) = s.strip_suffix('b').or_else(|| s.strip_suffix('B')) {
270 stripped
271 } else {
272 s
273 };
274
275 if s.is_empty() {
277 return Err(Error::new(InvalidBytesSize));
278 }
279
280 let last_char = s.chars().last().unwrap();
282 if !"kKmMgGtTpP".contains(last_char) {
283 return Err(Error::new(InvalidBytesSize));
284 }
285
286 let num_part = &s[..s.len() - last_char.len_utf8()];
288 if num_part.trim().is_empty() {
289 return Err(Error::new(InvalidBytesSize));
290 }
291
292 let number: u128 = num_part
294 .trim()
295 .parse()
296 .map_err(|_| Error::new(InvalidBytesSize))?;
297
298 let multiplier_key = last_char.to_string();
300 let multiplier = m
301 .get(&multiplier_key)
302 .ok_or_else(|| Error::new(InvalidBytesSize))?;
303
304 Ok(number * multiplier)
305}
306
307fn custom_size(mut size: f64, base: f64, m: &[String]) -> String {
308 let mut i = 0;
309 while size >= base && i < m.len() - 1 {
310 size /= base;
311 i += 1;
312 }
313
314 format!("{}{}", size, m[i].as_str())
315}
316
317#[cfg(test)]
318mod tests {
319 use super::*;
320
321 #[test]
322 fn test_binary_size_valid() {
323 let m = get_binary_size_map();
324 assert_eq!(parse_size("1k", &m).unwrap(), KiB);
326 assert_eq!(parse_size("2m", &m).unwrap(), 2 * MiB);
327 assert_eq!(parse_size("3g", &m).unwrap(), 3 * GiB);
328 assert_eq!(parse_size("4t", &m).unwrap(), 4 * TiB);
329 assert_eq!(parse_size("5p", &m).unwrap(), 5 * PiB);
330 }
331
332 #[test]
333 fn test_decimal_size_valid() {
334 let m = get_decimal_size_map();
335 assert_eq!(parse_size("1k", &m).unwrap(), KB);
336 assert_eq!(parse_size("2m", &m).unwrap(), 2 * MB);
337 assert_eq!(parse_size("3g", &m).unwrap(), 3 * GB);
338 assert_eq!(parse_size("4t", &m).unwrap(), 4 * TB);
339 assert_eq!(parse_size("5p", &m).unwrap(), 5 * PB);
340 }
341
342 #[test]
343 fn test_trailing_b_suffix() {
344 let m = get_binary_size_map();
345 assert_eq!(parse_size("1kb", &m).unwrap(), KiB);
347 assert_eq!(parse_size("2mB", &m).unwrap(), 2 * MiB);
348 }
349
350 #[test]
351 fn test_invalid_inputs() {
352 let m = get_binary_size_map();
353 assert!(parse_size("1", &m).is_err());
355 assert!(parse_size("10x", &m).is_err());
357 assert!(parse_size("abc", &m).is_err());
359 assert!(parse_size("k", &m).is_err());
361 assert!(parse_size("123z", &m).is_err());
363 }
364
365 #[test]
366 fn test_uppercase_multiplier_fails() {
367 let m = get_binary_size_map();
368 assert!(parse_size("1K", &m).is_err());
371 }
372}