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

commit 515611f1c825c2a7f85bf246c681e9bd123f8614
Author: Devin Leamy <[email protected]>
AuthorDate: Wed Feb 28 12:25:39 2024 -0500

    [cgroups2] List the available subsystems in a cgroup.
    
    Creates an interface to "cgroups.controllers" to obtain a set of
    the subsystems available on a host.
    
    Review: https://reviews.apache.org/r/74875/
---
 src/linux/cgroups2.cpp                     | 122 ++++++++++++++++++++++++++++-
 src/linux/cgroups2.hpp                     |  18 +++++
 src/tests/containerizer/cgroups2_tests.cpp |  30 +++++++
 3 files changed, 169 insertions(+), 1 deletion(-)

diff --git a/src/linux/cgroups2.cpp b/src/linux/cgroups2.cpp
index 0a1da2cb4..ccf01ccbb 100644
--- a/src/linux/cgroups2.cpp
+++ b/src/linux/cgroups2.cpp
@@ -14,16 +14,25 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <iterator>
+#include <ostream>
+#include <set>
 #include <string>
+#include <vector>
 
 #include <stout/os.hpp>
 #include <stout/path.hpp>
+#include <stout/stringify.hpp>
 #include <stout/try.hpp>
 
 #include "linux/cgroups2.hpp"
 #include "linux/fs.hpp"
 
+using std::ostream;
+using std::set;
 using std::string;
+using std::vector;
+
 using mesos::internal::fs::MountTable;
 
 namespace cgroups2 {
@@ -55,6 +64,76 @@ const std::string TYPE = "cgroup.type";
 } // namespace control {
 
 
+namespace subtree_control {
+
+struct State
+{
+  State() = default;
+
+  // We don't return errors here because enabling something
+  // unknown will fail when writing it back out.
+  void enable(const vector<string>& subsystems)
+  {
+    foreach(const string& subsystem, subsystems) {
+      enable(subsystem);
+    }
+  }
+
+  // We don't return errors here because enabling something
+  // unknown will fail when writing it back out.
+  void enable(const string& subsystem)
+  {
+    _disabled.erase(subsystem);
+    _enabled.insert(subsystem);
+  }
+
+  // We don't return errors here since disabling something
+  // unknown will fail when writing it back out.
+  void disable(const string& subsystem)
+  {
+    _enabled.erase(subsystem);
+    _disabled.insert(subsystem);
+  }
+
+  set<string> enabled()  const { return _enabled; }
+  set<string> disabled() const { return _disabled; }
+
+  bool enabled(const string& subsystem) const
+  {
+    return _enabled.find(subsystem) != _enabled.end();
+  }
+
+  static State parse(const string& contents)
+  {
+    State control;
+    vector<string> subsystems = strings::split(contents, " ");
+    control._enabled.insert(
+        std::make_move_iterator(subsystems.begin()),
+        std::make_move_iterator(subsystems.end()));
+    return control;
+  }
+
+private:
+  set<string> _enabled;
+  set<string> _disabled;
+};
+
+
+std::ostream& operator<<(std::ostream& stream, const State& state)
+{
+  foreach(const string& system, state.enabled()) {
+    stream << "+" << system << " ";
+  }
+  foreach(const string& system, state.disabled()) {
+    stream << "-" << system << " ";
+  }
+  return stream;
+}
+
+} // namespace subtree_control {
+
+
+
 Try<string> read(const string& cgroup, const string& control)
 {
   return os::read(path::join(
@@ -163,4 +242,45 @@ Try<Nothing> unmount()
   }
 }
 
-} // namespace cgroups2
+namespace subsystems {
+
+Try<set<string>> available(const string& cgroup)
+{
+  Try<string> contents = cgroups2::read(
+      cgroup,
+      cgroups2::control::CONTROLLERS);
+
+  if (contents.isError()) {
+    return Error("Failed to read cgroup.controllers in"
+                 " '" + cgroup + "': " + contents.error());
+  }
+
+  vector<string> subsystems = strings::split(*contents, " ");
+  return set<string>(std::make_move_iterator(subsystems.begin()),
+                     std::make_move_iterator(subsystems.end()));
+}
+
+
+Try<Nothing> enable(
+    const string& cgroup,
+    const vector<string>& subsystems)
+{
+  Try<string> contents = cgroups2::read(
+      cgroup,
+      cgroups2::control::SUBTREE_CONTROLLERS);
+
+  if (contents.isError()) {
+    return Error(contents.error());
+  }
+
+  subtree_control::State control = subtree_control::State::parse(*contents);
+  control.enable(subsystems);
+  return cgroups2::write(
+      cgroup,
+      control::SUBTREE_CONTROLLERS,
+      stringify(control));
+}
+
+} // namespace subsystems {
+
+} // namespace cgroups2 {
diff --git a/src/linux/cgroups2.hpp b/src/linux/cgroups2.hpp
index 0c54e464b..e44009f40 100644
--- a/src/linux/cgroups2.hpp
+++ b/src/linux/cgroups2.hpp
@@ -22,6 +22,10 @@
 
 namespace cgroups2 {
 
+// Root cgroup in the cgroup v2 hierarchy. Since the root cgroup has the same
+// path as the root mount point its relative path is the empty string.
+const std::string ROOT_CGROUP = "";
+
 // Checks if cgroups2 is available on the system.
 bool enabled();
 
@@ -38,6 +42,20 @@ Try<bool> mounted();
 // responsibility of the caller to ensure all child cgroups have been 
destroyed.
 Try<Nothing> unmount();
 
+namespace subsystems {
+
+// Gets the subsystems that can be controlled by the provided cgroup.
+// Providing cgroups2::ROOT_CGROUP will yield the set of subsystems available
+// on the host.
+Try<std::set<std::string>> available(const std::string& cgroup);
+
+// Enables the given subsystems in the cgroup and disables all other 
subsystems.
+// Errors if a requested subsystem is not available.
+Try<Nothing> enable(
+  const std::string &cgroup,
+  const std::vector<std::string>& subsystems);
+
+} // namespace subsystems {
 } // namespace cgroups2
 
 #endif // __CGROUPS_V2_HPP__
