Merge "libprocessgroup: Add MaxActivationDepth" into main

This commit is contained in:
T.J. Mercier 2024-06-26 22:57:45 +00:00 committed by Gerrit Code Review
commit 3d54c32e33
16 changed files with 290 additions and 13 deletions

View file

@ -84,6 +84,7 @@ cc_library {
header_libs: [
"libcutils_headers",
"libprocessgroup_headers",
"libprocessgroup_util",
],
export_include_dirs: ["include"],
export_header_lib_headers: [

View file

@ -28,6 +28,7 @@
#include <android-base/strings.h>
#include <cgroup_map.h>
#include <processgroup/processgroup.h>
#include <processgroup/util.h>
using android::base::StartsWith;
using android::base::StringPrintf;
@ -216,7 +217,13 @@ int CgroupMap::ActivateControllers(const std::string& path) const {
for (uint32_t i = 0; i < controller_count; ++i) {
const ACgroupController* controller = ACgroupFile_getController(i);
const uint32_t flags = ACgroupController_getFlags(controller);
if (flags & CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION) {
uint32_t max_activation_depth = UINT32_MAX;
if (__builtin_available(android 36, *)) {
max_activation_depth = ACgroupController_getMaxActivationDepth(controller);
}
const int depth = util::GetCgroupDepth(ACgroupController_getPath(controller), path);
if (flags & CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION && depth < max_activation_depth) {
std::string str("+");
str.append(ACgroupController_getName(controller));
if (!WriteStringToFile(str, path + "/cgroup.subtree_control")) {

View file

@ -32,6 +32,11 @@ uint32_t ACgroupController_getFlags(const ACgroupController* controller) {
return controller->flags();
}
uint32_t ACgroupController_getMaxActivationDepth(const ACgroupController* controller) {
CHECK(controller != nullptr);
return controller->max_activation_depth();
}
const char* ACgroupController_getName(const ACgroupController* controller) {
CHECK(controller != nullptr);
return controller->name();

View file

@ -78,6 +78,14 @@ __attribute__((warn_unused_result)) uint32_t ACgroupController_getVersion(const
__attribute__((warn_unused_result, weak)) uint32_t ACgroupController_getFlags(
const ACgroupController*) __INTRODUCED_IN(30);
/**
* Returns the maximum activation depth of the given controller.
* Only applicable to cgroup v2 controllers.
* Returns UINT32_MAX if no maximum activation depth is set.
*/
__attribute__((warn_unused_result, weak)) uint32_t ACgroupController_getMaxActivationDepth(
const ACgroupController* controller) __INTRODUCED_IN(36);
/**
* Returns the name of the given controller.
* If the given controller is null, return nullptr.

View file

@ -16,3 +16,10 @@ LIBCGROUPRC_30 { # introduced=30
local:
*;
};
LIBCGROUPRC_36 { # introduced=36
global:
ACgroupController_getMaxActivationDepth; # llndk=202504 systemapi
local:
*;
};

View file

@ -21,13 +21,11 @@ namespace cgrouprc {
namespace format {
CgroupController::CgroupController(uint32_t version, uint32_t flags, const std::string& name,
const std::string& path)
{
const std::string& path, uint32_t max_activation_depth)
: version_(version), flags_(flags), max_activation_depth_(max_activation_depth) {
// strlcpy isn't available on host. Although there is an implementation
// in licutils, libcutils itself depends on libcgrouprc_format, causing
// a circular dependency.
version_ = version;
flags_ = flags;
strncpy(name_, name.c_str(), sizeof(name_) - 1);
name_[sizeof(name_) - 1] = '\0';
strncpy(path_, path.c_str(), sizeof(path_) - 1);
@ -42,6 +40,10 @@ uint32_t CgroupController::flags() const {
return flags_;
}
uint32_t CgroupController::max_activation_depth() const {
return max_activation_depth_;
}
const char* CgroupController::name() const {
return name_;
}

View file

@ -29,10 +29,11 @@ struct CgroupController {
public:
CgroupController() = default;
CgroupController(uint32_t version, uint32_t flags, const std::string& name,
const std::string& path);
const std::string& path, uint32_t max_activation_depth);
uint32_t version() const;
uint32_t flags() const;
uint32_t max_activation_depth() const;
const char* name() const;
const char* path() const;
@ -44,6 +45,7 @@ struct CgroupController {
uint32_t version_ = 0;
uint32_t flags_ = 0;
uint32_t max_activation_depth_ = UINT32_MAX;
char name_[CGROUP_NAME_BUF_SZ] = {};
char path_[CGROUP_PATH_BUF_SZ] = {};
};

View file

@ -24,7 +24,7 @@ message Cgroups {
Cgroups2 cgroups2 = 2 [json_name = "Cgroups2"];
}
// Next: 8
// Next: 9
message Cgroup {
string controller = 1 [json_name = "Controller"];
string path = 2 [json_name = "Path"];
@ -36,6 +36,7 @@ message Cgroup {
// https://developers.google.com/protocol-buffers/docs/proto3#default
bool needs_activation = 6 [json_name = "NeedsActivation"];
bool is_optional = 7 [json_name = "Optional"];
uint32 max_activation_depth = 8 [json_name = "MaxActivationDepth"];
}
// Next: 6

View file

@ -37,6 +37,7 @@ cc_library_shared {
],
header_libs: [
"libprocessgroup_headers",
"libprocessgroup_util",
],
export_header_lib_headers: [
"libprocessgroup_headers",

View file

@ -30,7 +30,8 @@ namespace cgrouprc {
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);
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_; }
mode_t mode() const { return mode_; }

View file

@ -42,6 +42,7 @@
#include <processgroup/format/cgroup_file.h>
#include <processgroup/processgroup.h>
#include <processgroup/setup.h>
#include <processgroup/util.h>
#include "../build_flags.h"
#include "cgroup_descriptor.h"
@ -173,9 +174,15 @@ static void MergeCgroupToDescriptors(std::map<std::string, CgroupDescriptor>* de
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);
cgroup["UID"].asString(), cgroup["GID"].asString(), controller_flags,
max_activation_depth);
auto iter = descriptors->find(name);
if (iter == descriptors->end()) {
@ -324,7 +331,8 @@ static bool ActivateV2CgroupController(const CgroupDescriptor& descriptor) {
return false;
}
if (controller->flags() & CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION) {
if (controller->flags() & CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION &&
controller->max_activation_depth() > 0) {
std::string str = "+";
str += controller->name();
std::string path = controller->path();
@ -433,8 +441,12 @@ static bool WriteRcFile(const std::map<std::string, CgroupDescriptor>& descripto
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 = 0)
: controller_(version, flags, name, path), mode_(mode), uid_(uid), gid_(gid) {}
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();
@ -502,8 +514,11 @@ static bool CreateV2SubHierarchy(
for (const auto& [name, descriptor] : descriptors) {
const format::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);
if (controller->version() == 2 && name != CGROUPV2_HIERARCHY_NAME &&
flags & CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION) {
flags & CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION && depth < max_activation_depth) {
std::string str("+");
str += controller->name();
if (!android::base::WriteStringToFile(str, path + "/cgroup.subtree_control")) {

View file

@ -0,0 +1,47 @@
//
// 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.
//
package {
default_team: "trendy_team_android_kernel",
default_applicable_licenses: ["Android-Apache-2.0"],
}
cc_library_headers {
name: "libprocessgroup_util",
vendor_available: true,
product_available: true,
ramdisk_available: true,
vendor_ramdisk_available: true,
recovery_available: true,
host_supported: true,
native_bridge_supported: true,
apex_available: [
"//apex_available:platform",
"//apex_available:anyapex",
],
min_sdk_version: "30",
export_include_dirs: [
"include",
],
defaults: ["libprocessgroup_build_flags_cc"],
}
cc_test {
name: "libprocessgroup_util_test",
header_libs: ["libprocessgroup_util"],
srcs: ["tests/util.cpp"],
test_suites: ["general-tests"],
}

View file

@ -0,0 +1,3 @@
# Bug component: 1293033
surenb@google.com
tjmercier@google.com

View file

@ -0,0 +1,7 @@
{
"postsubmit": [
{
"name": "libprocessgroup_util_test"
}
]
}

View file

@ -0,0 +1,61 @@
/*
* 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
#include <algorithm>
#include <iterator>
#include <string>
namespace util {
namespace internal {
const char SEP = '/';
std::string DeduplicateAndTrimSeparators(const std::string& path) {
bool lastWasSep = false;
std::string ret;
std::copy_if(path.begin(), path.end(), std::back_inserter(ret), [&lastWasSep](char c) {
if (lastWasSep) {
if (c == SEP) return false;
lastWasSep = false;
} else if (c == SEP) {
lastWasSep = true;
}
return true;
});
if (ret.length() > 1 && ret.back() == SEP) ret.pop_back();
return ret;
}
} // namespace internal
unsigned int GetCgroupDepth(const std::string& controller_root, const std::string& cgroup_path) {
const std::string deduped_root = internal::DeduplicateAndTrimSeparators(controller_root);
const std::string deduped_path = internal::DeduplicateAndTrimSeparators(cgroup_path);
if (deduped_root.empty() || deduped_path.empty() || !deduped_path.starts_with(deduped_root))
return 0;
return std::count(deduped_path.begin() + deduped_root.size(), deduped_path.end(),
internal::SEP);
}
} // namespace util

View file

@ -0,0 +1,109 @@
/*
* 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/util.h>
#include "gtest/gtest.h"
using util::GetCgroupDepth;
TEST(EmptyInputs, bothEmpty) {
EXPECT_EQ(GetCgroupDepth({}, {}), 0);
}
TEST(EmptyInputs, rootEmpty) {
EXPECT_EQ(GetCgroupDepth({}, "foo"), 0);
}
TEST(EmptyInputs, pathEmpty) {
EXPECT_EQ(GetCgroupDepth("foo", {}), 0);
}
TEST(InvalidInputs, pathNotInRoot) {
EXPECT_EQ(GetCgroupDepth("foo", "bar"), 0);
}
TEST(InvalidInputs, rootLargerThanPath) {
EXPECT_EQ(GetCgroupDepth("/a/long/path", "/short"), 0);
}
TEST(InvalidInputs, pathLargerThanRoot) {
EXPECT_EQ(GetCgroupDepth("/short", "/a/long/path"), 0);
}
TEST(InvalidInputs, missingSeparator) {
EXPECT_EQ(GetCgroupDepth("/controller/root", "/controller/rootcgroup"), 0);
}
TEST(ExtraSeparators, root) {
EXPECT_EQ(GetCgroupDepth("///sys/fs/cgroup", "/sys/fs/cgroup/a/b/c"), 3);
EXPECT_EQ(GetCgroupDepth("/sys///fs/cgroup", "/sys/fs/cgroup/a/b/c"), 3);
EXPECT_EQ(GetCgroupDepth("/sys/fs///cgroup", "/sys/fs/cgroup/a/b/c"), 3);
EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup", "///sys/fs/cgroup/a/b/c"), 3);
EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup", "/sys///fs/cgroup/a/b/c"), 3);
EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup", "/sys/fs///cgroup/a/b/c"), 3);
EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup", "/sys/fs/cgroup///a/b/c"), 3);
EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup", "/sys/fs/cgroup/a///b/c"), 3);
EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup", "/sys/fs/cgroup/a/b///c"), 3);
}
TEST(SeparatorEndings, rootEndsInSeparator) {
EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup/", "/sys/fs/cgroup/a/b"), 2);
EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup///", "/sys/fs/cgroup/a/b"), 2);
EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup/", "/sys/fs/cgroup/a/b/"), 2);
EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup///", "/sys/fs/cgroup/a/b/"), 2);
}
TEST(SeparatorEndings, pathEndsInSeparator) {
EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup", "/sys/fs/cgroup/a/b/"), 2);
EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup", "/sys/fs/cgroup/a/b///"), 2);
EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup/", "/sys/fs/cgroup/a/b/"), 2);
EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup/", "/sys/fs/cgroup/a/b///"), 2);
}
TEST(ValidInputs, rootHasZeroDepth) {
EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup", "/sys/fs/cgroup"), 0);
EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup/", "/sys/fs/cgroup"), 0);
EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup", "/sys/fs/cgroup/"), 0);
EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup/", "/sys/fs/cgroup/"), 0);
}
TEST(ValidInputs, atLeastDepth10) {
EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup", "/sys/fs/cgroup/a/b/c/d/e/f/g/h/i/j"), 10);
}
TEST(ValidInputs, androidCgroupNames) {
EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup", "/sys/fs/cgroup/system/uid_0/pid_1000"), 3);
EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup", "/sys/fs/cgroup/uid_0/pid_1000"), 2);
EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup", "/sys/fs/cgroup/apps/uid_100000/pid_1000"), 3);
EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup", "/sys/fs/cgroup/uid_100000/pid_1000"), 2);
EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup", "/sys/fs/cgroup/apps"), 1);
EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup", "/sys/fs/cgroup/system"), 1);
}
TEST(ValidInputs, androidCgroupNames_nonDefaultRoot) {
EXPECT_EQ(GetCgroupDepth("/custom/root", "/custom/root/system/uid_0/pid_1000"), 3);
EXPECT_EQ(GetCgroupDepth("/custom/root", "/custom/root/uid_0/pid_1000"), 2);
EXPECT_EQ(GetCgroupDepth("/custom/root", "/custom/root/apps/uid_100000/pid_1000"), 3);
EXPECT_EQ(GetCgroupDepth("/custom/root", "/custom/root/uid_100000/pid_1000"), 2);
EXPECT_EQ(GetCgroupDepth("/custom/root", "/custom/root/apps"), 1);
EXPECT_EQ(GetCgroupDepth("/custom/root", "/custom/root/system"), 1);
}