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);
 }