diff --git a/src/tests/containerizer/cgroups2_tests.cpp 
b/src/tests/containerizer/cgroups2_tests.cpp
index 4981e9588..ca696d5fd 100644
--- a/src/tests/containerizer/cgroups2_tests.cpp
+++ b/src/tests/containerizer/cgroups2_tests.cpp
@@ -14,21 +14,51 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <set>
+#include <string>
+
 #include <stout/tests/utils.hpp>
+#include <stout/try.hpp>
 
 #include "linux/cgroups2.hpp"
 
+using std::set;
+using std::string;
+
 namespace mesos {
 namespace internal {
 namespace tests {
 
 class Cgroups2Test : public TemporaryDirectoryTest {};
 
+
 TEST_F(Cgroups2Test, ROOT_CGROUPS2_Enabled)
 {
   EXPECT_TRUE(cgroups2::enabled());
 }
 
+
+TEST_F(Cgroups2Test, ROOT_CGROUPS2_AvailableSubsystems)
+{
+
+  Try<bool> mounted = cgroups2::mounted();
+  ASSERT_SOME(mounted);
+
+  if (!*mounted) {
+    ASSERT_SOME(cgroups2::mount());
+  }
+
+  Try<set<string>> available = cgroups2::subsystems::available(
+    cgroups2::ROOT_CGROUP);
+
+  ASSERT_SOME(available);
+  EXPECT_TRUE(available->count("cpu") == 1);
+
+  if (!*mounted) {
+    EXPECT_SOME(cgroups2::unmount());
+  }
+}
+
 } // namespace tests {
 } // namespace internal {
 } // namespace mesos {

Reply via email to