Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6d6ff398 authored by T.J. Mercier's avatar T.J. Mercier Committed by Gerrit Code Review
Browse files

Merge changes from topic "memcg_v2_soong_flags" into main

* changes:
  Add build flag to split the cgroup v2 hierarchy into apps/system
  Add build flag to force memcg to the v2 cgroup hierarchy
  Use ConvertUid{Pid}ToPath for all path generation
  Fix unused params and remove unneeded cflags
parents 73c6f08f 1cfa2c41
Loading
Loading
Loading
Loading
+29 −10
Original line number Diff line number Diff line
@@ -2,15 +2,34 @@ 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",
        "cgroup_v2_sys_app_isolation",
    ],
    properties: [
        "cflags",
    ],
}

libprocessgroup_flag_aware_cc_defaults {
    name: "libprocessgroup_build_flags_cc",
    cpp_std: "gnu++20",
    soong_config_variables: {
        memcg_v2_force_enabled: {
            cflags: [
        "-Wall",
        "-Werror",
        "-Wexit-time-destructors",
        "-Wno-unused-parameter",
                "-DMEMCG_V2_FORCE_ENABLED=true",
            ],
        },
        cgroup_v2_sys_app_isolation: {
            cflags: [
                "-DCGROUP_V2_SYS_APP_ISOLATION=true",
            ],
        },
    },
}

cc_library_headers {
@@ -73,7 +92,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",
@@ -84,7 +103,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",
    ],
+37 −0
Original line number Diff line number Diff line
/*
 * 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

#ifndef CGROUP_V2_SYS_APP_ISOLATION
#define CGROUP_V2_SYS_APP_ISOLATION false
#endif

namespace android::libprocessgroup_flags {

inline consteval bool force_memcg_v2() {
    return MEMCG_V2_FORCE_ENABLED;
}

inline consteval bool cgroup_v2_sys_app_isolation() {
    return CGROUP_V2_SYS_APP_ISOLATION;
}

}  // namespace android::libprocessgroup_flags
+0 −8
Original line number Diff line number Diff line
@@ -78,14 +78,6 @@ bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path)
    return true;
}

static std::string ConvertUidToPath(const char* cgroup, uid_t uid) {
    return StringPrintf("%s/uid_%u", cgroup, uid);
}

static std::string ConvertUidPidToPath(const char* cgroup, uid_t uid, pid_t pid) {
    return StringPrintf("%s/uid_%u/pid_%d", cgroup, uid, pid);
}

static bool CgroupKillAvailable() {
    static std::once_flag f;
    static bool cgroup_kill_available = false;
+1 −4
Original line number Diff line number Diff line
@@ -41,8 +41,5 @@ cc_library_shared {
    export_header_lib_headers: [
        "libprocessgroup_headers",
    ],
    cflags: [
        "-Wall",
        "-Werror",
    ],
    defaults: ["libprocessgroup_build_flags_cc"],
}
+124 −3
Original line number Diff line number Diff line
@@ -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,76 @@ 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;
}

static bool CreateV2SubHierarchy(
        const std::string& path,
        const std::map<std::string, android::cgrouprc::CgroupDescriptor>& descriptors) {
    using namespace android::cgrouprc;

    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;

    if (!Mkdir(path, cgv2_descriptor.mode(), cgv2_descriptor.uid(), cgv2_descriptor.gid())) {
        PLOG(ERROR) << "Failed to create directory for " << path;
        return false;
    }

    // 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();
        std::uint32_t flags = controller->flags();
        if (controller->version() == 2 && name != CGROUPV2_HIERARCHY_NAME &&
            flags & CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION) {
            std::string str("+");
            str += controller->name();
            if (!android::base::WriteStringToFile(str, path + "/cgroup.subtree_control")) {
                if (flags & CGROUPRC_CONTROLLER_FLAG_OPTIONAL) {
                    PLOG(WARNING) << "Activation of cgroup controller " << str << " failed in path "
                                  << path;
                } else {
                    return false;
                }
            }
        }
    }
    return true;
}

bool CgroupSetup() {
    using namespace android::cgrouprc;

@@ -457,6 +552,32 @@ 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!";
        }
    }

    // System / app isolation.
    // This really belongs in early-init in init.rc, but we cannot use the flag there.
    if (android::libprocessgroup_flags::cgroup_v2_sys_app_isolation()) {
        const auto it = descriptors.find(CGROUPV2_HIERARCHY_NAME);
        const std::string cgroup_v2_root = (it == descriptors.end())
                                                   ? CGROUP_V2_ROOT_DEFAULT
                                                   : it->second.controller()->path();

        LOG(INFO) << "Using system/app isolation under: " << cgroup_v2_root;
        if (!CreateV2SubHierarchy(cgroup_v2_root + "/apps", descriptors) ||
            !CreateV2SubHierarchy(cgroup_v2_root + "/system", descriptors)) {
            return false;
        }
    }

    // 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";
Loading