diff --git a/src/libraries/Common/src/Interop/Linux/cgroups/Interop.cgroups.cs b/src/libraries/Common/src/Interop/Linux/cgroups/Interop.cgroups.cs
index 973d4c63573e70..513ed5306fa5a7 100644
--- a/src/libraries/Common/src/Interop/Linux/cgroups/Interop.cgroups.cs
+++ b/src/libraries/Common/src/Interop/Linux/cgroups/Interop.cgroups.cs
@@ -116,16 +116,24 @@ internal static bool TryGetMemoryLimitV1(out ulong limit)
/// The read limit, or 0 if it couldn't be read.
/// true if the limit was read successfully; otherwise, false.
internal static bool TryGetMemoryLimitV2(out ulong limit)
+ {
+ return TryGetMemoryLimitV2(s_cgroupMemoryPath, s_cgroupMemoryHierarchyMountPath, out limit);
+ }
+
+ /// Tries to read the memory limit from a cgroup v2 path hierarchy.
+ /// The current cgroup memory path.
+ /// The cgroup memory hierarchy mount path.
+ /// The read limit, or 0 if it couldn't be read.
+ /// true if the limit was read successfully; otherwise, false.
+ 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))
{
@@ -135,9 +143,13 @@ internal static bool TryGetMemoryLimitV2(out ulong limit)
minLimit = currentLevelLimit;
}
}
+ if (currentCGroupMemoryPath == cgroupMemoryHierarchyMountPath)
+ {
+ break;
+ }
+
currentCGroupMemoryPath = Path.GetDirectoryName(currentCGroupMemoryPath);
}
- while (currentCGroupMemoryPath!.Length != cgroupMemoryHierarchyMountPath.Length);
}
limit = minLimit;
@@ -145,6 +157,18 @@ internal static bool TryGetMemoryLimitV2(out ulong limit)
return foundAnyLimit;
}
+ private static bool IsPathAtOrBelowMount(string path, string mount)
+ {
+ if (!path.StartsWith(mount, StringComparison.Ordinal))
+ {
+ return false;
+ }
+
+ return path.Length == mount.Length ||
+ mount == "/" ||
+ path[mount.Length] == '/';
+ }
+
/// Tries to parse a memory limit from the specified file.
/// The path to the file to parse.
/// The parsed result, or 0 if it couldn't be parsed.
diff --git a/src/libraries/Common/tests/Tests/Interop/cgroupsTests.cs b/src/libraries/Common/tests/Tests/Interop/cgroupsTests.cs
index 3ad0b3cee4d150..0d05a6b11b9d70 100644
--- a/src/libraries/Common/tests/Tests/Interop/cgroupsTests.cs
+++ b/src/libraries/Common/tests/Tests/Interop/cgroupsTests.cs
@@ -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")]