Skip to content

Commit 895d941

Browse files
committed
more robust cgroup version detection
1 parent cfb5bbc commit 895d941

File tree

1 file changed

+120
-39
lines changed

1 file changed

+120
-39
lines changed

cli/lib/worker.rs

+120-39
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ pub fn get_cache_storage_dir() -> PathBuf {
132132
pub fn create_isolate_create_params() -> Option<v8::CreateParams> {
133133
#[cfg(any(target_os = "android", target_os = "linux"))]
134134
{
135-
get_memory_limit_linux().map(|memory_limit| {
135+
linux::get_memory_limit().map(|memory_limit| {
136136
v8::CreateParams::default()
137137
.heap_limits_from_system_memory(memory_limit, 0)
138138
})
@@ -147,48 +147,129 @@ pub fn create_isolate_create_params() -> Option<v8::CreateParams> {
147147
}
148148
}
149149

150-
/// Get memory limit with cgroup (either v1 or v2) taken into account.
151150
#[cfg(any(target_os = "android", target_os = "linux"))]
152-
fn get_memory_limit_linux() -> Option<u64> {
153-
let system_total_memory =
154-
deno_runtime::deno_os::sys_info::mem_info().map(|mem_info| mem_info.total);
155-
156-
let Ok(self_cgroup) = std::fs::read_to_string("/proc/self/cgroup") else {
157-
return system_total_memory;
158-
};
159-
160-
let limit = match self_cgroup.strip_prefix("0::/") {
161-
Some(cgroup_v2_relpath) => {
162-
// cgroup v2
163-
let limit_path = std::path::Path::new("/sys/fs/cgroup")
164-
.join(cgroup_v2_relpath)
165-
.join("memory.max");
166-
std::fs::read_to_string(limit_path)
167-
.ok()
168-
.and_then(|s| s.trim().parse::<u64>().ok())
169-
}
170-
None => {
171-
// cgroup v1
172-
let Some(cgroup_v1_relpath) = self_cgroup.lines().find_map(|l| {
173-
let split = l.split(":").collect::<Vec<_>>();
174-
if split.get(1) == Some(&"memory") {
175-
split.get(2)
176-
} else {
177-
None
151+
mod linux {
152+
/// Get memory limit with cgroup (either v1 or v2) taken into account.
153+
pub(super) fn get_memory_limit() -> Option<u64> {
154+
let system_total_memory = deno_runtime::deno_os::sys_info::mem_info()
155+
.map(|mem_info| mem_info.total);
156+
157+
let Ok(self_cgroup) = std::fs::read_to_string("/proc/self/cgroup") else {
158+
return system_total_memory;
159+
};
160+
161+
let limit = match parse_self_cgroup(&self_cgroup) {
162+
CgroupVersion::V1 { cgroup_relpath } => {
163+
let limit_path = std::path::Path::new("/sys/fs/cgroup/memory")
164+
.join(cgroup_relpath)
165+
.join("memory.limit_in_bytes");
166+
std::fs::read_to_string(limit_path)
167+
.ok()
168+
.and_then(|s| s.trim().parse::<u64>().ok())
169+
}
170+
CgroupVersion::V2 { cgroup_relpath } => {
171+
let limit_path = std::path::Path::new("/sys/fs/cgroup")
172+
.join(cgroup_relpath)
173+
.join("memory.max");
174+
std::fs::read_to_string(limit_path)
175+
.ok()
176+
.and_then(|s| s.trim().parse::<u64>().ok())
177+
}
178+
CgroupVersion::None => system_total_memory,
179+
};
180+
181+
limit.or(system_total_memory)
182+
}
183+
184+
enum CgroupVersion<'a> {
185+
V1 { cgroup_relpath: &'a str },
186+
V2 { cgroup_relpath: &'a str },
187+
None,
188+
}
189+
190+
fn parse_self_cgroup<'a>(self_cgroup_content: &'a str) -> CgroupVersion<'a> {
191+
let mut cgroup_version = CgroupVersion::None;
192+
193+
for line in self_cgroup_content.lines() {
194+
let split = line.split(":").collect::<Vec<_>>();
195+
196+
match &split[..] {
197+
// A line like `4:memory:/foo/bar` means that memory is managed by v1.
198+
[_, "memory", cgroup_v1_relpath] => {
199+
cgroup_version = CgroupVersion::V1 {
200+
cgroup_relpath: cgroup_v1_relpath,
201+
};
202+
break;
178203
}
179-
}) else {
180-
return system_total_memory;
181-
};
182-
let limit_path = std::path::Path::new("/sys/fs/cgroup/memory")
183-
.join(cgroup_v1_relpath)
184-
.join("memory.limit_in_bytes");
185-
std::fs::read_to_string(limit_path)
186-
.ok()
187-
.and_then(|s| s.trim().parse::<u64>().ok())
204+
// A line like `0::/foo/bar` means that cgroup v2 is used.
205+
// However, it is still possible that memory is managed by v1 when
206+
// hybrid mode is enabled. We continue until the whole lines are checked
207+
// or an explicit memory line (indicating v1) is found.
208+
["0", "", cgroup_v2_relpath] => {
209+
cgroup_version = CgroupVersion::V2 {
210+
cgroup_relpath: cgroup_v2_relpath,
211+
};
212+
}
213+
_ => {}
214+
}
188215
}
189-
};
190216

191-
limit.or(system_total_memory)
217+
cgroup_version
218+
}
219+
220+
#[test]
221+
fn test_parse_self_cgroup_v2() {
222+
let self_cgroup = "0::/user.slice/user-1000.slice/session-3.scope";
223+
let cgroup_version = parse_self_cgroup(self_cgroup);
224+
assert!(matches!(
225+
cgroup_version,
226+
CgroupVersion::V2 { cgroup_relpath } if cgroup_relpath == "/user.slice/user-1000.slice/session-3.scope"
227+
));
228+
}
229+
230+
#[test]
231+
fn test_parse_self_cgroup_hybrid() {
232+
let self_cgroup = r#"12:rdma:/
233+
11:blkio:/user.slice
234+
10:devices:/user.slice
235+
9:cpu,cpuacct:/user.slice
236+
8:pids:/user.slice/user-1000.slice/session-3.scope
237+
7:memory:/user.slice/user-1000.slice/session-3.scope
238+
6:perf_event:/
239+
5:freezer:/
240+
4:net_cls,net_prio:/
241+
3:hugetlb:/
242+
2:cpuset:/
243+
1:name=systemd:/user.slice/user-1000.slice/session-3.scope
244+
0::/user.slice/user-1000.slice/session-3.scope
245+
"#;
246+
let cgroup_version = parse_self_cgroup(self_cgroup);
247+
assert!(matches!(
248+
cgroup_version,
249+
CgroupVersion::V1 { cgroup_relpath } if cgroup_relpath == "/user.slice/user-1000.slice/session-3.scope"
250+
));
251+
}
252+
253+
#[test]
254+
fn test_parse_self_cgroup_v1() {
255+
let self_cgroup = r#"11:hugetlb:/
256+
10:pids:/user.slice/user-1000.slice
257+
9:perf_event:/
258+
8:devices:/user.slice
259+
7:net_cls,net_prio:/
260+
6:memory:/
261+
5:blkio:/
262+
4:cpuset:/
263+
3:cpu,cpuacct:/
264+
2:freezer:/
265+
1:name=systemd:/user.slice/user-1000.slice/session-2.scope
266+
"#;
267+
let cgroup_version = parse_self_cgroup(self_cgroup);
268+
assert!(matches!(
269+
cgroup_version,
270+
CgroupVersion::V1 { cgroup_relpath } if cgroup_relpath == "/"
271+
));
272+
}
192273
}
193274

194275
#[derive(Debug, thiserror::Error, deno_error::JsError)]

0 commit comments

Comments
 (0)