Skip to content

fix: detect hybrid cgroup memory limits - #1669

Closed
leiysky wants to merge 2 commits into
GuillaumeGomez:mainfrom
leiysky:main
Closed

fix: detect hybrid cgroup memory limits#1669
leiysky wants to merge 2 commits into
GuillaumeGomez:mainfrom
leiysky:main

Conversation

@leiysky

@leiysky leiysky commented May 20, 2026

Copy link
Copy Markdown

Summary

Fix Linux cgroup memory limit detection in hybrid cgroup environments.

This changes cgroup path parsing to keep cgroup v2 and v1 memory-controller paths separately, prefers the v1 memory-controller limit when available, and stops treating /proc/meminfo MemTotal as a cgroup memory limit when no finite cgroup limit is found.

In some Kubernetes/container environments, /proc/<pid>/cgroup can contain both a cgroup v2 entry and a v1 memory-controller entry, for example:

0::/system.slice/service.scope
11:memory:/memory.slice/service.scope

The old implementation parsed this into a single path and reused it for both v2 and v1. If the v2 path had memory.max = max, memory_limits still initialized the result from host MemTotal, so cgroup_limits() could return host memory as Some(CGroupLimits) instead of either falling back to the v1 memory-controller path or returning None.

This can make callers believe a container has the host's memory capacity when it actually has a smaller cgroup limit.

Copilot AI review requested due to automatic review settings May 20, 2026 12:51

This comment was marked as spam.

Comment thread src/unix/linux/cgroup.rs
Comment on lines +498 to +528
fn decode_cgroup_path(path: &str) -> String {
let bytes = path.as_bytes();
let mut decoded = Vec::with_capacity(bytes.len());
let mut pos = 0;

while pos < bytes.len() {
if bytes[pos] == b'\\'
&& pos + 3 < bytes.len()
&& let Some(value) = decode_octal_escape(&bytes[pos + 1..pos + 4])
{
decoded.push(value);
pos += 4;
continue;
}

decoded.push(bytes[pos]);
pos += 1;
}

String::from_utf8(decoded).unwrap_or_else(|_| path.to_owned())
}

fn decode_octal_escape(digits: &[u8]) -> Option<u8> {
let mut value = 0;

for digit in digits {
if !(b'0'..=b'7').contains(digit) {
return None;
}
value = value * 8 + (digit - b'0');
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless I missed something, we can keep the path as is (with Path::new()) and therefore completely remove these 2 functions.

Comment thread src/unix/linux/cgroup.rs
|| controllers
.split(',')
.any(|controller| controller == "memory")
let Some(hierarchy_id) = fields.next() else {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice improvement. 👍

Comment thread src/unix/linux/cgroup.rs
Comment on lines +412 to +417
for field in fields.by_ref() {
if field == "-" {
found_separator = true;
break;
}
}

@GuillaumeGomez GuillaumeGomez May 28, 2026

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for field in fields.by_ref() {
if field == "-" {
found_separator = true;
break;
}
}
// Skipping optional fields (the end is marked with "-").
while let Some(field) = fields.next() {
if field == "-" {
found_separator = true;
break;
}
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Took me a while to understand what by_ref() was doing so I'd rather have "simpler" code.

@tisonkun

tisonkun commented Sep 1, 2026

Copy link
Copy Markdown

Hey @GuillaumeGomez! I'm @leiysky's collegaue and I'm trying to continue this PR in #1723 so that we can move from an internal pinned dependency back to the upstream sysinfo.

I'll appreciate it if you can review #1723 when you have some time.

@GuillaumeGomez

Copy link
Copy Markdown
Owner

Closing in favour of #1723 then.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants