diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp index ca2e0ec7f..f529ad76d 100644 --- a/libprocessgroup/Android.bp +++ b/libprocessgroup/Android.bp @@ -2,9 +2,28 @@ package { default_applicable_licenses: ["Android-Apache-2.0"], } -cc_defaults { - name: "libprocessgroup_defaults", +soong_config_module_type { + name: "libprocessgroup_flag_aware_cc_defaults", + module_type: "cc_defaults", + config_namespace: "ANDROID", + bool_variables: [ + "memcg_v2_force_enabled", + ], + properties: [ + "cflags", + ], +} + +libprocessgroup_flag_aware_cc_defaults { + name: "libprocessgroup_build_flags_cc", cpp_std: "gnu++20", + soong_config_variables: { + memcg_v2_force_enabled: { + cflags: [ + "-DMEMCG_V2_FORCE_ENABLED=true", + ], + }, + }, } cc_library_headers { @@ -67,7 +86,7 @@ cc_library { export_header_lib_headers: [ "libprocessgroup_headers", ], - defaults: ["libprocessgroup_defaults"], + defaults: ["libprocessgroup_build_flags_cc"], apex_available: [ "//apex_available:platform", "//apex_available:anyapex", @@ -78,7 +97,7 @@ cc_library { cc_test { name: "task_profiles_test", host_supported: true, - defaults: ["libprocessgroup_defaults"], + defaults: ["libprocessgroup_build_flags_cc"], srcs: [ "task_profiles_test.cpp", ], diff --git a/libprocessgroup/build_flags.h b/libprocessgroup/build_flags.h new file mode 100644 index 000000000..69fa76e1b --- /dev/null +++ b/libprocessgroup/build_flags.h @@ -0,0 +1,29 @@ +/* + * 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. + */ + +#pragma once + +#ifndef MEMCG_V2_FORCE_ENABLED +#define MEMCG_V2_FORCE_ENABLED false +#endif + +namespace android::libprocessgroup_flags { + +inline consteval bool force_memcg_v2() { + return MEMCG_V2_FORCE_ENABLED; +} + +} // namespace android::libprocessgroup_flags diff --git a/libprocessgroup/setup/Android.bp b/libprocessgroup/setup/Android.bp index ea6c24706..1e0783ab0 100644 --- a/libprocessgroup/setup/Android.bp +++ b/libprocessgroup/setup/Android.bp @@ -41,8 +41,5 @@ cc_library_shared { export_header_lib_headers: [ "libprocessgroup_headers", ], - cflags: [ - "-Wall", - "-Werror", - ], + defaults: ["libprocessgroup_build_flags_cc"], } diff --git a/libprocessgroup/setup/cgroup_map_write.cpp b/libprocessgroup/setup/cgroup_map_write.cpp index 4e44c91be..a9fd0e58b 100644 --- a/libprocessgroup/setup/cgroup_map_write.cpp +++ b/libprocessgroup/setup/cgroup_map_write.cpp @@ -29,7 +29,7 @@ #include #include -#include +#include #include #include @@ -43,6 +43,7 @@ #include #include +#include "../build_flags.h" #include "cgroup_descriptor.h" using android::base::GetUintProperty; @@ -57,6 +58,8 @@ static constexpr const char* CGROUPS_DESC_VENDOR_FILE = "/vendor/etc/cgroups.jso 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; @@ -182,6 +185,8 @@ static void MergeCgroupToDescriptors(std::map* de } } +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; @@ -205,22 +210,41 @@ static bool ReadDescriptorsFromFile(const std::string& file_name, 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"]; - std::string root_path = cgroups2["Path"].asString(); + 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; } @@ -308,7 +332,8 @@ static bool ActivateV2CgroupController(const CgroupDescriptor& descriptor) { if (!base::WriteStringToFile(str, path)) { if (IsOptionalController(controller)) { - PLOG(INFO) << "Failed to activate optional controller " << controller->name(); + PLOG(INFO) << "Failed to activate optional controller " << controller->name() + << " at " << path; return true; } PLOG(ERROR) << "Failed to activate controller " << controller->name(); @@ -424,6 +449,40 @@ void CgroupDescriptor::set_mounted(bool mounted) { } // namespace cgrouprc } // namespace android +static std::optional MGLRUDisabled() { + const std::string file_name = "/sys/kernel/mm/lru_gen/enabled"; + std::string content; + if (!android::base::ReadFileToString(file_name, &content)) { + PLOG(ERROR) << "Failed to read MGLRU state from " << file_name; + return {}; + } + + return content == "0x0000"; +} + +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 " + << "kernel memory use will be higher than intended."; + } else if (it->second.controller()->path() != cgroup_v2_root) { + cgroup_v2_root = it->second.controller()->path(); + } + + const std::string file_name = cgroup_v2_root + "/cgroup.controllers"; + std::string content; + if (!android::base::ReadFileToString(file_name, &content)) { + PLOG(ERROR) << "Failed to read cgroup controllers from " << file_name; + return {}; + } + + // If we've forced memcg to v2 and it's not available, then it could only have been disabled + // on the kernel command line (GKI sets CONFIG_MEMCG). + return content.find("memory") == std::string::npos; +} + bool CgroupSetup() { using namespace android::cgrouprc; @@ -457,6 +516,17 @@ bool CgroupSetup() { } } + if (force_memcg_v2) { + if (MGLRUDisabled().value_or(false)) { + LOG(WARNING) << "Memcg forced to v2 hierarchy with MGLRU disabled! " + << "Global reclaim performance will suffer."; + } + if (MEMCGDisabled(descriptors).value_or(false)) { + LOG(WARNING) << "Memcg forced to v2 hierarchy while memcg is disabled by kernel " + << "command line!"; + } + } + // 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";