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 3642cf9f1 [cgroups2] Add a subset of memory usage statistics.
3642cf9f1 is described below
commit 3642cf9f1ff7e93db99973dab9552609a498e5c1
Author: Devin Leamy <[email protected]>
AuthorDate: Fri Apr 19 18:56:44 2024 -0400
[cgroups2] Add a subset of memory usage statistics.
Cgroups v2 exposes memory statistics through the 'memory.stat' control.
Here we introduce `cgroups2::memory::stats` to read a subset of the memory
usage statistics into a new `memory::Stats` object. These statistics will
be used by the `MemoryControllerProcess` to populate a `ResourceStatistics`
object, like is done by the `MemorySubsystemProcess` in cgroups v1.
Additional statistics from the 'memory.stat' control can be included as
they are required.
This closes #564
---
src/linux/cgroups2.cpp | 54 +++++++++++++++++++++++++++++-
src/linux/cgroups2.hpp | 45 +++++++++++++++++++++++++
src/tests/containerizer/cgroups2_tests.cpp | 10 ++++++
3 files changed, 108 insertions(+), 1 deletion(-)
diff --git a/src/linux/cgroups2.cpp b/src/linux/cgroups2.cpp
index 2dd197c4c..f309d604d 100644
--- a/src/linux/cgroups2.cpp
+++ b/src/linux/cgroups2.cpp
@@ -811,6 +811,48 @@ const string LOW = "memory.low";
const string HIGH = "memory.high";
const string MAX = "memory.max";
const string MIN = "memory.min";
+const string STAT = "memory.stat";
+
+namespace stat {
+
+Try<Stats> parse(const string& content)
+{
+ Stats stats;
+
+ foreach (const string& line, strings::split(content, "\n")) {
+ if (line.empty()) {
+ continue;
+ }
+
+ vector<string> tokens = strings::split(line, " ");
+ if (tokens.size() != 2) {
+ return Error("Invalid line format in 'memory.stat'; expected "
+ "<key> <value> received: '" + line + "'");
+ }
+
+ const string& key = tokens[0];
+ const string& value = tokens[1];
+
+ Try<uint64_t> n = numify<uint64_t>(value);
+ if (n.isError()) {
+ return Error("Failed to numify '" + value + "': " + n.error());
+ }
+ const Bytes bytes(*n);
+
+ if (key == "anon") { stats.anon = bytes; }
+ else if (key == "file") { stats.file = bytes; }
+ else if (key == "kernel") { stats.kernel = bytes; }
+ else if (key == "kernel_stack") { stats.kernel_stack = bytes; }
+ else if (key == "pagetables") { stats.pagetables = bytes; }
+ else if (key == "sock") { stats.sock = bytes; }
+ else if (key == "vmalloc") { stats.vmalloc = bytes; }
+ else if (key == "file_mapped") { stats.file_mapped = bytes; }
+ }
+
+ return stats;
+}
+
+} // namespace stat {
} // namespace control {
@@ -852,7 +894,6 @@ Try<Events> parse(const string& content)
} // namespace events {
-
Future<Nothing> oom(const string& cgroup)
{
// TODO(dleamy): Update this to use inotify, rather than polling.
@@ -964,6 +1005,17 @@ Result<Bytes> high(const string& cgroup)
return internal::parse_bytelimit(*contents);
}
+
+Try<Stats> stats(const string& cgroup)
+{
+ Try<string> contents = cgroups2::read<string>(cgroup, control::STAT);
+ if (contents.isError()) {
+ return Error("Failed to read 'memory.stat': " + contents.error());
+ }
+
+ return control::stat::parse(*contents);
+}
+
} // namespace memory {
namespace devices {
diff --git a/src/linux/cgroups2.hpp b/src/linux/cgroups2.hpp
index 91f8d65e6..7060573a4 100644
--- a/src/linux/cgroups2.hpp
+++ b/src/linux/cgroups2.hpp
@@ -243,6 +243,45 @@ Try<BandwidthLimit> max(const std::string& cgroup);
// See: https://docs.kernel.org/admin-guide/cgroup-v2.html
namespace memory {
+// Memory usage statistics.
+//
+// Snapshot of the 'memory.stat' control file.
+//
+// Note:
+// We only record a subset of the memory statistics; a complete list can be
+// found in the kernel documentation.
+// https://docs.kernel.org/admin-guide/cgroup-v2.html#memory-interface-files
+struct Stats
+{
+ // Amount of memory used in anonymous mappings such as brk(), sbrk(),
+ // and mmap(MAP_ANONYMOUS)
+ Bytes anon;
+
+ // Amount of memory used to cache filesystem data, including tmpfs and
+ // shared memory.
+ Bytes file;
+
+ // Amount of total kernel memory, including (kernel_stack, pagetables,
+ // percpu, vmalloc, slab) in addition to other kernel memory use cases.
+ Bytes kernel;
+
+ // Amount of memory allocated to kernel stacks.
+ Bytes kernel_stack;
+
+ // Amount of memory allocated for page tables.
+ Bytes pagetables;
+
+ // Amount of memory used in network transmission buffers.
+ Bytes sock;
+
+ // Amount of memory used for vmap backed memory.
+ Bytes vmalloc;
+
+ // Amount of cached filesystem data mapped with mmap().
+ Bytes file_mapped;
+};
+
+
// Cgroup memory controller events.
//
// Snapshot of the 'memory.events' or 'memory.local.events' control files.
@@ -358,6 +397,12 @@ Try<Nothing> set_high(
// Cannot be used for the root cgroup.
Result<Bytes> high(const std::string& cgroup);
+
+// Get the memory usage statistics for a cgroup.
+//
+// Cannot be used for the root cgroup.
+Try<Stats> stats(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 4cffa80f1..592c4bce6 100644
--- a/src/tests/containerizer/cgroups2_tests.cpp
+++ b/src/tests/containerizer/cgroups2_tests.cpp
@@ -331,6 +331,16 @@ TEST_F(Cgroups2Test, ROOT_CGROUPS2_MemoryUsage)
}
+TEST_F(Cgroups2Test, ROOT_CGROUPS2_MemoryStats)
+{
+ ASSERT_SOME(enable_controllers({"memory"}));
+
+ ASSERT_SOME(cgroups2::create(TEST_CGROUP));
+ ASSERT_SOME(cgroups2::controllers::enable(TEST_CGROUP, {"memory"}));
+ ASSERT_SOME(cgroups2::memory::stats(TEST_CGROUP));
+}
+
+
TEST_F(Cgroups2Test, ROOT_CGROUPS2_MemoryLow)
{
ASSERT_SOME(enable_controllers({"memory"}));