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 {
