diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp index 298fd9f30..083068b54 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,7 +319,6 @@ cc_binary_host { "libprocessgroup", "libjsoncpp", "libcgrouprc", - "libcgrouprc_format", "liburing", "libz", ], diff --git a/init/Android.bp b/init/Android.bp index 18a79d6c4..8da2e7c2e 100644 --- a/init/Android.bp +++ b/init/Android.bp @@ -163,7 +163,6 @@ 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 6c8089926..17498da6d 100644 --- a/init/init.cpp +++ b/init/init.cpp @@ -636,9 +636,6 @@ 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 3c3eeb663..fc4c571b7 100644 --- a/libcutils/Android.bp +++ b/libcutils/Android.bp @@ -301,7 +301,7 @@ cc_defaults { android: { static_executable: true, static_libs: [ - "libcgrouprc_format", + "libprocessgroup_util", ] + test_libraries + always_static_test_libraries, }, not_windows: { diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp index a60bfe973..d623a1152 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++20", + cpp_std: "gnu++23", soong_config_variables: { memcg_v2_force_enabled: { cflags: [ @@ -116,5 +116,6 @@ cc_test { ], static_libs: [ "libgmock", + "libprocessgroup_util", ], } diff --git a/libprocessgroup/cgroup_map.cpp b/libprocessgroup/cgroup_map.cpp index fb01cfda9..aeccfe89b 100644 --- a/libprocessgroup/cgroup_map.cpp +++ b/libprocessgroup/cgroup_map.cpp @@ -151,7 +151,7 @@ bool CgroupMap::LoadRcFile() { void CgroupMap::Print() const { if (!loaded_) { LOG(ERROR) << "CgroupMap::Print called for [" << getpid() - << "] failed, RC file was not initialized properly"; + << "] failed, cgroups were not initialized properly"; return; } LOG(INFO) << "File version = " << ACgroupFile_getVersion(); @@ -221,7 +221,7 @@ int CgroupMap::ActivateControllers(const std::string& path) const { if (__builtin_available(android 36, *)) { max_activation_depth = ACgroupController_getMaxActivationDepth(controller); } - const int depth = util::GetCgroupDepth(ACgroupController_getPath(controller), path); + const int depth = GetCgroupDepth(ACgroupController_getPath(controller), path); if (flags & CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION && depth < max_activation_depth) { std::string str("+"); diff --git a/libprocessgroup/cgrouprc/Android.bp b/libprocessgroup/cgrouprc/Android.bp index cb912476e..38b2fa31d 100644 --- a/libprocessgroup/cgrouprc/Android.bp +++ b/libprocessgroup/cgrouprc/Android.bp @@ -49,7 +49,8 @@ cc_library { "libbase", ], static_libs: [ - "libcgrouprc_format", + "libjsoncpp", + "libprocessgroup_util", ], stubs: { symbol_file: "libcgrouprc.map.txt", diff --git a/libprocessgroup/cgrouprc/a_cgroup_file.cpp b/libprocessgroup/cgrouprc/a_cgroup_file.cpp index e26d84114..33c8376f4 100644 --- a/libprocessgroup/cgrouprc/a_cgroup_file.cpp +++ b/libprocessgroup/cgrouprc/a_cgroup_file.cpp @@ -14,93 +14,51 @@ * limitations under the License. */ -#include -#include - -#include +#include #include -#include -#include #include -#include +#include #include "cgrouprc_internal.h" -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; +static CgroupDescriptorMap* LoadDescriptors() { + CgroupDescriptorMap* descriptors = new CgroupDescriptorMap; + if (!ReadDescriptors(descriptors)) { + LOG(ERROR) << "Failed to load cgroup description file"; return nullptr; } - - 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; + return descriptors; } -static CgroupFile* GetInstance() { +static const CgroupDescriptorMap* GetInstance() { // Deliberately leak this object (not munmap) to avoid a race between destruction on // process exit and concurrent access from another thread. - static auto* file = LoadRcFile(); - return file; + static const CgroupDescriptorMap* descriptors = LoadDescriptors(); + return descriptors; } uint32_t ACgroupFile_getVersion() { - auto file = GetInstance(); - if (file == nullptr) return 0; - return file->version_; + 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; } uint32_t ACgroupFile_getControllerCount() { - auto file = GetInstance(); - if (file == nullptr) return 0; - return file->controller_count_; + auto descriptors = GetInstance(); + if (descriptors == nullptr) return 0; + return descriptors->size(); } const ACgroupController* ACgroupFile_getController(uint32_t index) { - auto file = GetInstance(); - if (file == nullptr) return nullptr; - CHECK(index < file->controller_count_); + auto descriptors = GetInstance(); + if (descriptors == nullptr) return nullptr; + CHECK(index < descriptors->size()); // Although the object is not actually an ACgroupController object, all ACgroupController_* // functions implicitly convert ACgroupController* back to CgroupController* before invoking // member functions. - return static_cast(&file->controllers_[index]); + const CgroupController* p = std::next(descriptors->begin(), index)->second.controller(); + return static_cast(p); } diff --git a/libprocessgroup/cgrouprc/cgrouprc_internal.h b/libprocessgroup/cgrouprc/cgrouprc_internal.h index cd02f0304..d51770346 100644 --- a/libprocessgroup/cgrouprc/cgrouprc_internal.h +++ b/libprocessgroup/cgrouprc/cgrouprc_internal.h @@ -16,9 +16,6 @@ #pragma once -#include +#include -#include -#include - -struct ACgroupController : android::cgrouprc::format::CgroupController {}; +struct ACgroupController : CgroupController {}; diff --git a/libprocessgroup/cgrouprc_format/Android.bp b/libprocessgroup/cgrouprc_format/Android.bp index 059092419..6f9ab3e73 100644 --- a/libprocessgroup/cgrouprc_format/Android.bp +++ b/libprocessgroup/cgrouprc_format/Android.bp @@ -23,17 +23,4 @@ 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/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h index ffffeb48b..805775744 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); -static constexpr const char* CGROUPS_RC_PATH = "/dev/cgroup_info/cgroup.rc"; +[[deprecated]] static constexpr const char* CGROUPS_RC_PATH = "/dev/cgroup_info/cgroup.rc"; bool UsePerAppMemcg(); diff --git a/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_file.h b/libprocessgroup/internal.h similarity index 54% rename from libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_file.h rename to libprocessgroup/internal.h index 2d9786fe6..ef855790d 100644 --- a/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_file.h +++ b/libprocessgroup/internal.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -16,23 +16,6 @@ #pragma once -#include +#include -#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 +static const std::string CGROUP_V2_ROOT_DEFAULT = "/sys/fs/cgroup"; \ No newline at end of file diff --git a/libprocessgroup/setup/Android.bp b/libprocessgroup/setup/Android.bp index 1a4ad0118..cc6c67cf4 100644 --- a/libprocessgroup/setup/Android.bp +++ b/libprocessgroup/setup/Android.bp @@ -33,7 +33,6 @@ 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 06ce186fd..1afd2ee9c 100644 --- a/libprocessgroup/setup/cgroup_descriptor.h +++ b/libprocessgroup/setup/cgroup_descriptor.h @@ -21,10 +21,7 @@ #include -#include - -namespace android { -namespace cgrouprc { +#include // Complete controller description for mounting cgroups class CgroupDescriptor { @@ -33,7 +30,7 @@ class CgroupDescriptor { mode_t mode, const std::string& uid, const std::string& gid, uint32_t flags, uint32_t max_activation_depth); - const format::CgroupController* controller() const { return &controller_; } + const CgroupController* controller() const { return &controller_; } mode_t mode() const { return mode_; } std::string uid() const { return uid_; } std::string gid() const { return gid_; } @@ -41,11 +38,8 @@ class CgroupDescriptor { void set_mounted(bool mounted); private: - format::CgroupController controller_; + 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 bd4187475..821168008 100644 --- a/libprocessgroup/setup/cgroup_map_write.cpp +++ b/libprocessgroup/setup/cgroup_map_write.cpp @@ -22,45 +22,28 @@ #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 "cgroup_descriptor.h" - -using android::base::GetUintProperty; -using android::base::StringPrintf; -using android::base::unique_fd; - -namespace android { -namespace cgrouprc { +#include "../internal.h" 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; @@ -148,149 +131,15 @@ 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 format::CgroupController* controller) { +static bool IsOptionalController(const CgroupController* controller) { return controller->flags() & CGROUPRC_CONTROLLER_FLAG_OPTIONAL; } static bool MountV2CgroupController(const CgroupDescriptor& descriptor) { - const format::CgroupController* controller = descriptor.controller(); + const 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 @@ -324,7 +173,7 @@ static bool MountV2CgroupController(const CgroupDescriptor& descriptor) { } static bool ActivateV2CgroupController(const CgroupDescriptor& descriptor) { - const format::CgroupController* controller = descriptor.controller(); + const CgroupController* controller = descriptor.controller(); if (!Mkdir(controller->path(), descriptor.mode(), descriptor.uid(), descriptor.gid())) { LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup"; @@ -338,7 +187,7 @@ static bool ActivateV2CgroupController(const CgroupDescriptor& descriptor) { std::string path = controller->path(); path += "/cgroup.subtree_control"; - if (!base::WriteStringToFile(str, path)) { + if (!android::base::WriteStringToFile(str, path)) { if (IsOptionalController(controller)) { PLOG(INFO) << "Failed to activate optional controller " << controller->name() << " at " << path; @@ -353,7 +202,7 @@ static bool ActivateV2CgroupController(const CgroupDescriptor& descriptor) { } static bool MountV1CgroupController(const CgroupDescriptor& descriptor) { - const format::CgroupController* controller = descriptor.controller(); + const CgroupController* controller = descriptor.controller(); // mkdir [mode] [owner] [group] if (!Mkdir(controller->path(), descriptor.mode(), descriptor.uid(), descriptor.gid())) { @@ -388,7 +237,7 @@ static bool MountV1CgroupController(const CgroupDescriptor& descriptor) { } static bool SetupCgroup(const CgroupDescriptor& descriptor) { - const format::CgroupController* controller = descriptor.controller(); + const CgroupController* controller = descriptor.controller(); if (controller->version() == 2) { if (!strcmp(controller->name(), CGROUPV2_HIERARCHY_NAME)) { @@ -410,35 +259,6 @@ 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, @@ -458,9 +278,6 @@ 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; @@ -472,9 +289,8 @@ static std::optional MGLRUDisabled() { return content == "0x0000"; } -static std::optional MEMCGDisabled( - const std::map& descriptors) { - std::string cgroup_v2_root = android::cgrouprc::CGROUP_V2_ROOT_DEFAULT; +static std::optional MEMCGDisabled(const CgroupDescriptorMap& descriptors) { + std::string cgroup_v2_root = 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 " @@ -495,14 +311,10 @@ static std::optional MEMCGDisabled( return content.find("memory") == std::string::npos; } -static bool CreateV2SubHierarchy( - const std::string& path, - const std::map& descriptors) { - using namespace android::cgrouprc; - +static bool CreateV2SubHierarchy(const std::string& path, const CgroupDescriptorMap& descriptors) { const auto cgv2_iter = descriptors.find(CGROUPV2_HIERARCHY_NAME); if (cgv2_iter == descriptors.end()) return false; - const android::cgrouprc::CgroupDescriptor cgv2_descriptor = cgv2_iter->second; + const 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; @@ -512,10 +324,10 @@ static bool CreateV2SubHierarchy( // Activate all v2 controllers in path so they can be activated in // children as they are created. for (const auto& [name, descriptor] : descriptors) { - const format::CgroupController* controller = descriptor.controller(); + const 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); + const int depth = GetCgroupDepth(controller->path(), path); if (controller->version() == 2 && name != CGROUPV2_HIERARCHY_NAME && flags & CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION && depth < max_activation_depth) { @@ -535,22 +347,13 @@ static bool CreateV2SubHierarchy( } bool CgroupSetup() { - using namespace android::cgrouprc; - - std::map descriptors; + CgroupDescriptorMap 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"; @@ -559,15 +362,18 @@ bool CgroupSetup() { // setup cgroups for (auto& [name, descriptor] : descriptors) { - if (SetupCgroup(descriptor)) { - descriptor.set_mounted(true); - } else { + if (descriptor.controller()->flags() & CGROUPRC_CONTROLLER_FLAG_MOUNTED) { + LOG(WARNING) << "Attempt to call CgroupSetup() more than once"; + return true; + } + + if (!SetupCgroup(descriptor)) { // issue a warning and proceed with the next cgroup LOG(WARNING) << "Failed to setup " << name << " cgroup"; } } - if (force_memcg_v2) { + if (android::libprocessgroup_flags::force_memcg_v2()) { if (MGLRUDisabled().value_or(false)) { LOG(WARNING) << "Memcg forced to v2 hierarchy with MGLRU disabled! " << "Global reclaim performance will suffer."; @@ -593,26 +399,5 @@ 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 54ba69b4e..1c74d4ed5 100644 --- a/libprocessgroup/util/Android.bp +++ b/libprocessgroup/util/Android.bp @@ -37,8 +37,16 @@ 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/cgrouprc_format/cgroup_controller.cpp b/libprocessgroup/util/cgroup_controller.cpp similarity index 90% rename from libprocessgroup/cgrouprc_format/cgroup_controller.cpp rename to libprocessgroup/util/cgroup_controller.cpp index 0dd909a29..fb4168075 100644 --- a/libprocessgroup/cgrouprc_format/cgroup_controller.cpp +++ b/libprocessgroup/util/cgroup_controller.cpp @@ -14,11 +14,9 @@ * limitations under the License. */ -#include +#include -namespace android { -namespace cgrouprc { -namespace format { +#include CgroupController::CgroupController(uint32_t version, uint32_t flags, const std::string& name, const std::string& path, uint32_t max_activation_depth) @@ -54,8 +52,4 @@ const char* CgroupController::path() const { void CgroupController::set_flags(uint32_t flags) { flags_ = flags; -} - -} // namespace format -} // namespace cgrouprc -} // namespace android +} \ No newline at end of file diff --git a/libprocessgroup/util/cgroup_descriptor.cpp b/libprocessgroup/util/cgroup_descriptor.cpp new file mode 100644 index 000000000..4d3347f34 --- /dev/null +++ b/libprocessgroup/util/cgroup_descriptor.cpp @@ -0,0 +1,38 @@ +/* + * 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/cgrouprc_format/include/processgroup/format/cgroup_controller.h b/libprocessgroup/util/include/processgroup/cgroup_controller.h similarity index 87% rename from libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_controller.h rename to libprocessgroup/util/include/processgroup/cgroup_controller.h index c0c1f6034..fe6a829a0 100644 --- a/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_controller.h +++ b/libprocessgroup/util/include/processgroup/cgroup_controller.h @@ -20,11 +20,7 @@ #include #include -namespace android { -namespace cgrouprc { -namespace format { - -// Minimal controller description to be mmapped into process address space +// Minimal controller description struct CgroupController { public: CgroupController() = default; @@ -48,8 +44,4 @@ struct CgroupController { uint32_t max_activation_depth_ = UINT32_MAX; char name_[CGROUP_NAME_BUF_SZ] = {}; char path_[CGROUP_PATH_BUF_SZ] = {}; -}; - -} // namespace format -} // namespace cgrouprc -} // namespace android +}; \ No newline at end of file diff --git a/libprocessgroup/util/include/processgroup/cgroup_descriptor.h b/libprocessgroup/util/include/processgroup/cgroup_descriptor.h new file mode 100644 index 000000000..1afd2ee9c --- /dev/null +++ b/libprocessgroup/util/include/processgroup/cgroup_descriptor.h @@ -0,0 +1,45 @@ +/* + * 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 8d013af55..d592a6347 100644 --- a/libprocessgroup/util/include/processgroup/util.h +++ b/libprocessgroup/util/include/processgroup/util.h @@ -16,10 +16,18 @@ #pragma once +#include #include -namespace util { +#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 unsigned int GetCgroupDepth(const std::string& controller_root, const std::string& cgroup_path); -} // namespace util +using CgroupControllerName = std::string; +using CgroupDescriptorMap = std::map; +bool ReadDescriptors(CgroupDescriptorMap* descriptors); diff --git a/libprocessgroup/util/tests/util.cpp b/libprocessgroup/util/tests/util.cpp index 1de7d6f3f..6caef8ee3 100644 --- a/libprocessgroup/util/tests/util.cpp +++ b/libprocessgroup/util/tests/util.cpp @@ -18,8 +18,6 @@ #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 9b88a223a..bff4c6f1a 100644 --- a/libprocessgroup/util/util.cpp +++ b/libprocessgroup/util/util.cpp @@ -18,9 +18,33 @@ #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) { @@ -42,9 +66,135 @@ 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); @@ -56,4 +206,47 @@ unsigned int GetCgroupDepth(const std::string& controller_root, const std::strin return std::count(deduped_path.begin() + deduped_root.size(), deduped_path.end(), SEP); } -} // namespace util +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; +}