summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorT.J. Mercier <tjmercier@google.com>2024-03-05 21:55:15 +0000
committerT.J. Mercier <tjmercier@google.com>2024-04-08 21:15:48 +0000
commitf8901767e63636d1fa884255cf9bfe0c7ab368b9 (patch)
treed729f1dcc43d9bcf228eed98428038d3b857091e
parentd1e048f95618b6fb7f66c81e1c650482d95cd452 (diff)
downloadcore-f8901767e63636d1fa884255cf9bfe0c7ab368b9.tar.gz
Add build flag to force memcg to the v2 cgroup hierarchy
This flag prevents memcg from being mounted as a v1 hierarchy, even if cgroups.json specifies it should be mounted as v1. It will activate memcg in the v2 hierarchy even if cgroups.json does not specify that it should be activated in the v2 hierarchy. The cgroup_disable=memory kernel command line argument will still prevent memcg from being activated, as this forced memcg controller is marked as optional. Bug: 327480673 Change-Id: Iad2491dd0c1576156ee2346928d041d85af890f0
-rw-r--r--libprocessgroup/Android.bp27
-rw-r--r--libprocessgroup/build_flags.h29
-rw-r--r--libprocessgroup/setup/Android.bp5
-rw-r--r--libprocessgroup/setup/cgroup_map_write.cpp76
4 files changed, 126 insertions, 11 deletions
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 <time.h>
#include <unistd.h>
-#include <regex>
+#include <optional>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -43,6 +43,7 @@
#include <processgroup/processgroup.h>
#include <processgroup/setup.h>
+#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<std::string, CgroupDescriptor>* de
}
}
+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;
@@ -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<bool> 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<bool> MEMCGDisabled(
+ const std::map<std::string, android::cgrouprc::CgroupDescriptor>& 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 <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";