This is an automated email from the ASF dual-hosted git repository.

bmahler pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mesos.git


The following commit(s) were added to refs/heads/master by this push:
     new aed4041fd  [cgroups2] Introduced an interface to set a hard memory 
limit.
aed4041fd is described below

commit aed4041fda2d605f1714f8a49ef34608356bab55
Author: Devin Leamy <dle...@twitter.com>
AuthorDate: Mon Apr 15 15:53:51 2024 -0400

     [cgroups2] Introduced an interface to set a hard memory limit.
    
    The "memory.max" control contains the hard memory limit that a cgroup
    and its descendants must remain below.
    
    We introduce `cgroups2::memory::set_max` and `cgroups2::memory::max`
    to set and get this limit.
    
    This closes #557
---
 src/linux/cgroups2.cpp                     | 44 ++++++++++++++++++++++++++++++
 src/linux/cgroups2.hpp                     | 14 ++++++++++
 src/tests/containerizer/cgroups2_tests.cpp | 21 ++++++++++++++
 3 files changed, 79 insertions(+)

diff --git a/src/linux/cgroups2.cpp b/src/linux/cgroups2.cpp
index e70e80780..f9912564b 100644
--- a/src/linux/cgroups2.cpp
+++ b/src/linux/cgroups2.cpp
@@ -769,9 +769,33 @@ Try<cpu::BandwidthLimit> max(const string& cgroup)
 
 namespace memory {
 
+namespace internal {
+
+// Parse a byte limit from a string.
+//
+// Format: "max" OR a u64_t string representing bytes.
+Result<Bytes> parse_bytelimit(const string& value)
+{
+  const string trimmed = strings::trim(value);
+  if (trimmed == "max") {
+    return None();
+  }
+
+  Try<uint64_t> bytes = numify<uint64_t>(trimmed);
+  if (bytes.isError()) {
+    return Error("Failed to numify '" + trimmed + "': " + bytes.error());
+  }
+
+  return Bytes(*bytes);
+}
+
+} // namespace internal {
+
+
 namespace control {
 
 const string CURRENT = "memory.current";
+const string MAX = "memory.max";
 const string MIN = "memory.min";
 
 } // namespace control {
@@ -804,6 +828,26 @@ Try<Bytes> min(const string& cgroup)
   return Bytes(*contents);
 }
 
+
+Try<Nothing> set_max(const string& cgroup, const Option<Bytes>& limit)
+{
+  return cgroups2::write(
+      cgroup,
+      control::MAX,
+      limit.isNone() ?  "max" : stringify(limit->bytes()));
+}
+
+
+Result<Bytes> max(const string& cgroup)
+{
+  Try<string> contents = cgroups2::read<string>(cgroup, control::MAX);
+  if (contents.isError()) {
+    return Error("Failed to read 'memory.max': " + contents.error());
+  }
+
+  return internal::parse_bytelimit(*contents);
+}
+
 } // namespace memory {
 
 namespace devices {
diff --git a/src/linux/cgroups2.hpp b/src/linux/cgroups2.hpp
index efd37dc11..e87ea0113 100644
--- a/src/linux/cgroups2.hpp
+++ b/src/linux/cgroups2.hpp
@@ -261,6 +261,20 @@ Try<Nothing> set_min(const std::string& cgroup, const 
Bytes& bytes);
 // Cannot be used for the root cgroup.
 Try<Bytes> min(const std::string& cgroup);
 
+
+// Set the maximum memory that can be used by a cgroup and its descendants.
+// If the limit is reached and memory cannot be reclaimed, then the OOM
+// killer will be invoked on the container.
+// If limit is None, then there is no maximum memory limit.
+// Cannot be used for the root cgroup.
+Try<Nothing> set_max(const std::string& cgroup, const Option<Bytes>& limit);
+
+
+// Get the maximum memory that can be used by a cgroup and its descendants.
+// If the returned limit is None, then there is no maximum memory limit.
+// Cannot be used for the root cgroup.
+Result<Bytes> max(const std::string& cgroup);
+
 } // namespace memory {
 
 namespace devices {
diff --git a/src/tests/containerizer/cgroups2_tests.cpp 
b/src/tests/containerizer/cgroups2_tests.cpp
index 6da6f7d1c..221f612a8 100644
--- a/src/tests/containerizer/cgroups2_tests.cpp
+++ b/src/tests/containerizer/cgroups2_tests.cpp
@@ -344,6 +344,27 @@ TEST_F(Cgroups2Test, ROOT_CGROUPS2_MemoryMinimum)
 }
 
 
+TEST_F(Cgroups2Test, ROOT_CGROUPS2_MemoryMaximum)
+{
+  ASSERT_SOME(enable_controllers({"memory"}));
+
+  ASSERT_SOME(cgroups2::create(TEST_CGROUP));
+  ASSERT_SOME(cgroups2::controllers::enable(TEST_CGROUP, {"memory"}));
+
+  Bytes limit = Bytes(os::pagesize()) * 5;
+
+  // Does not exist for the root cgroup.
+  EXPECT_ERROR(cgroups2::memory::max(cgroups2::ROOT_CGROUP));
+  EXPECT_ERROR(cgroups2::memory::set_max(cgroups2::ROOT_CGROUP, limit));
+
+  EXPECT_SOME(cgroups2::memory::set_max(TEST_CGROUP, limit));
+  EXPECT_SOME_EQ(limit, cgroups2::memory::max(TEST_CGROUP));
+
+  EXPECT_SOME(cgroups2::memory::set_max(TEST_CGROUP, None()));
+  EXPECT_NONE(cgroups2::memory::max(TEST_CGROUP));
+}
+
+
 // Check that byte amounts written to the memory controller are rounded
 // down to the nearest page size.
 TEST_F(Cgroups2Test, ROOT_CGROUPS2_MemoryBytesRounding)

Reply via email to