diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp index 083068b54..298fd9f30 100644 --- a/fs_mgr/libsnapshot/snapuserd/Android.bp +++ b/fs_mgr/libsnapshot/snapuserd/Android.bp @@ -85,9 +85,9 @@ cc_library_static { "libsnapshot_cow", "liburing", "libprocessgroup", - "libprocessgroup_util", "libjsoncpp", "libcgrouprc", + "libcgrouprc_format", ], include_dirs: ["bionic/libc/kernel"], export_include_dirs: ["include"], @@ -129,9 +129,9 @@ cc_defaults { "libsnapshot_cow", "libsnapuserd", "libprocessgroup", - "libprocessgroup_util", "libjsoncpp", "libcgrouprc", + "libcgrouprc_format", "libsnapuserd_client", "libz", "liblz4", @@ -221,9 +221,9 @@ cc_defaults { "libsnapshot_cow", "libsnapuserd", "libprocessgroup", - "libprocessgroup_util", "libjsoncpp", "libcgrouprc", + "libcgrouprc_format", "liburing", "libz", ], @@ -319,6 +319,7 @@ cc_binary_host { "libprocessgroup", "libjsoncpp", "libcgrouprc", + "libcgrouprc_format", "liburing", "libz", ], diff --git a/init/Android.bp b/init/Android.bp index 8da2e7c2e..18a79d6c4 100644 --- a/init/Android.bp +++ b/init/Android.bp @@ -163,6 +163,7 @@ libinit_cc_defaults { "libavb", "libavf_cc_flags", "libbootloader_message", + "libcgrouprc_format", "liblmkd_utils", "liblz4", "libzstd", diff --git a/init/init.cpp b/init/init.cpp index 17498da6d..6c8089926 100644 --- a/init/init.cpp +++ b/init/init.cpp @@ -636,6 +636,9 @@ static Result SetupCgroupsAction(const BuiltinArguments&) { LOG(INFO) << "Cgroups support in kernel is not enabled"; return {}; } + // Have to create using make_dir function + // for appropriate sepolicy to be set for it + make_dir(android::base::Dirname(CGROUPS_RC_PATH), 0711); if (!CgroupSetup()) { return ErrnoError() << "Failed to setup cgroups"; } diff --git a/libcutils/Android.bp b/libcutils/Android.bp index fc4c571b7..3c3eeb663 100644 --- a/libcutils/Android.bp +++ b/libcutils/Android.bp @@ -301,7 +301,7 @@ cc_defaults { android: { static_executable: true, static_libs: [ - "libprocessgroup_util", + "libcgrouprc_format", ] + test_libraries + always_static_test_libraries, }, not_windows: { diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp index 8448a39a4..a60bfe973 100644 --- a/libprocessgroup/Android.bp +++ b/libprocessgroup/Android.bp @@ -17,7 +17,7 @@ soong_config_module_type { libprocessgroup_flag_aware_cc_defaults { name: "libprocessgroup_build_flags_cc", - cpp_std: "gnu++23", + cpp_std: "gnu++20", soong_config_variables: { memcg_v2_force_enabled: { cflags: [ @@ -75,6 +75,7 @@ cc_library { double_loadable: true, shared_libs: [ "libbase", + "libcgrouprc", ], static_libs: [ "libjsoncpp", @@ -115,6 +116,5 @@ cc_test { ], static_libs: [ "libgmock", - "libprocessgroup_util", ], } diff --git a/libprocessgroup/cgroup_map.cpp b/libprocessgroup/cgroup_map.cpp index 32bef13a1..fb01cfda9 100644 --- a/libprocessgroup/cgroup_map.cpp +++ b/libprocessgroup/cgroup_map.cpp @@ -25,10 +25,12 @@ #include #include #include +#include #include #include #include +using android::base::StartsWith; using android::base::StringPrintf; using android::base::WriteStringToFile; @@ -38,17 +40,17 @@ static constexpr const char* CGROUP_TASKS_FILE_V2 = "/cgroup.threads"; uint32_t CgroupControllerWrapper::version() const { CHECK(HasValue()); - return controller_->version(); + return ACgroupController_getVersion(controller_); } const char* CgroupControllerWrapper::name() const { CHECK(HasValue()); - return controller_->name(); + return ACgroupController_getName(controller_); } const char* CgroupControllerWrapper::path() const { CHECK(HasValue()); - return controller_->path(); + return ACgroupController_getPath(controller_); } bool CgroupControllerWrapper::HasValue() const { @@ -60,7 +62,7 @@ bool CgroupControllerWrapper::IsUsable() { if (state_ == UNKNOWN) { if (__builtin_available(android 30, *)) { - uint32_t flags = controller_->flags(); + uint32_t flags = ACgroupController_getFlags(controller_); state_ = (flags & CGROUPRC_CONTROLLER_FLAG_MOUNTED) != 0 ? USABLE : MISSING; } else { state_ = access(GetProcsFilePath("", 0, 0).c_str(), F_OK) == 0 ? USABLE : MISSING; @@ -127,8 +129,8 @@ bool CgroupControllerWrapper::GetTaskGroup(pid_t tid, std::string* group) const } CgroupMap::CgroupMap() { - if (!LoadDescriptors()) { - LOG(ERROR) << "CgroupMap::LoadDescriptors called for [" << getpid() << "] failed"; + if (!LoadRcFile()) { + LOG(ERROR) << "CgroupMap::LoadRcFile called for [" << getpid() << "] failed"; } } @@ -139,9 +141,9 @@ CgroupMap& CgroupMap::GetInstance() { return *instance; } -bool CgroupMap::LoadDescriptors() { +bool CgroupMap::LoadRcFile() { if (!loaded_) { - loaded_ = ReadDescriptors(&descriptors_); + loaded_ = (ACgroupFile_getVersion() != 0); } return loaded_; } @@ -149,30 +151,43 @@ bool CgroupMap::LoadDescriptors() { void CgroupMap::Print() const { if (!loaded_) { LOG(ERROR) << "CgroupMap::Print called for [" << getpid() - << "] failed, cgroups were not initialized properly"; + << "] failed, RC file was not initialized properly"; return; } - LOG(INFO) << "Controller count = " << descriptors_.size(); + LOG(INFO) << "File version = " << ACgroupFile_getVersion(); + LOG(INFO) << "File controller count = " << ACgroupFile_getControllerCount(); LOG(INFO) << "Mounted cgroups:"; - for (const auto& [name, descriptor] : descriptors_) { - LOG(INFO) << "\t" << descriptor.controller()->name() << " ver " - << descriptor.controller()->version() << " path " - << descriptor.controller()->path() << " flags " - << descriptor.controller()->flags(); + auto controller_count = ACgroupFile_getControllerCount(); + for (uint32_t i = 0; i < controller_count; ++i) { + const ACgroupController* controller = ACgroupFile_getController(i); + if (__builtin_available(android 30, *)) { + LOG(INFO) << "\t" << ACgroupController_getName(controller) << " ver " + << ACgroupController_getVersion(controller) << " path " + << ACgroupController_getPath(controller) << " flags " + << ACgroupController_getFlags(controller); + } else { + LOG(INFO) << "\t" << ACgroupController_getName(controller) << " ver " + << ACgroupController_getVersion(controller) << " path " + << ACgroupController_getPath(controller); + } } } CgroupControllerWrapper CgroupMap::FindController(const std::string& name) const { if (!loaded_) { LOG(ERROR) << "CgroupMap::FindController called for [" << getpid() - << "] failed, cgroups were not initialized properly"; + << "] failed, RC file was not initialized properly"; return CgroupControllerWrapper(nullptr); } - if (const auto it = descriptors_.find(name); it != descriptors_.end()) { - return CgroupControllerWrapper(it->second.controller()); + auto controller_count = ACgroupFile_getControllerCount(); + for (uint32_t i = 0; i < controller_count; ++i) { + const ACgroupController* controller = ACgroupFile_getController(i); + if (name == ACgroupController_getName(controller)) { + return CgroupControllerWrapper(controller); + } } return CgroupControllerWrapper(nullptr); @@ -181,19 +196,47 @@ CgroupControllerWrapper CgroupMap::FindController(const std::string& name) const CgroupControllerWrapper CgroupMap::FindControllerByPath(const std::string& path) const { if (!loaded_) { LOG(ERROR) << "CgroupMap::FindControllerByPath called for [" << getpid() - << "] failed, cgroups were not initialized properly"; + << "] failed, RC file was not initialized properly"; return CgroupControllerWrapper(nullptr); } - for (const auto& [name, descriptor] : descriptors_) { - if (path.starts_with(descriptor.controller()->path())) { - return CgroupControllerWrapper(descriptor.controller()); + auto controller_count = ACgroupFile_getControllerCount(); + for (uint32_t i = 0; i < controller_count; ++i) { + const ACgroupController* controller = ACgroupFile_getController(i); + if (StartsWith(path, ACgroupController_getPath(controller))) { + return CgroupControllerWrapper(controller); } } return CgroupControllerWrapper(nullptr); } -bool CgroupMap::ActivateControllers(const std::string& path) const { - return ::ActivateControllers(path, descriptors_); +int CgroupMap::ActivateControllers(const std::string& path) const { + if (__builtin_available(android 30, *)) { + auto controller_count = ACgroupFile_getControllerCount(); + for (uint32_t i = 0; i < controller_count; ++i) { + const ACgroupController* controller = ACgroupFile_getController(i); + const uint32_t flags = ACgroupController_getFlags(controller); + uint32_t max_activation_depth = UINT32_MAX; + if (__builtin_available(android 36, *)) { + max_activation_depth = ACgroupController_getMaxActivationDepth(controller); + } + const int depth = util::GetCgroupDepth(ACgroupController_getPath(controller), path); + + if (flags & CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION && depth < max_activation_depth) { + std::string str("+"); + str.append(ACgroupController_getName(controller)); + if (!WriteStringToFile(str, path + "/cgroup.subtree_control")) { + if (flags & CGROUPRC_CONTROLLER_FLAG_OPTIONAL) { + PLOG(WARNING) << "Activation of cgroup controller " << str + << " failed in path " << path; + } else { + return -errno; + } + } + } + } + return 0; + } + return -ENOSYS; } diff --git a/libprocessgroup/cgroup_map.h b/libprocessgroup/cgroup_map.h index fb9907645..364279414 100644 --- a/libprocessgroup/cgroup_map.h +++ b/libprocessgroup/cgroup_map.h @@ -18,17 +18,15 @@ #include -#include #include -#include -#include +#include -// Convenient wrapper of a CgroupController pointer. +// Convenient wrapper of an ACgroupController pointer. class CgroupControllerWrapper { public: // Does not own controller - explicit CgroupControllerWrapper(const CgroupController* controller) + explicit CgroupControllerWrapper(const ACgroupController* controller) : controller_(controller) {} uint32_t version() const; @@ -49,7 +47,7 @@ class CgroupControllerWrapper { MISSING = 2, }; - const CgroupController* controller_ = nullptr; // CgroupMap owns the object behind this pointer + const ACgroupController* controller_ = nullptr; ControllerState state_ = ControllerState::UNKNOWN; }; @@ -58,12 +56,11 @@ class CgroupMap { static CgroupMap& GetInstance(); CgroupControllerWrapper FindController(const std::string& name) const; CgroupControllerWrapper FindControllerByPath(const std::string& path) const; - bool ActivateControllers(const std::string& path) const; + int ActivateControllers(const std::string& path) const; private: bool loaded_ = false; - CgroupDescriptorMap descriptors_; CgroupMap(); - bool LoadDescriptors(); + bool LoadRcFile(); void Print() const; }; diff --git a/libprocessgroup/cgrouprc/Android.bp b/libprocessgroup/cgrouprc/Android.bp index 38b2fa31d..cb912476e 100644 --- a/libprocessgroup/cgrouprc/Android.bp +++ b/libprocessgroup/cgrouprc/Android.bp @@ -49,8 +49,7 @@ cc_library { "libbase", ], static_libs: [ - "libjsoncpp", - "libprocessgroup_util", + "libcgrouprc_format", ], stubs: { symbol_file: "libcgrouprc.map.txt", diff --git a/libprocessgroup/cgrouprc/a_cgroup_controller.cpp b/libprocessgroup/cgrouprc/a_cgroup_controller.cpp index 5a326e55d..889b3becf 100644 --- a/libprocessgroup/cgrouprc/a_cgroup_controller.cpp +++ b/libprocessgroup/cgrouprc/a_cgroup_controller.cpp @@ -32,6 +32,11 @@ uint32_t ACgroupController_getFlags(const ACgroupController* controller) { return controller->flags(); } +uint32_t ACgroupController_getMaxActivationDepth(const ACgroupController* controller) { + CHECK(controller != nullptr); + return controller->max_activation_depth(); +} + const char* ACgroupController_getName(const ACgroupController* controller) { CHECK(controller != nullptr); return controller->name(); diff --git a/libprocessgroup/cgrouprc/a_cgroup_file.cpp b/libprocessgroup/cgrouprc/a_cgroup_file.cpp index 33c8376f4..e26d84114 100644 --- a/libprocessgroup/cgrouprc/a_cgroup_file.cpp +++ b/libprocessgroup/cgrouprc/a_cgroup_file.cpp @@ -14,51 +14,93 @@ * limitations under the License. */ -#include +#include +#include + +#include #include +#include +#include #include -#include +#include #include "cgrouprc_internal.h" -static CgroupDescriptorMap* LoadDescriptors() { - CgroupDescriptorMap* descriptors = new CgroupDescriptorMap; - if (!ReadDescriptors(descriptors)) { - LOG(ERROR) << "Failed to load cgroup description file"; +using android::base::StringPrintf; +using android::base::unique_fd; + +using android::cgrouprc::format::CgroupController; +using android::cgrouprc::format::CgroupFile; + +static CgroupFile* LoadRcFile() { + struct stat sb; + + unique_fd fd(TEMP_FAILURE_RETRY(open(CGROUPS_RC_PATH, O_RDONLY | O_CLOEXEC))); + if (fd < 0) { + PLOG(ERROR) << "open() failed for " << CGROUPS_RC_PATH; return nullptr; } - return descriptors; + + if (fstat(fd, &sb) < 0) { + PLOG(ERROR) << "fstat() failed for " << CGROUPS_RC_PATH; + return nullptr; + } + + size_t file_size = sb.st_size; + if (file_size < sizeof(CgroupFile)) { + LOG(ERROR) << "Invalid file format " << CGROUPS_RC_PATH; + return nullptr; + } + + CgroupFile* file_data = (CgroupFile*)mmap(nullptr, file_size, PROT_READ, MAP_SHARED, fd, 0); + if (file_data == MAP_FAILED) { + PLOG(ERROR) << "Failed to mmap " << CGROUPS_RC_PATH; + return nullptr; + } + + if (file_data->version_ != CgroupFile::FILE_CURR_VERSION) { + LOG(ERROR) << CGROUPS_RC_PATH << " file version mismatch"; + munmap(file_data, file_size); + return nullptr; + } + + auto expected = sizeof(CgroupFile) + file_data->controller_count_ * sizeof(CgroupController); + if (file_size != expected) { + LOG(ERROR) << CGROUPS_RC_PATH << " file has invalid size, expected " << expected + << ", actual " << file_size; + munmap(file_data, file_size); + return nullptr; + } + + return file_data; } -static const CgroupDescriptorMap* GetInstance() { +static CgroupFile* GetInstance() { // Deliberately leak this object (not munmap) to avoid a race between destruction on // process exit and concurrent access from another thread. - static const CgroupDescriptorMap* descriptors = LoadDescriptors(); - return descriptors; + static auto* file = LoadRcFile(); + return file; } uint32_t ACgroupFile_getVersion() { - static constexpr uint32_t FILE_VERSION_1 = 1; - auto descriptors = GetInstance(); - if (descriptors == nullptr) return 0; - // There has only ever been one version, and there will be no more since cgroup.rc is no more - return FILE_VERSION_1; + auto file = GetInstance(); + if (file == nullptr) return 0; + return file->version_; } uint32_t ACgroupFile_getControllerCount() { - auto descriptors = GetInstance(); - if (descriptors == nullptr) return 0; - return descriptors->size(); + auto file = GetInstance(); + if (file == nullptr) return 0; + return file->controller_count_; } const ACgroupController* ACgroupFile_getController(uint32_t index) { - auto descriptors = GetInstance(); - if (descriptors == nullptr) return nullptr; - CHECK(index < descriptors->size()); + auto file = GetInstance(); + if (file == nullptr) return nullptr; + CHECK(index < file->controller_count_); // Although the object is not actually an ACgroupController object, all ACgroupController_* // functions implicitly convert ACgroupController* back to CgroupController* before invoking // member functions. - const CgroupController* p = std::next(descriptors->begin(), index)->second.controller(); - return static_cast(p); + return static_cast(&file->controllers_[index]); } diff --git a/libprocessgroup/cgrouprc/cgrouprc_internal.h b/libprocessgroup/cgrouprc/cgrouprc_internal.h index d51770346..cd02f0304 100644 --- a/libprocessgroup/cgrouprc/cgrouprc_internal.h +++ b/libprocessgroup/cgrouprc/cgrouprc_internal.h @@ -16,6 +16,9 @@ #pragma once -#include +#include -struct ACgroupController : CgroupController {}; +#include +#include + +struct ACgroupController : android::cgrouprc::format::CgroupController {}; diff --git a/libprocessgroup/cgrouprc/include/android/cgrouprc.h b/libprocessgroup/cgrouprc/include/android/cgrouprc.h index e704a36aa..3a57df547 100644 --- a/libprocessgroup/cgrouprc/include/android/cgrouprc.h +++ b/libprocessgroup/cgrouprc/include/android/cgrouprc.h @@ -78,6 +78,14 @@ __attribute__((warn_unused_result)) uint32_t ACgroupController_getVersion(const __attribute__((warn_unused_result, weak)) uint32_t ACgroupController_getFlags( const ACgroupController*) __INTRODUCED_IN(30); +/** + * Returns the maximum activation depth of the given controller. + * Only applicable to cgroup v2 controllers. + * Returns UINT32_MAX if no maximum activation depth is set. + */ +__attribute__((warn_unused_result, weak)) uint32_t ACgroupController_getMaxActivationDepth( + const ACgroupController* controller) __INTRODUCED_IN(36); + /** * Returns the name of the given controller. * If the given controller is null, return nullptr. diff --git a/libprocessgroup/cgrouprc/libcgrouprc.map.txt b/libprocessgroup/cgrouprc/libcgrouprc.map.txt index b62b10f3b..30bd25f18 100644 --- a/libprocessgroup/cgrouprc/libcgrouprc.map.txt +++ b/libprocessgroup/cgrouprc/libcgrouprc.map.txt @@ -16,3 +16,10 @@ LIBCGROUPRC_30 { # introduced=30 local: *; }; + +LIBCGROUPRC_36 { # introduced=36 + global: + ACgroupController_getMaxActivationDepth; # llndk=202504 systemapi + local: + *; +}; diff --git a/libprocessgroup/cgrouprc_format/Android.bp b/libprocessgroup/cgrouprc_format/Android.bp index 6f9ab3e73..059092419 100644 --- a/libprocessgroup/cgrouprc_format/Android.bp +++ b/libprocessgroup/cgrouprc_format/Android.bp @@ -23,4 +23,17 @@ cc_library_static { vendor_ramdisk_available: true, recovery_available: true, native_bridge_supported: true, + srcs: [ + "cgroup_controller.cpp", + ], + cflags: [ + "-Wall", + "-Werror", + ], + export_include_dirs: [ + "include", + ], + shared_libs: [ + "libbase", + ], } diff --git a/libprocessgroup/util/cgroup_controller.cpp b/libprocessgroup/cgrouprc_format/cgroup_controller.cpp similarity index 90% rename from libprocessgroup/util/cgroup_controller.cpp rename to libprocessgroup/cgrouprc_format/cgroup_controller.cpp index fb4168075..0dd909a29 100644 --- a/libprocessgroup/util/cgroup_controller.cpp +++ b/libprocessgroup/cgrouprc_format/cgroup_controller.cpp @@ -14,9 +14,11 @@ * limitations under the License. */ -#include +#include -#include +namespace android { +namespace cgrouprc { +namespace format { CgroupController::CgroupController(uint32_t version, uint32_t flags, const std::string& name, const std::string& path, uint32_t max_activation_depth) @@ -52,4 +54,8 @@ const char* CgroupController::path() const { void CgroupController::set_flags(uint32_t flags) { flags_ = flags; -} \ No newline at end of file +} + +} // namespace format +} // namespace cgrouprc +} // namespace android diff --git a/libprocessgroup/util/include/processgroup/cgroup_controller.h b/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_controller.h similarity index 87% rename from libprocessgroup/util/include/processgroup/cgroup_controller.h rename to libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_controller.h index fe6a829a0..c0c1f6034 100644 --- a/libprocessgroup/util/include/processgroup/cgroup_controller.h +++ b/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_controller.h @@ -20,7 +20,11 @@ #include #include -// Minimal controller description +namespace android { +namespace cgrouprc { +namespace format { + +// Minimal controller description to be mmapped into process address space struct CgroupController { public: CgroupController() = default; @@ -44,4 +48,8 @@ struct CgroupController { uint32_t max_activation_depth_ = UINT32_MAX; char name_[CGROUP_NAME_BUF_SZ] = {}; char path_[CGROUP_PATH_BUF_SZ] = {}; -}; \ No newline at end of file +}; + +} // namespace format +} // namespace cgrouprc +} // namespace android diff --git a/libprocessgroup/internal.h b/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_file.h similarity index 54% rename from libprocessgroup/internal.h rename to libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_file.h index ef855790d..2d9786fe6 100644 --- a/libprocessgroup/internal.h +++ b/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_file.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2024 The Android Open Source Project + * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,23 @@ #pragma once -#include +#include -static const std::string CGROUP_V2_ROOT_DEFAULT = "/sys/fs/cgroup"; \ No newline at end of file +#include + +namespace android { +namespace cgrouprc { +namespace format { + +struct CgroupFile { + uint32_t version_; + uint32_t controller_count_; + CgroupController controllers_[]; + + static constexpr uint32_t FILE_VERSION_1 = 1; + static constexpr uint32_t FILE_CURR_VERSION = FILE_VERSION_1; +}; + +} // namespace format +} // namespace cgrouprc +} // namespace android diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h index 805775744..ffffeb48b 100644 --- a/libprocessgroup/include/processgroup/processgroup.h +++ b/libprocessgroup/include/processgroup/processgroup.h @@ -57,7 +57,7 @@ __BEGIN_DECLS bool SetProcessProfilesCached(uid_t uid, pid_t pid, const std::vector& profiles); -[[deprecated]] static constexpr const char* CGROUPS_RC_PATH = "/dev/cgroup_info/cgroup.rc"; +static constexpr const char* CGROUPS_RC_PATH = "/dev/cgroup_info/cgroup.rc"; bool UsePerAppMemcg(); diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp index d3719eea3..83a2258bf 100644 --- a/libprocessgroup/processgroup.cpp +++ b/libprocessgroup/processgroup.cpp @@ -662,9 +662,10 @@ static int createProcessGroupInternal(uid_t uid, pid_t initialPid, std::string c return -errno; } if (activate_controllers) { - if (!CgroupMap::GetInstance().ActivateControllers(uid_path)) { - PLOG(ERROR) << "Failed to activate controllers in " << uid_path; - return -errno; + ret = CgroupMap::GetInstance().ActivateControllers(uid_path); + if (ret) { + LOG(ERROR) << "Failed to activate controllers in " << uid_path; + return ret; } } diff --git a/libprocessgroup/setup/Android.bp b/libprocessgroup/setup/Android.bp index cc6c67cf4..1a4ad0118 100644 --- a/libprocessgroup/setup/Android.bp +++ b/libprocessgroup/setup/Android.bp @@ -33,6 +33,7 @@ cc_library_shared { "libjsoncpp", ], static_libs: [ + "libcgrouprc_format", "libprocessgroup_util", ], header_libs: [ diff --git a/libprocessgroup/setup/cgroup_descriptor.h b/libprocessgroup/setup/cgroup_descriptor.h index 1afd2ee9c..06ce186fd 100644 --- a/libprocessgroup/setup/cgroup_descriptor.h +++ b/libprocessgroup/setup/cgroup_descriptor.h @@ -21,7 +21,10 @@ #include -#include +#include + +namespace android { +namespace cgrouprc { // Complete controller description for mounting cgroups class CgroupDescriptor { @@ -30,7 +33,7 @@ class CgroupDescriptor { mode_t mode, const std::string& uid, const std::string& gid, uint32_t flags, uint32_t max_activation_depth); - const CgroupController* controller() const { return &controller_; } + const format::CgroupController* controller() const { return &controller_; } mode_t mode() const { return mode_; } std::string uid() const { return uid_; } std::string gid() const { return gid_; } @@ -38,8 +41,11 @@ class CgroupDescriptor { void set_mounted(bool mounted); private: - CgroupController controller_; + format::CgroupController controller_; mode_t mode_ = 0; std::string uid_; std::string gid_; }; + +} // namespace cgrouprc +} // namespace android diff --git a/libprocessgroup/setup/cgroup_map_write.cpp b/libprocessgroup/setup/cgroup_map_write.cpp index d05bf2408..bd4187475 100644 --- a/libprocessgroup/setup/cgroup_map_write.cpp +++ b/libprocessgroup/setup/cgroup_map_write.cpp @@ -22,28 +22,45 @@ #include #include #include +#include #include #include #include +#include #include #include #include #include -#include +#include +#include +#include +#include +#include +#include +#include #include #include #include #include "../build_flags.h" -#include "../internal.h" +#include "cgroup_descriptor.h" + +using android::base::GetUintProperty; +using android::base::StringPrintf; +using android::base::unique_fd; + +namespace android { +namespace cgrouprc { static constexpr const char* CGROUPS_DESC_FILE = "/etc/cgroups.json"; static constexpr const char* CGROUPS_DESC_VENDOR_FILE = "/vendor/etc/cgroups.json"; static constexpr const char* TEMPLATE_CGROUPS_DESC_API_FILE = "/etc/task_profiles/cgroups_%u.json"; +static const std::string CGROUP_V2_ROOT_DEFAULT = "/sys/fs/cgroup"; + static bool ChangeDirModeAndOwner(const std::string& path, mode_t mode, const std::string& uid, const std::string& gid, bool permissive_mode = false) { uid_t pw_uid = -1; @@ -131,15 +148,149 @@ static bool Mkdir(const std::string& path, mode_t mode, const std::string& uid, return true; } +static void MergeCgroupToDescriptors(std::map* descriptors, + const Json::Value& cgroup, const std::string& name, + const std::string& root_path, int cgroups_version) { + const std::string cgroup_path = cgroup["Path"].asString(); + std::string path; + + if (!root_path.empty()) { + path = root_path; + if (cgroup_path != ".") { + path += "/"; + path += cgroup_path; + } + } else { + path = cgroup_path; + } + + uint32_t controller_flags = 0; + + if (cgroup["NeedsActivation"].isBool() && cgroup["NeedsActivation"].asBool()) { + controller_flags |= CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION; + } + + if (cgroup["Optional"].isBool() && cgroup["Optional"].asBool()) { + controller_flags |= CGROUPRC_CONTROLLER_FLAG_OPTIONAL; + } + + uint32_t max_activation_depth = UINT32_MAX; + if (cgroup.isMember("MaxActivationDepth")) { + max_activation_depth = cgroup["MaxActivationDepth"].asUInt(); + } + + CgroupDescriptor descriptor( + cgroups_version, name, path, std::strtoul(cgroup["Mode"].asString().c_str(), 0, 8), + cgroup["UID"].asString(), cgroup["GID"].asString(), controller_flags, + max_activation_depth); + + auto iter = descriptors->find(name); + if (iter == descriptors->end()) { + descriptors->emplace(name, descriptor); + } else { + iter->second = descriptor; + } +} + +static const bool force_memcg_v2 = android::libprocessgroup_flags::force_memcg_v2(); + +static bool ReadDescriptorsFromFile(const std::string& file_name, + std::map* descriptors) { + std::vector result; + std::string json_doc; + + if (!android::base::ReadFileToString(file_name, &json_doc)) { + PLOG(ERROR) << "Failed to read task profiles from " << file_name; + return false; + } + + Json::CharReaderBuilder builder; + std::unique_ptr reader(builder.newCharReader()); + Json::Value root; + std::string errorMessage; + if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) { + LOG(ERROR) << "Failed to parse cgroups description: " << errorMessage; + return false; + } + + if (root.isMember("Cgroups")) { + const Json::Value& cgroups = root["Cgroups"]; + for (Json::Value::ArrayIndex i = 0; i < cgroups.size(); ++i) { + std::string name = cgroups[i]["Controller"].asString(); + + if (force_memcg_v2 && name == "memory") continue; + + MergeCgroupToDescriptors(descriptors, cgroups[i], name, "", 1); + } + } + + bool memcgv2_present = false; + std::string root_path; + if (root.isMember("Cgroups2")) { + const Json::Value& cgroups2 = root["Cgroups2"]; + root_path = cgroups2["Path"].asString(); + MergeCgroupToDescriptors(descriptors, cgroups2, CGROUPV2_HIERARCHY_NAME, "", 2); + + const Json::Value& childGroups = cgroups2["Controllers"]; + for (Json::Value::ArrayIndex i = 0; i < childGroups.size(); ++i) { + std::string name = childGroups[i]["Controller"].asString(); + + if (force_memcg_v2 && name == "memory") memcgv2_present = true; + + MergeCgroupToDescriptors(descriptors, childGroups[i], name, root_path, 2); + } + } + + if (force_memcg_v2 && !memcgv2_present) { + LOG(INFO) << "Forcing memcg to v2 hierarchy"; + Json::Value memcgv2; + memcgv2["Controller"] = "memory"; + memcgv2["NeedsActivation"] = true; + memcgv2["Path"] = "."; + memcgv2["Optional"] = true; // In case of cgroup_disabled=memory, so we can still boot + MergeCgroupToDescriptors(descriptors, memcgv2, "memory", + root_path.empty() ? CGROUP_V2_ROOT_DEFAULT : root_path, 2); + } + + return true; +} + +static bool ReadDescriptors(std::map* descriptors) { + // load system cgroup descriptors + if (!ReadDescriptorsFromFile(CGROUPS_DESC_FILE, descriptors)) { + return false; + } + + // load API-level specific system cgroups descriptors if available + unsigned int api_level = GetUintProperty("ro.product.first_api_level", 0); + if (api_level > 0) { + std::string api_cgroups_path = + android::base::StringPrintf(TEMPLATE_CGROUPS_DESC_API_FILE, api_level); + if (!access(api_cgroups_path.c_str(), F_OK) || errno != ENOENT) { + if (!ReadDescriptorsFromFile(api_cgroups_path, descriptors)) { + return false; + } + } + } + + // load vendor cgroup descriptors if the file exists + if (!access(CGROUPS_DESC_VENDOR_FILE, F_OK) && + !ReadDescriptorsFromFile(CGROUPS_DESC_VENDOR_FILE, descriptors)) { + return false; + } + + return true; +} + // To avoid issues in sdk_mac build #if defined(__ANDROID__) -static bool IsOptionalController(const CgroupController* controller) { +static bool IsOptionalController(const format::CgroupController* controller) { return controller->flags() & CGROUPRC_CONTROLLER_FLAG_OPTIONAL; } static bool MountV2CgroupController(const CgroupDescriptor& descriptor) { - const CgroupController* controller = descriptor.controller(); + const format::CgroupController* controller = descriptor.controller(); // /sys/fs/cgroup is created by cgroup2 with specific selinux permissions, // try to create again in case the mount point is changed @@ -173,18 +324,36 @@ static bool MountV2CgroupController(const CgroupDescriptor& descriptor) { } static bool ActivateV2CgroupController(const CgroupDescriptor& descriptor) { - const CgroupController* controller = descriptor.controller(); + const format::CgroupController* controller = descriptor.controller(); if (!Mkdir(controller->path(), descriptor.mode(), descriptor.uid(), descriptor.gid())) { LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup"; return false; } - return ::ActivateControllers(controller->path(), {{controller->name(), descriptor}}); + if (controller->flags() & CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION && + controller->max_activation_depth() > 0) { + std::string str = "+"; + str += controller->name(); + std::string path = controller->path(); + path += "/cgroup.subtree_control"; + + if (!base::WriteStringToFile(str, path)) { + if (IsOptionalController(controller)) { + PLOG(INFO) << "Failed to activate optional controller " << controller->name() + << " at " << path; + return true; + } + PLOG(ERROR) << "Failed to activate controller " << controller->name(); + return false; + } + } + + return true; } static bool MountV1CgroupController(const CgroupDescriptor& descriptor) { - const CgroupController* controller = descriptor.controller(); + const format::CgroupController* controller = descriptor.controller(); // mkdir [mode] [owner] [group] if (!Mkdir(controller->path(), descriptor.mode(), descriptor.uid(), descriptor.gid())) { @@ -219,7 +388,7 @@ static bool MountV1CgroupController(const CgroupDescriptor& descriptor) { } static bool SetupCgroup(const CgroupDescriptor& descriptor) { - const CgroupController* controller = descriptor.controller(); + const format::CgroupController* controller = descriptor.controller(); if (controller->version() == 2) { if (!strcmp(controller->name(), CGROUPV2_HIERARCHY_NAME)) { @@ -241,6 +410,35 @@ static bool SetupCgroup(const CgroupDescriptor&) { #endif +static bool WriteRcFile(const std::map& descriptors) { + unique_fd fd(TEMP_FAILURE_RETRY(open(CGROUPS_RC_PATH, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, + S_IRUSR | S_IRGRP | S_IROTH))); + if (fd < 0) { + PLOG(ERROR) << "open() failed for " << CGROUPS_RC_PATH; + return false; + } + + format::CgroupFile fl; + fl.version_ = format::CgroupFile::FILE_CURR_VERSION; + fl.controller_count_ = descriptors.size(); + int ret = TEMP_FAILURE_RETRY(write(fd, &fl, sizeof(fl))); + if (ret < 0) { + PLOG(ERROR) << "write() failed for " << CGROUPS_RC_PATH; + return false; + } + + for (const auto& [name, descriptor] : descriptors) { + ret = TEMP_FAILURE_RETRY( + write(fd, descriptor.controller(), sizeof(format::CgroupController))); + if (ret < 0) { + PLOG(ERROR) << "write() failed for " << CGROUPS_RC_PATH; + return false; + } + } + + return true; +} + CgroupDescriptor::CgroupDescriptor(uint32_t version, const std::string& name, const std::string& path, mode_t mode, const std::string& uid, const std::string& gid, uint32_t flags, @@ -260,6 +458,9 @@ void CgroupDescriptor::set_mounted(bool mounted) { controller_.set_flags(flags); } +} // namespace cgrouprc +} // namespace android + static std::optional MGLRUDisabled() { const std::string file_name = "/sys/kernel/mm/lru_gen/enabled"; std::string content; @@ -271,8 +472,9 @@ static std::optional MGLRUDisabled() { return content == "0x0000"; } -static std::optional MEMCGDisabled(const CgroupDescriptorMap& descriptors) { - std::string cgroup_v2_root = CGROUP_V2_ROOT_DEFAULT; +static std::optional MEMCGDisabled( + const std::map& descriptors) { + std::string cgroup_v2_root = android::cgrouprc::CGROUP_V2_ROOT_DEFAULT; const auto it = descriptors.find(CGROUPV2_HIERARCHY_NAME); if (it == descriptors.end()) { LOG(WARNING) << "No Cgroups2 path found in cgroups.json. Vendor has modified Android, and " @@ -293,10 +495,14 @@ static std::optional MEMCGDisabled(const CgroupDescriptorMap& descriptors) return content.find("memory") == std::string::npos; } -static bool CreateV2SubHierarchy(const std::string& path, const CgroupDescriptorMap& descriptors) { +static bool CreateV2SubHierarchy( + const std::string& path, + const std::map& descriptors) { + using namespace android::cgrouprc; + const auto cgv2_iter = descriptors.find(CGROUPV2_HIERARCHY_NAME); if (cgv2_iter == descriptors.end()) return false; - const CgroupDescriptor cgv2_descriptor = cgv2_iter->second; + const android::cgrouprc::CgroupDescriptor cgv2_descriptor = cgv2_iter->second; if (!Mkdir(path, cgv2_descriptor.mode(), cgv2_descriptor.uid(), cgv2_descriptor.gid())) { PLOG(ERROR) << "Failed to create directory for " << path; @@ -305,17 +511,46 @@ static bool CreateV2SubHierarchy(const std::string& path, const CgroupDescriptor // Activate all v2 controllers in path so they can be activated in // children as they are created. - return ::ActivateControllers(path, descriptors); + for (const auto& [name, descriptor] : descriptors) { + const format::CgroupController* controller = descriptor.controller(); + std::uint32_t flags = controller->flags(); + std::uint32_t max_activation_depth = controller->max_activation_depth(); + const int depth = util::GetCgroupDepth(controller->path(), path); + + if (controller->version() == 2 && name != CGROUPV2_HIERARCHY_NAME && + flags & CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION && depth < max_activation_depth) { + std::string str("+"); + str += controller->name(); + if (!android::base::WriteStringToFile(str, path + "/cgroup.subtree_control")) { + if (flags & CGROUPRC_CONTROLLER_FLAG_OPTIONAL) { + PLOG(WARNING) << "Activation of cgroup controller " << str << " failed in path " + << path; + } else { + return false; + } + } + } + } + return true; } bool CgroupSetup() { - CgroupDescriptorMap descriptors; + using namespace android::cgrouprc; + + std::map descriptors; if (getpid() != 1) { LOG(ERROR) << "Cgroup setup can be done only by init process"; return false; } + // Make sure we do this only one time. No need for std::call_once because + // init is a single-threaded process + if (access(CGROUPS_RC_PATH, F_OK) == 0) { + LOG(WARNING) << "Attempt to call CgroupSetup() more than once"; + return true; + } + // load cgroups.json file if (!ReadDescriptors(&descriptors)) { LOG(ERROR) << "Failed to load cgroup description file"; @@ -324,18 +559,15 @@ bool CgroupSetup() { // setup cgroups for (auto& [name, descriptor] : descriptors) { - if (descriptor.controller()->flags() & CGROUPRC_CONTROLLER_FLAG_MOUNTED) { - LOG(WARNING) << "Attempt to call CgroupSetup() more than once"; - return true; - } - - if (!SetupCgroup(descriptor)) { + if (SetupCgroup(descriptor)) { + descriptor.set_mounted(true); + } else { // issue a warning and proceed with the next cgroup LOG(WARNING) << "Failed to setup " << name << " cgroup"; } } - if (android::libprocessgroup_flags::force_memcg_v2()) { + if (force_memcg_v2) { if (MGLRUDisabled().value_or(false)) { LOG(WARNING) << "Memcg forced to v2 hierarchy with MGLRU disabled! " << "Global reclaim performance will suffer."; @@ -361,5 +593,26 @@ bool CgroupSetup() { } } + // mkdir 0711 system system + if (!Mkdir(android::base::Dirname(CGROUPS_RC_PATH), 0711, "system", "system")) { + LOG(ERROR) << "Failed to create directory for " << CGROUPS_RC_PATH << " file"; + return false; + } + + // Generate file which can be directly mmapped into + // process memory. This optimizes performance, memory usage + // and limits infrormation shared with unprivileged processes + // to the minimum subset of information from cgroups.json + if (!WriteRcFile(descriptors)) { + LOG(ERROR) << "Failed to write " << CGROUPS_RC_PATH << " file"; + return false; + } + + // chmod 0644 + if (fchmodat(AT_FDCWD, CGROUPS_RC_PATH, 0644, AT_SYMLINK_NOFOLLOW) < 0) { + PLOG(ERROR) << "fchmodat() failed"; + return false; + } + return true; } diff --git a/libprocessgroup/util/Android.bp b/libprocessgroup/util/Android.bp index 1c74d4ed5..54ba69b4e 100644 --- a/libprocessgroup/util/Android.bp +++ b/libprocessgroup/util/Android.bp @@ -37,16 +37,8 @@ cc_library_static { "include", ], srcs: [ - "cgroup_controller.cpp", - "cgroup_descriptor.cpp", "util.cpp", ], - shared_libs: [ - "libbase", - ], - static_libs: [ - "libjsoncpp", - ], defaults: ["libprocessgroup_build_flags_cc"], } diff --git a/libprocessgroup/util/cgroup_descriptor.cpp b/libprocessgroup/util/cgroup_descriptor.cpp deleted file mode 100644 index 4d3347f34..000000000 --- a/libprocessgroup/util/cgroup_descriptor.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include // For flag values - -CgroupDescriptor::CgroupDescriptor(uint32_t version, const std::string& name, - const std::string& path, mode_t mode, const std::string& uid, - const std::string& gid, uint32_t flags, - uint32_t max_activation_depth) - : controller_(version, flags, name, path, max_activation_depth), - mode_(mode), - uid_(uid), - gid_(gid) {} - -void CgroupDescriptor::set_mounted(bool mounted) { - uint32_t flags = controller_.flags(); - if (mounted) { - flags |= CGROUPRC_CONTROLLER_FLAG_MOUNTED; - } else { - flags &= ~CGROUPRC_CONTROLLER_FLAG_MOUNTED; - } - controller_.set_flags(flags); -} diff --git a/libprocessgroup/util/include/processgroup/cgroup_descriptor.h b/libprocessgroup/util/include/processgroup/cgroup_descriptor.h deleted file mode 100644 index 1afd2ee9c..000000000 --- a/libprocessgroup/util/include/processgroup/cgroup_descriptor.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -#include - -#include - -// Complete controller description for mounting cgroups -class CgroupDescriptor { - public: - CgroupDescriptor(uint32_t version, const std::string& name, const std::string& path, - mode_t mode, const std::string& uid, const std::string& gid, uint32_t flags, - uint32_t max_activation_depth); - - const CgroupController* controller() const { return &controller_; } - mode_t mode() const { return mode_; } - std::string uid() const { return uid_; } - std::string gid() const { return gid_; } - - void set_mounted(bool mounted); - - private: - CgroupController controller_; - mode_t mode_ = 0; - std::string uid_; - std::string gid_; -}; diff --git a/libprocessgroup/util/include/processgroup/util.h b/libprocessgroup/util/include/processgroup/util.h index 2c7b32926..8d013af55 100644 --- a/libprocessgroup/util/include/processgroup/util.h +++ b/libprocessgroup/util/include/processgroup/util.h @@ -16,20 +16,10 @@ #pragma once -#include #include -#include "cgroup_descriptor.h" - -// Duplicated from cgrouprc.h. Don't depend on libcgrouprc here. -#define CGROUPRC_CONTROLLER_FLAG_MOUNTED 0x1 -#define CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION 0x2 -#define CGROUPRC_CONTROLLER_FLAG_OPTIONAL 0x4 +namespace util { unsigned int GetCgroupDepth(const std::string& controller_root, const std::string& cgroup_path); -using CgroupControllerName = std::string; -using CgroupDescriptorMap = std::map; -bool ReadDescriptors(CgroupDescriptorMap* descriptors); - -bool ActivateControllers(const std::string& path, const CgroupDescriptorMap& descriptors); +} // namespace util diff --git a/libprocessgroup/util/tests/util.cpp b/libprocessgroup/util/tests/util.cpp index 6caef8ee3..1de7d6f3f 100644 --- a/libprocessgroup/util/tests/util.cpp +++ b/libprocessgroup/util/tests/util.cpp @@ -18,6 +18,8 @@ #include "gtest/gtest.h" +using util::GetCgroupDepth; + TEST(EmptyInputs, bothEmpty) { EXPECT_EQ(GetCgroupDepth({}, {}), 0); } diff --git a/libprocessgroup/util/util.cpp b/libprocessgroup/util/util.cpp index 14016751c..9b88a223a 100644 --- a/libprocessgroup/util/util.cpp +++ b/libprocessgroup/util/util.cpp @@ -18,33 +18,9 @@ #include #include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include "../build_flags.h" -#include "../internal.h" - -using android::base::GetUintProperty; namespace { -constexpr const char* CGROUPS_DESC_FILE = "/etc/cgroups.json"; -constexpr const char* CGROUPS_DESC_VENDOR_FILE = "/vendor/etc/cgroups.json"; -constexpr const char* TEMPLATE_CGROUPS_DESC_API_FILE = "/etc/task_profiles/cgroups_%u.json"; - -// This should match the publicly declared value in processgroup.h, -// but we don't want this library to depend on libprocessgroup. -constexpr std::string CGROUPV2_HIERARCHY_NAME_INTERNAL = "cgroup2"; - const char SEP = '/'; std::string DeduplicateAndTrimSeparators(const std::string& path) { @@ -66,135 +42,9 @@ std::string DeduplicateAndTrimSeparators(const std::string& path) { return ret; } -void MergeCgroupToDescriptors(CgroupDescriptorMap* descriptors, const Json::Value& cgroup, - const std::string& name, const std::string& root_path, - int cgroups_version) { - const std::string cgroup_path = cgroup["Path"].asString(); - std::string path; - - if (!root_path.empty()) { - path = root_path; - if (cgroup_path != ".") { - path += "/"; - path += cgroup_path; - } - } else { - path = cgroup_path; - } - - uint32_t controller_flags = 0; - - if (cgroup["NeedsActivation"].isBool() && cgroup["NeedsActivation"].asBool()) { - controller_flags |= CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION; - } - - if (cgroup["Optional"].isBool() && cgroup["Optional"].asBool()) { - controller_flags |= CGROUPRC_CONTROLLER_FLAG_OPTIONAL; - } - - uint32_t max_activation_depth = UINT32_MAX; - if (cgroup.isMember("MaxActivationDepth")) { - max_activation_depth = cgroup["MaxActivationDepth"].asUInt(); - } - - CgroupDescriptor descriptor( - cgroups_version, name, path, std::strtoul(cgroup["Mode"].asString().c_str(), 0, 8), - cgroup["UID"].asString(), cgroup["GID"].asString(), controller_flags, - max_activation_depth); - - auto iter = descriptors->find(name); - if (iter == descriptors->end()) { - descriptors->emplace(name, descriptor); - } else { - iter->second = descriptor; - } -} - -bool ReadDescriptorsFromFile(const std::string& file_name, CgroupDescriptorMap* descriptors) { - static constexpr bool force_memcg_v2 = android::libprocessgroup_flags::force_memcg_v2(); - std::vector result; - std::string json_doc; - - if (!android::base::ReadFileToString(file_name, &json_doc)) { - PLOG(ERROR) << "Failed to read task profiles from " << file_name; - return false; - } - - Json::CharReaderBuilder builder; - std::unique_ptr reader(builder.newCharReader()); - Json::Value root; - std::string errorMessage; - if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) { - LOG(ERROR) << "Failed to parse cgroups description: " << errorMessage; - return false; - } - - if (root.isMember("Cgroups")) { - const Json::Value& cgroups = root["Cgroups"]; - for (Json::Value::ArrayIndex i = 0; i < cgroups.size(); ++i) { - std::string name = cgroups[i]["Controller"].asString(); - - if (force_memcg_v2 && name == "memory") continue; - - MergeCgroupToDescriptors(descriptors, cgroups[i], name, "", 1); - } - } - - bool memcgv2_present = false; - std::string root_path; - if (root.isMember("Cgroups2")) { - const Json::Value& cgroups2 = root["Cgroups2"]; - root_path = cgroups2["Path"].asString(); - MergeCgroupToDescriptors(descriptors, cgroups2, CGROUPV2_HIERARCHY_NAME_INTERNAL, "", 2); - - const Json::Value& childGroups = cgroups2["Controllers"]; - for (Json::Value::ArrayIndex i = 0; i < childGroups.size(); ++i) { - std::string name = childGroups[i]["Controller"].asString(); - - if (force_memcg_v2 && name == "memory") memcgv2_present = true; - - MergeCgroupToDescriptors(descriptors, childGroups[i], name, root_path, 2); - } - } - - if (force_memcg_v2 && !memcgv2_present) { - LOG(INFO) << "Forcing memcg to v2 hierarchy"; - Json::Value memcgv2; - memcgv2["Controller"] = "memory"; - memcgv2["NeedsActivation"] = true; - memcgv2["Path"] = "."; - memcgv2["Optional"] = true; // In case of cgroup_disabled=memory, so we can still boot - MergeCgroupToDescriptors(descriptors, memcgv2, "memory", - root_path.empty() ? CGROUP_V2_ROOT_DEFAULT : root_path, 2); - } - - return true; -} - -using MountDir = std::string; -using MountOpts = std::string; -static std::optional> ReadCgroupV1Mounts() { - FILE* fp = setmntent("/proc/mounts", "r"); - if (fp == nullptr) { - PLOG(ERROR) << "Failed to read mounts"; - return std::nullopt; - } - - std::map mounts; - const std::string_view CGROUP_V1_TYPE = "cgroup"; - for (mntent* mentry = getmntent(fp); mentry != nullptr; mentry = getmntent(fp)) { - if (mentry->mnt_type && CGROUP_V1_TYPE == mentry->mnt_type && - mentry->mnt_dir && mentry->mnt_opts) { - mounts[mentry->mnt_dir] = mentry->mnt_opts; - } - } - endmntent(fp); - - return mounts; -} - } // anonymous namespace +namespace util { unsigned int GetCgroupDepth(const std::string& controller_root, const std::string& cgroup_path) { const std::string deduped_root = DeduplicateAndTrimSeparators(controller_root); @@ -206,70 +56,4 @@ unsigned int GetCgroupDepth(const std::string& controller_root, const std::strin return std::count(deduped_path.begin() + deduped_root.size(), deduped_path.end(), SEP); } -bool ReadDescriptors(CgroupDescriptorMap* descriptors) { - // load system cgroup descriptors - if (!ReadDescriptorsFromFile(CGROUPS_DESC_FILE, descriptors)) { - return false; - } - - // load API-level specific system cgroups descriptors if available - unsigned int api_level = GetUintProperty("ro.product.first_api_level", 0); - if (api_level > 0) { - std::string api_cgroups_path = - android::base::StringPrintf(TEMPLATE_CGROUPS_DESC_API_FILE, api_level); - if (!access(api_cgroups_path.c_str(), F_OK) || errno != ENOENT) { - if (!ReadDescriptorsFromFile(api_cgroups_path, descriptors)) { - return false; - } - } - } - - // load vendor cgroup descriptors if the file exists - if (!access(CGROUPS_DESC_VENDOR_FILE, F_OK) && - !ReadDescriptorsFromFile(CGROUPS_DESC_VENDOR_FILE, descriptors)) { - return false; - } - - // check for v1 mount/usability status - std::optional> v1Mounts; - for (auto& [name, descriptor] : *descriptors) { - const CgroupController* const controller = descriptor.controller(); - - if (controller->version() != 1) continue; - - // Read only once, and only if we have at least one v1 controller - if (!v1Mounts) { - v1Mounts = ReadCgroupV1Mounts(); - if (!v1Mounts) return false; - } - - if (const auto it = v1Mounts->find(controller->path()); it != v1Mounts->end()) { - if (it->second.contains(controller->name())) descriptor.set_mounted(true); - } - } - - return true; -} - -bool ActivateControllers(const std::string& path, const CgroupDescriptorMap& descriptors) { - for (const auto& [name, descriptor] : descriptors) { - const uint32_t flags = descriptor.controller()->flags(); - const uint32_t max_activation_depth = descriptor.controller()->max_activation_depth(); - const unsigned int depth = GetCgroupDepth(descriptor.controller()->path(), path); - - if (flags & CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION && depth < max_activation_depth) { - std::string str("+"); - str.append(descriptor.controller()->name()); - if (!android::base::WriteStringToFile(str, path + "/cgroup.subtree_control")) { - if (flags & CGROUPRC_CONTROLLER_FLAG_OPTIONAL) { - PLOG(WARNING) << "Activation of cgroup controller " << str - << " failed in path " << path; - } else { - return false; - } - } - } - } - return true; -} - +} // namespace util