libprocessgroup: Remove cgroup.rc file
The cgroup.rc file was introduced in 192aee782 ("libprocessgroup: Add
support for task profiles") back with the initial support for task
profiles. It was intended to optimize performance associated with cgroup
operations. However over time, supporting this file led to making
libprocessgroup code more complicated (such as the cgrouprc LLNDK
interface), and the file ended up getting mmaped into nearly every
process on Android even though only a handful of them actually use it.
Replacing this file with reading and parsing of cgroup information on
demand allows us to simplify and shrink libprocessgroup, and eliminates
thousands of unused mappings without negatively affecting boot time or
other performance metrics.
Bug: 349105928
Test: Verified with memcg v2 and MaxActivationDepth 1 on Cuttlefish, Raven, and Mokey
Change-Id: Ic3f01fdf7fda89a56ab80657e1cf4573156273e6
This commit is contained in:
parent
8dd9d45534
commit
ae4ce8ccc5
23 changed files with 365 additions and 389 deletions
|
|
@ -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",
|
||||
],
|
||||
|
|
|
|||
|
|
@ -163,7 +163,6 @@ libinit_cc_defaults {
|
|||
"libavb",
|
||||
"libavf_cc_flags",
|
||||
"libbootloader_message",
|
||||
"libcgrouprc_format",
|
||||
"liblmkd_utils",
|
||||
"liblz4",
|
||||
"libzstd",
|
||||
|
|
|
|||
|
|
@ -636,9 +636,6 @@ static Result<void> SetupCgroupsAction(const BuiltinArguments&) {
|
|||
LOG(INFO) << "Cgroups support in kernel is not enabled";
|
||||
return {};
|
||||
}
|
||||
// Have to create <CGROUPS_RC_DIR> 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";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -301,7 +301,7 @@ cc_defaults {
|
|||
android: {
|
||||
static_executable: true,
|
||||
static_libs: [
|
||||
"libcgrouprc_format",
|
||||
"libprocessgroup_util",
|
||||
] + test_libraries + always_static_test_libraries,
|
||||
},
|
||||
not_windows: {
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
],
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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("+");
|
||||
|
|
|
|||
|
|
@ -49,7 +49,8 @@ cc_library {
|
|||
"libbase",
|
||||
],
|
||||
static_libs: [
|
||||
"libcgrouprc_format",
|
||||
"libjsoncpp",
|
||||
"libprocessgroup_util",
|
||||
],
|
||||
stubs: {
|
||||
symbol_file: "libcgrouprc.map.txt",
|
||||
|
|
|
|||
|
|
@ -14,93 +14,51 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <memory>
|
||||
#include <iterator>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <android/cgrouprc.h>
|
||||
#include <processgroup/processgroup.h>
|
||||
#include <processgroup/util.h>
|
||||
|
||||
#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<ACgroupController*>(&file->controllers_[index]);
|
||||
const CgroupController* p = std::next(descriptors->begin(), index)->second.controller();
|
||||
return static_cast<const ACgroupController*>(p);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,9 +16,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <android/cgrouprc.h>
|
||||
#include <processgroup/cgroup_controller.h>
|
||||
|
||||
#include <processgroup/format/cgroup_controller.h>
|
||||
#include <processgroup/format/cgroup_file.h>
|
||||
|
||||
struct ACgroupController : android::cgrouprc::format::CgroupController {};
|
||||
struct ACgroupController : CgroupController {};
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
],
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ __BEGIN_DECLS
|
|||
|
||||
bool SetProcessProfilesCached(uid_t uid, pid_t pid, const std::vector<std::string>& 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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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 <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include <processgroup/format/cgroup_controller.h>
|
||||
|
||||
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";
|
||||
|
|
@ -33,7 +33,6 @@ cc_library_shared {
|
|||
"libjsoncpp",
|
||||
],
|
||||
static_libs: [
|
||||
"libcgrouprc_format",
|
||||
"libprocessgroup_util",
|
||||
],
|
||||
header_libs: [
|
||||
|
|
|
|||
|
|
@ -21,10 +21,7 @@
|
|||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <processgroup/format/cgroup_controller.h>
|
||||
|
||||
namespace android {
|
||||
namespace cgrouprc {
|
||||
#include <processgroup/cgroup_controller.h>
|
||||
|
||||
// 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
|
||||
|
|
|
|||
|
|
@ -22,45 +22,28 @@
|
|||
#include <fcntl.h>
|
||||
#include <grp.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/properties.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <android/cgrouprc.h>
|
||||
#include <json/reader.h>
|
||||
#include <json/value.h>
|
||||
#include <processgroup/format/cgroup_file.h>
|
||||
#include <processgroup/cgroup_descriptor.h>
|
||||
#include <processgroup/processgroup.h>
|
||||
#include <processgroup/setup.h>
|
||||
#include <processgroup/util.h>
|
||||
|
||||
#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<std::string, CgroupDescriptor>* 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<std::string, CgroupDescriptor>* descriptors) {
|
||||
std::vector<CgroupDescriptor> 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<Json::CharReader> 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<std::string, CgroupDescriptor>* 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<unsigned int>("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 <path> [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<std::string, CgroupDescriptor>& 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<bool> MGLRUDisabled() {
|
||||
const std::string file_name = "/sys/kernel/mm/lru_gen/enabled";
|
||||
std::string content;
|
||||
|
|
@ -472,9 +289,8 @@ static std::optional<bool> MGLRUDisabled() {
|
|||
return content == "0x0000";
|
||||
}
|
||||
|
||||
static std::optional<bool> MEMCGDisabled(
|
||||
const std::map<std::string, android::cgrouprc::CgroupDescriptor>& descriptors) {
|
||||
std::string cgroup_v2_root = android::cgrouprc::CGROUP_V2_ROOT_DEFAULT;
|
||||
static std::optional<bool> 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<bool> MEMCGDisabled(
|
|||
return content.find("memory") == std::string::npos;
|
||||
}
|
||||
|
||||
static bool CreateV2SubHierarchy(
|
||||
const std::string& path,
|
||||
const std::map<std::string, android::cgrouprc::CgroupDescriptor>& 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<std::string, CgroupDescriptor> 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 <CGROUPS_RC_DIR> 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 <CGROUPS_RC_FILE> 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 <CGROUPS_RC_PATH>
|
||||
if (fchmodat(AT_FDCWD, CGROUPS_RC_PATH, 0644, AT_SYMLINK_NOFOLLOW) < 0) {
|
||||
PLOG(ERROR) << "fchmodat() failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"],
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,11 +14,9 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <processgroup/format/cgroup_controller.h>
|
||||
#include <processgroup/cgroup_controller.h>
|
||||
|
||||
namespace android {
|
||||
namespace cgrouprc {
|
||||
namespace format {
|
||||
#include <cstring>
|
||||
|
||||
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
|
||||
}
|
||||
38
libprocessgroup/util/cgroup_descriptor.cpp
Normal file
38
libprocessgroup/util/cgroup_descriptor.cpp
Normal file
|
|
@ -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 <processgroup/cgroup_descriptor.h>
|
||||
|
||||
#include <processgroup/util.h> // 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);
|
||||
}
|
||||
|
|
@ -20,11 +20,7 @@
|
|||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
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
|
||||
};
|
||||
|
|
@ -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 <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <processgroup/cgroup_controller.h>
|
||||
|
||||
// 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_;
|
||||
};
|
||||
|
|
@ -16,10 +16,18 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
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<CgroupControllerName, CgroupDescriptor>;
|
||||
bool ReadDescriptors(CgroupDescriptorMap* descriptors);
|
||||
|
|
|
|||
|
|
@ -18,8 +18,6 @@
|
|||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using util::GetCgroupDepth;
|
||||
|
||||
TEST(EmptyInputs, bothEmpty) {
|
||||
EXPECT_EQ(GetCgroupDepth({}, {}), 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,9 +18,33 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
#include <mntent.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/properties.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <json/reader.h>
|
||||
#include <json/value.h>
|
||||
|
||||
#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<CgroupDescriptor> 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<Json::CharReader> 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<std::map<MountDir, MountOpts>> ReadCgroupV1Mounts() {
|
||||
FILE* fp = setmntent("/proc/mounts", "r");
|
||||
if (fp == nullptr) {
|
||||
PLOG(ERROR) << "Failed to read mounts";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::map<MountDir, MountOpts> 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<unsigned int>("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<std::map<MountDir, MountOpts>> 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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue