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)