Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 29 additions & 5 deletions src/libraries/Common/src/Interop/Linux/cgroups/Interop.cgroups.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,16 +116,24 @@ internal static bool TryGetMemoryLimitV1(out ulong limit)
/// <param name="limit">The read limit, or 0 if it couldn't be read.</param>
/// <returns>true if the limit was read successfully; otherwise, false.</returns>
internal static bool TryGetMemoryLimitV2(out ulong limit)
{
return TryGetMemoryLimitV2(s_cgroupMemoryPath, s_cgroupMemoryHierarchyMountPath, out limit);
}

/// <summary>Tries to read the memory limit from a cgroup v2 path hierarchy.</summary>
/// <param name="currentCGroupMemoryPath">The current cgroup memory path.</param>
/// <param name="cgroupMemoryHierarchyMountPath">The cgroup memory hierarchy mount path.</param>
/// <param name="limit">The read limit, or 0 if it couldn't be read.</param>
/// <returns>true if the limit was read successfully; otherwise, false.</returns>
internal static bool TryGetMemoryLimitV2(string? currentCGroupMemoryPath, string? cgroupMemoryHierarchyMountPath, out ulong limit)
{
bool foundAnyLimit = false;
ulong minLimit = ulong.MaxValue;
string? currentCGroupMemoryPath = s_cgroupMemoryPath;
string? cgroupMemoryHierarchyMountPath = s_cgroupMemoryHierarchyMountPath;
if (currentCGroupMemoryPath != null && cgroupMemoryHierarchyMountPath != null)
{
// Iterate over the directory hierarchy representing the cgroup hierarchy until reaching the
// mount directory. The mount directory doesn't contain the memory.max.
do
// mount directory. The mount directory can contain memory.max when it is not the global root.
while (currentCGroupMemoryPath != null && IsPathAtOrBelowMount(currentCGroupMemoryPath, cgroupMemoryHierarchyMountPath))
{
if (TryReadMemoryValueFromFile(currentCGroupMemoryPath + "/memory.max", out ulong currentLevelLimit))
{
Expand All @@ -135,16 +143,32 @@ internal static bool TryGetMemoryLimitV2(out ulong limit)
minLimit = currentLevelLimit;
}
}
if (currentCGroupMemoryPath == cgroupMemoryHierarchyMountPath)
{
break;
}

currentCGroupMemoryPath = Path.GetDirectoryName(currentCGroupMemoryPath);
}
while (currentCGroupMemoryPath!.Length != cgroupMemoryHierarchyMountPath.Length);
}

limit = minLimit;

return foundAnyLimit;
}

private static bool IsPathAtOrBelowMount(string path, string mount)
Comment thread
janvorli marked this conversation as resolved.
{
if (!path.StartsWith(mount, StringComparison.Ordinal))
{
return false;
}

return path.Length == mount.Length ||
mount == "/" ||
path[mount.Length] == '/';
}

/// <summary>Tries to parse a memory limit from the specified file.</summary>
/// <param name="path">The path to the file to parse.</param>
/// <param name="result">The parsed result, or 0 if it couldn't be parsed.</param>
Expand Down
30 changes: 30 additions & 0 deletions src/libraries/Common/tests/Tests/Interop/cgroupsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,41 @@ public void ValidateTryReadMemoryValue(bool expectedResult, string valueText, ul
[Theory]
[InlineData("/sys/fs/cgroup/cpu/my_cgroup", "/docker/1234", "/sys/fs/cgroup/cpu", "/docker/1234/my_cgroup")]
[InlineData("/sys/fs/cgroup/cpu/my_cgroup", "/", "/sys/fs/cgroup/cpu", "/my_cgroup")]
[InlineData("/sys/fs/cgroup", "/some/container", "/sys/fs/cgroup", "/some/container")]
public void ValidateFindCGroupPath(string expectedResult, string hierarchyRoot, string hierarchyMount, string cgroupPathRelativeToMount)
{
Assert.Equal(expectedResult, Interop.cgroups.FindCGroupPath(hierarchyRoot, hierarchyMount, cgroupPathRelativeToMount));
}

[Fact]
public void ValidateTryGetMemoryLimitV2AtHierarchyMount()
{
string testRoot = GetTestFilePath();
string hierarchyMount = Path.Combine(testRoot, "mount");
Directory.CreateDirectory(hierarchyMount);
File.WriteAllText(Path.Combine(hierarchyMount, "memory.max"), "1");

Assert.True(Interop.cgroups.TryGetMemoryLimitV2(hierarchyMount, hierarchyMount, out ulong limit));
Assert.Equal(1UL, limit);
}

[Fact]
public void ValidateTryGetMemoryLimitV2StopsAtHierarchyMount()
{
string testRoot = GetTestFilePath();
string hierarchyMount = Path.Combine(testRoot, "mount");
string parentCGroup = Path.Combine(hierarchyMount, "a");
string currentCGroup = Path.Combine(parentCGroup, "b");
Directory.CreateDirectory(currentCGroup);
File.WriteAllText(Path.Combine(hierarchyMount, "memory.max"), "20");
File.WriteAllText(Path.Combine(parentCGroup, "memory.max"), "20");
File.WriteAllText(Path.Combine(currentCGroup, "memory.max"), "10");
File.WriteAllText(Path.Combine(testRoot, "memory.max"), "2");

Assert.True(Interop.cgroups.TryGetMemoryLimitV2(currentCGroup, hierarchyMount, out ulong limit));
Assert.Equal(10UL, limit);
}

[Theory]
[InlineData(true, 2, "0 0 0:0 / /foo ignore ignore - cgroup2 cgroup2 ignore", "ignore", "/", "/foo")]
[InlineData(true, 2, "0 0 0:0 / /foo ignore ignore - cgroup2 cgroup2 ignore", "memory", "/", "/foo")]
Expand Down
Loading