libprocessgroup: Add support for task profiles
Abstract usage of cgroups into task profiles that allows for changes
in cgroup hierarchy and version without affecting framework codebase.
Rework current processgroup and sched_policy API function implementations
to use task profiles instead of hardcoded paths and attributes.
Mount cgroups using information from cgroups.json rather than from init.rc
Exempt-From-Owner-Approval: already approved in internal master
Bug: 111307099
Test: builds, boots
Change-Id: If5532d6dc570add825cebd5b5148e00c7d688e32
Merged-In: If5532d6dc570add825cebd5b5148e00c7d688e32
Signed-off-by: Suren Baghdasaryan <surenb@google.com>
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 8d2ac3d..e9dec12 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -25,12 +25,12 @@
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <chrono>
+#include <map>
#include <memory>
#include <mutex>
#include <set>
@@ -43,8 +43,8 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <cutils/android_filesystem_config.h>
-
#include <processgroup/processgroup.h>
+#include <task_profiles.h>
using android::base::GetBoolProperty;
using android::base::StartsWith;
@@ -53,16 +53,103 @@
using namespace std::chrono_literals;
-static const char kCpuacctCgroup[] = "/acct";
-static const char kMemoryCgroup[] = "/dev/memcg/apps";
-
#define PROCESSGROUP_CGROUP_PROCS_FILE "/cgroup.procs"
+bool CgroupSetupCgroups() {
+ return CgroupMap::SetupCgroups();
+}
+
+bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path) {
+ const CgroupController* controller = CgroupMap::GetInstance().FindController(cgroup_name);
+
+ if (controller == nullptr) {
+ return false;
+ }
+
+ if (path) {
+ *path = controller->path();
+ }
+
+ return true;
+}
+
+bool CgroupGetAttributePath(const std::string& attr_name, std::string* path) {
+ const TaskProfiles& tp = TaskProfiles::GetInstance();
+ const ProfileAttribute* attr = tp.GetAttribute(attr_name);
+
+ if (attr == nullptr) {
+ return false;
+ }
+
+ if (path) {
+ *path = StringPrintf("%s/%s", attr->controller()->path(), attr->file_name().c_str());
+ }
+
+ return true;
+}
+
+bool CgroupGetAttributePathForTask(const std::string& attr_name, int tid, std::string* path) {
+ const TaskProfiles& tp = TaskProfiles::GetInstance();
+ const ProfileAttribute* attr = tp.GetAttribute(attr_name);
+
+ if (attr == nullptr) {
+ return false;
+ }
+
+ if (!attr->GetPathForTask(tid, path)) {
+ PLOG(ERROR) << "Failed to find cgroup for tid " << tid;
+ return false;
+ }
+
+ return true;
+}
+
+bool UsePerAppMemcg() {
+ bool low_ram_device = GetBoolProperty("ro.config.low_ram", false);
+ return GetBoolProperty("ro.config.per_app_memcg", low_ram_device);
+}
+
static bool isMemoryCgroupSupported() {
- static bool memcg_supported = !access("/dev/memcg/memory.limit_in_bytes", F_OK);
+ std::string cgroup_name;
+ static bool memcg_supported = (CgroupMap::GetInstance().FindController("memory") != nullptr);
+
return memcg_supported;
}
+bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles) {
+ const TaskProfiles& tp = TaskProfiles::GetInstance();
+
+ for (const auto& name : profiles) {
+ const TaskProfile* profile = tp.GetProfile(name);
+ if (profile != nullptr) {
+ if (!profile->ExecuteForProcess(uid, pid)) {
+ PLOG(WARNING) << "Failed to apply " << name << " process profile";
+ }
+ } else {
+ PLOG(WARNING) << "Failed to find " << name << "process profile";
+ }
+ }
+
+ return true;
+}
+
+bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles) {
+ const TaskProfiles& tp = TaskProfiles::GetInstance();
+
+ for (const auto& name : profiles) {
+ const TaskProfile* profile = tp.GetProfile(name);
+ if (profile != nullptr) {
+ if (!profile->ExecuteForTask(tid)) {
+ PLOG(WARNING) << "Failed to apply " << name << " task profile";
+ }
+ } else {
+ PLOG(WARNING) << "Failed to find " << name << "task profile";
+ }
+ }
+
+ return true;
+}
+
static std::string ConvertUidToPath(const char* cgroup, uid_t uid) {
return StringPrintf("%s/uid_%d", cgroup, uid);
}
@@ -103,11 +190,21 @@
}
}
-void removeAllProcessGroups()
-{
+void removeAllProcessGroups() {
LOG(VERBOSE) << "removeAllProcessGroups()";
- for (const char* cgroup_root_path : {kCpuacctCgroup, kMemoryCgroup}) {
- std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path), closedir);
+
+ std::vector<std::string> cgroups;
+ std::string path;
+
+ if (CgroupGetControllerPath("cpuacct", &path)) {
+ cgroups.push_back(path);
+ }
+ if (CgroupGetControllerPath("memory", &path)) {
+ cgroups.push_back(path);
+ }
+
+ for (std::string cgroup_root_path : cgroups) {
+ std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path.c_str()), closedir);
if (root == NULL) {
PLOG(ERROR) << "Failed to open " << cgroup_root_path;
} else {
@@ -121,7 +218,7 @@
continue;
}
- auto path = StringPrintf("%s/%s", cgroup_root_path, dir->d_name);
+ auto path = StringPrintf("%s/%s", cgroup_root_path.c_str(), dir->d_name);
RemoveUidProcessGroups(path);
LOG(VERBOSE) << "Removing " << path;
if (rmdir(path.c_str()) == -1) PLOG(WARNING) << "Failed to remove " << path;
@@ -130,6 +227,21 @@
}
}
+static bool MkdirAndChown(const std::string& path, mode_t mode, uid_t uid, gid_t gid) {
+ if (mkdir(path.c_str(), mode) == -1 && errno != EEXIST) {
+ return false;
+ }
+
+ if (chown(path.c_str(), uid, gid) == -1) {
+ int saved_errno = errno;
+ rmdir(path.c_str());
+ errno = saved_errno;
+ return false;
+ }
+
+ return true;
+}
+
// Returns number of processes killed on success
// Returns 0 if there are no processes in the process cgroup left to kill
// Returns -1 on error
@@ -200,10 +312,16 @@
}
static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries) {
+ std::string cpuacct_path;
+ std::string memory_path;
+
+ CgroupGetControllerPath("cpuacct", &cpuacct_path);
+ CgroupGetControllerPath("memory", &memory_path);
+
const char* cgroup =
- (!access(ConvertUidPidToPath(kCpuacctCgroup, uid, initialPid).c_str(), F_OK))
- ? kCpuacctCgroup
- : kMemoryCgroup;
+ (!access(ConvertUidPidToPath(cpuacct_path.c_str(), uid, initialPid).c_str(), F_OK))
+ ? cpuacct_path.c_str()
+ : memory_path.c_str();
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
@@ -258,44 +376,22 @@
return KillProcessGroup(uid, initialPid, signal, 0 /*retries*/);
}
-static bool MkdirAndChown(const std::string& path, mode_t mode, uid_t uid, gid_t gid) {
- if (mkdir(path.c_str(), mode) == -1 && errno != EEXIST) {
- return false;
- }
-
- if (chown(path.c_str(), uid, gid) == -1) {
- int saved_errno = errno;
- rmdir(path.c_str());
- errno = saved_errno;
- return false;
- }
-
- return true;
-}
-
-static bool isPerAppMemcgEnabled() {
- static bool per_app_memcg =
- GetBoolProperty("ro.config.per_app_memcg", GetBoolProperty("ro.config.low_ram", false));
- return per_app_memcg;
-}
-
-int createProcessGroup(uid_t uid, int initialPid, bool memControl)
-{
- const char* cgroup;
- if (isMemoryCgroupSupported() && (memControl || isPerAppMemcgEnabled())) {
- cgroup = kMemoryCgroup;
+int createProcessGroup(uid_t uid, int initialPid, bool memControl) {
+ std::string cgroup;
+ if (isMemoryCgroupSupported() && (memControl || UsePerAppMemcg())) {
+ CgroupGetControllerPath("memory", &cgroup);
} else {
- cgroup = kCpuacctCgroup;
+ CgroupGetControllerPath("cpuacct", &cgroup);
}
- auto uid_path = ConvertUidToPath(cgroup, uid);
+ auto uid_path = ConvertUidToPath(cgroup.c_str(), uid);
if (!MkdirAndChown(uid_path, 0750, AID_SYSTEM, AID_SYSTEM)) {
PLOG(ERROR) << "Failed to make and chown " << uid_path;
return -errno;
}
- auto uid_pid_path = ConvertUidPidToPath(cgroup, uid, initialPid);
+ auto uid_pid_path = ConvertUidPidToPath(cgroup.c_str(), uid, initialPid);
if (!MkdirAndChown(uid_pid_path, 0750, AID_SYSTEM, AID_SYSTEM)) {
PLOG(ERROR) << "Failed to make and chown " << uid_pid_path;
@@ -313,13 +409,17 @@
return ret;
}
-static bool SetProcessGroupValue(uid_t uid, int pid, const std::string& file_name, int64_t value) {
+static bool SetProcessGroupValue(int tid, const std::string& attr_name, int64_t value) {
if (!isMemoryCgroupSupported()) {
PLOG(ERROR) << "Memcg is not mounted.";
return false;
}
- auto path = ConvertUidPidToPath(kMemoryCgroup, uid, pid) + file_name;
+ std::string path;
+ if (!CgroupGetAttributePathForTask(attr_name, tid, &path)) {
+ PLOG(ERROR) << "Failed to find attribute '" << attr_name << "'";
+ return false;
+ }
if (!WriteStringToFile(std::to_string(value), path)) {
PLOG(ERROR) << "Failed to write '" << value << "' to " << path;
@@ -328,14 +428,14 @@
return true;
}
-bool setProcessGroupSwappiness(uid_t uid, int pid, int swappiness) {
- return SetProcessGroupValue(uid, pid, "/memory.swappiness", swappiness);
+bool setProcessGroupSwappiness(uid_t, int pid, int swappiness) {
+ return SetProcessGroupValue(pid, "MemSwappiness", swappiness);
}
-bool setProcessGroupSoftLimit(uid_t uid, int pid, int64_t soft_limit_in_bytes) {
- return SetProcessGroupValue(uid, pid, "/memory.soft_limit_in_bytes", soft_limit_in_bytes);
+bool setProcessGroupSoftLimit(uid_t, int pid, int64_t soft_limit_in_bytes) {
+ return SetProcessGroupValue(pid, "MemSoftLimit", soft_limit_in_bytes);
}
-bool setProcessGroupLimit(uid_t uid, int pid, int64_t limit_in_bytes) {
- return SetProcessGroupValue(uid, pid, "/memory.limit_in_bytes", limit_in_bytes);
+bool setProcessGroupLimit(uid_t, int pid, int64_t limit_in_bytes) {
+ return SetProcessGroupValue(pid, "MemLimit", limit_in_bytes);
}