Loading init/README.md +10 −3 Original line number Diff line number Diff line Loading @@ -235,9 +235,16 @@ runs the service. to "123,124,125". Since keycodes are handled very early in init, only PRODUCT_DEFAULT_PROPERTY_OVERRIDES properties can be used. `memcg.limit_in_bytes <value>` > Sets the child's memory.limit_in_bytes to the specified value (only if memcg is mounted), which must be equal or greater than 0. `memcg.limit_in_bytes <value>` and `memcg.limit_percent <value>` > Sets the child's memory.limit_in_bytes to the minimum of `limit_in_bytes` bytes and `limit_percent` which is interpreted as a percentage of the size of the device's physical memory (only if memcg is mounted). Values must be equal or greater than 0. `memcg.limit_property <value>` > Sets the child's memory.limit_in_bytes to the value of the specified property (only if memcg is mounted). This property will override the values specified via `memcg.limit_in_bytes` and `memcg.limit_percent`. `memcg.soft_limit_in_bytes <value>` > Sets the child's memory.soft_limit_in_bytes to the specified value (only if memcg is mounted), Loading init/service.cpp +43 −7 Original line number Diff line number Diff line Loading @@ -235,9 +235,6 @@ Service::Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid, ioprio_pri_(0), priority_(0), oom_score_adjust_(-1000), swappiness_(-1), soft_limit_in_bytes_(-1), limit_in_bytes_(-1), start_order_(0), args_(args) {} Loading Loading @@ -630,6 +627,18 @@ Result<Success> Service::ParseMemcgLimitInBytes(std::vector<std::string>&& args) return Success(); } Result<Success> Service::ParseMemcgLimitPercent(std::vector<std::string>&& args) { if (!ParseInt(args[1], &limit_percent_, 0)) { return Error() << "limit_percent value must be equal or greater than 0"; } return Success(); } Result<Success> Service::ParseMemcgLimitProperty(std::vector<std::string>&& args) { limit_property_ = std::move(args[1]); return Success(); } Result<Success> Service::ParseMemcgSoftLimitInBytes(std::vector<std::string>&& args) { if (!ParseInt(args[1], &soft_limit_in_bytes_, 0)) { return Error() << "soft_limit_in_bytes value must be equal or greater than 0"; Loading Loading @@ -783,6 +792,10 @@ const Service::OptionParserMap::Map& Service::OptionParserMap::map() const { {"keycodes", {1, kMax, &Service::ParseKeycodes}}, {"memcg.limit_in_bytes", {1, 1, &Service::ParseMemcgLimitInBytes}}, {"memcg.limit_percent", {1, 1, &Service::ParseMemcgLimitPercent}}, {"memcg.limit_property", {1, 1, &Service::ParseMemcgLimitProperty}}, {"memcg.soft_limit_in_bytes", {1, 1, &Service::ParseMemcgSoftLimitInBytes}}, {"memcg.swappiness", Loading Loading @@ -1001,11 +1014,13 @@ Result<Success> Service::Start() { start_order_ = next_start_order_++; process_cgroup_empty_ = false; errno = -createProcessGroup(uid_, pid_); bool use_memcg = swappiness_ != -1 || soft_limit_in_bytes_ != -1 || limit_in_bytes_ != -1 || limit_percent_ != -1 || !limit_property_.empty(); errno = -createProcessGroup(uid_, pid_, use_memcg); if (errno != 0) { PLOG(ERROR) << "createProcessGroup(" << uid_ << ", " << pid_ << ") failed for service '" << name_ << "'"; } else { } else if (use_memcg) { if (swappiness_ != -1) { if (!setProcessGroupSwappiness(uid_, pid_, swappiness_)) { PLOG(ERROR) << "setProcessGroupSwappiness failed"; Loading @@ -1018,8 +1033,29 @@ Result<Success> Service::Start() { } } if (limit_in_bytes_ != -1) { if (!setProcessGroupLimit(uid_, pid_, limit_in_bytes_)) { size_t computed_limit_in_bytes = limit_in_bytes_; if (limit_percent_ != -1) { long page_size = sysconf(_SC_PAGESIZE); long num_pages = sysconf(_SC_PHYS_PAGES); if (page_size > 0 && num_pages > 0) { size_t max_mem = SIZE_MAX; if (size_t(num_pages) < SIZE_MAX / size_t(page_size)) { max_mem = size_t(num_pages) * size_t(page_size); } computed_limit_in_bytes = std::min(computed_limit_in_bytes, max_mem / 100 * limit_percent_); } } if (!limit_property_.empty()) { // This ends up overwriting computed_limit_in_bytes but only if the // property is defined. computed_limit_in_bytes = android::base::GetUintProperty( limit_property_, computed_limit_in_bytes, SIZE_MAX); } if (computed_limit_in_bytes != size_t(-1)) { if (!setProcessGroupLimit(uid_, pid_, computed_limit_in_bytes)) { PLOG(ERROR) << "setProcessGroupLimit failed"; } } Loading init/service.h +8 −3 Original line number Diff line number Diff line Loading @@ -154,6 +154,8 @@ class Service { Result<Success> ParseOomScoreAdjust(std::vector<std::string>&& args); Result<Success> ParseOverride(std::vector<std::string>&& args); Result<Success> ParseMemcgLimitInBytes(std::vector<std::string>&& args); Result<Success> ParseMemcgLimitPercent(std::vector<std::string>&& args); Result<Success> ParseMemcgLimitProperty(std::vector<std::string>&& args); Result<Success> ParseMemcgSoftLimitInBytes(std::vector<std::string>&& args); Result<Success> ParseMemcgSwappiness(std::vector<std::string>&& args); Result<Success> ParseNamespace(std::vector<std::string>&& args); Loading Loading @@ -213,9 +215,12 @@ class Service { int oom_score_adjust_; int swappiness_; int soft_limit_in_bytes_; int limit_in_bytes_; int swappiness_ = -1; int soft_limit_in_bytes_ = -1; int limit_in_bytes_ = -1; int limit_percent_ = -1; std::string limit_property_; bool process_cgroup_empty_ = false; Loading libprocessgroup/include/processgroup/processgroup.h +3 −1 Original line number Diff line number Diff line Loading @@ -31,8 +31,10 @@ int killProcessGroup(uid_t uid, int initialPid, int signal); // that it only returns 0 in the case that the cgroup exists and it contains no processes. int killProcessGroupOnce(uid_t uid, int initialPid, int signal); int createProcessGroup(uid_t uid, int initialPid); int createProcessGroup(uid_t uid, int initialPid, bool memControl = false); // Set various properties of a process group. For these functions to work, the process group must // have been created by passing memControl=true to createProcessGroup. bool setProcessGroupSwappiness(uid_t uid, int initialPid, int swappiness); bool setProcessGroupSoftLimit(uid_t uid, int initialPid, int64_t softLimitInBytes); bool setProcessGroupLimit(uid_t uid, int initialPid, int64_t limitInBytes); Loading libprocessgroup/processgroup.cpp +58 −57 Original line number Diff line number Diff line Loading @@ -53,49 +53,31 @@ using android::base::WriteStringToFile; using namespace std::chrono_literals; #define MEM_CGROUP_PATH "/dev/memcg/apps" #define MEM_CGROUP_TASKS "/dev/memcg/apps/tasks" #define ACCT_CGROUP_PATH "/acct" static const char kCpuacctCgroup[] = "/acct"; static const char kMemoryCgroup[] = "/dev/memcg/apps"; #define PROCESSGROUP_CGROUP_PROCS_FILE "/cgroup.procs" std::once_flag init_path_flag; static const std::string& GetCgroupRootPath() { static std::string cgroup_root_path; std::call_once(init_path_flag, [&]() { // low-ram devices use per-app memcg by default, unlike high-end ones bool low_ram_device = GetBoolProperty("ro.config.low_ram", false); bool per_app_memcg = GetBoolProperty("ro.config.per_app_memcg", low_ram_device); if (per_app_memcg) { // Check if mem cgroup is mounted, only then check for // write-access to avoid SELinux denials cgroup_root_path = (access(MEM_CGROUP_TASKS, F_OK) || access(MEM_CGROUP_PATH, W_OK) ? ACCT_CGROUP_PATH : MEM_CGROUP_PATH); } else { cgroup_root_path = ACCT_CGROUP_PATH; } }); return cgroup_root_path; static bool isMemoryCgroupSupported() { static bool memcg_supported = !access("/dev/memcg/memory.limit_in_bytes", F_OK); return memcg_supported; } static std::string ConvertUidToPath(uid_t uid) { return StringPrintf("%s/uid_%d", GetCgroupRootPath().c_str(), uid); static std::string ConvertUidToPath(const char* cgroup, uid_t uid) { return StringPrintf("%s/uid_%d", cgroup, uid); } static std::string ConvertUidPidToPath(uid_t uid, int pid) { return StringPrintf("%s/uid_%d/pid_%d", GetCgroupRootPath().c_str(), uid, pid); static std::string ConvertUidPidToPath(const char* cgroup, uid_t uid, int pid) { return StringPrintf("%s/uid_%d/pid_%d", cgroup, uid, pid); } static int RemoveProcessGroup(uid_t uid, int pid) { static int RemoveProcessGroup(const char* cgroup, uid_t uid, int pid) { int ret; auto uid_pid_path = ConvertUidPidToPath(uid, pid); auto uid_pid_path = ConvertUidPidToPath(cgroup, uid, pid); ret = rmdir(uid_pid_path.c_str()); auto uid_path = ConvertUidToPath(uid); auto uid_path = ConvertUidToPath(cgroup, uid); rmdir(uid_path.c_str()); return ret; Loading Loading @@ -124,8 +106,8 @@ static void RemoveUidProcessGroups(const std::string& uid_path) { void removeAllProcessGroups() { LOG(VERBOSE) << "removeAllProcessGroups()"; const auto& cgroup_root_path = GetCgroupRootPath(); std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path.c_str()), closedir); for (const char* cgroup_root_path : {kCpuacctCgroup, kMemoryCgroup}) { std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path), closedir); if (root == NULL) { PLOG(ERROR) << "Failed to open " << cgroup_root_path; } else { Loading @@ -139,19 +121,20 @@ void removeAllProcessGroups() continue; } auto path = StringPrintf("%s/%s", cgroup_root_path.c_str(), dir->d_name); auto path = StringPrintf("%s/%s", cgroup_root_path, dir->d_name); RemoveUidProcessGroups(path); LOG(VERBOSE) << "Removing " << path; if (rmdir(path.c_str()) == -1) PLOG(WARNING) << "Failed to remove " << path; } } } } // 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 static int DoKillProcessGroupOnce(uid_t uid, int initialPid, int signal) { auto path = ConvertUidPidToPath(uid, initialPid) + PROCESSGROUP_CGROUP_PROCS_FILE; static int DoKillProcessGroupOnce(const char* cgroup, uid_t uid, int initialPid, int signal) { auto path = ConvertUidPidToPath(cgroup, uid, initialPid) + PROCESSGROUP_CGROUP_PROCS_FILE; std::unique_ptr<FILE, decltype(&fclose)> fd(fopen(path.c_str(), "re"), fclose); if (!fd) { PLOG(WARNING) << "Failed to open process cgroup uid " << uid << " pid " << initialPid; Loading Loading @@ -217,11 +200,16 @@ static int DoKillProcessGroupOnce(uid_t uid, int initialPid, int signal) { } static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries) { const char* cgroup = (!access(ConvertUidPidToPath(kCpuacctCgroup, uid, initialPid).c_str(), F_OK)) ? kCpuacctCgroup : kMemoryCgroup; std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); int retry = retries; int processes; while ((processes = DoKillProcessGroupOnce(uid, initialPid, signal)) > 0) { while ((processes = DoKillProcessGroupOnce(cgroup, uid, initialPid, signal)) > 0) { LOG(VERBOSE) << "Killed " << processes << " processes for processgroup " << initialPid; if (retry > 0) { std::this_thread::sleep_for(5ms); Loading Loading @@ -251,7 +239,7 @@ static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries) LOG(INFO) << "Successfully killed process cgroup uid " << uid << " pid " << initialPid << " in " << static_cast<int>(ms) << "ms"; } return RemoveProcessGroup(uid, initialPid); return RemoveProcessGroup(cgroup, uid, initialPid); } else { if (retries > 0) { LOG(ERROR) << "Failed to kill process cgroup uid " << uid << " pid " << initialPid Loading Loading @@ -285,16 +273,29 @@ static bool MkdirAndChown(const std::string& path, mode_t mode, uid_t uid, gid_t return true; } int createProcessGroup(uid_t uid, int initialPid) 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) { auto uid_path = ConvertUidToPath(uid); const char* cgroup; if (isMemoryCgroupSupported() && (memControl || isPerAppMemcgEnabled())) { cgroup = kMemoryCgroup; } else { cgroup = kCpuacctCgroup; } auto uid_path = ConvertUidToPath(cgroup, 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(uid, initialPid); auto uid_pid_path = ConvertUidPidToPath(cgroup, uid, initialPid); if (!MkdirAndChown(uid_pid_path, 0750, AID_SYSTEM, AID_SYSTEM)) { PLOG(ERROR) << "Failed to make and chown " << uid_pid_path; Loading @@ -313,12 +314,12 @@ int createProcessGroup(uid_t uid, int initialPid) } static bool SetProcessGroupValue(uid_t uid, int pid, const std::string& file_name, int64_t value) { if (GetCgroupRootPath() != MEM_CGROUP_PATH) { if (!isMemoryCgroupSupported()) { PLOG(ERROR) << "Memcg is not mounted."; return false; } auto path = ConvertUidPidToPath(uid, pid) + file_name; auto path = ConvertUidPidToPath(kMemoryCgroup, uid, pid) + file_name; if (!WriteStringToFile(std::to_string(value), path)) { PLOG(ERROR) << "Failed to write '" << value << "' to " << path; Loading Loading
init/README.md +10 −3 Original line number Diff line number Diff line Loading @@ -235,9 +235,16 @@ runs the service. to "123,124,125". Since keycodes are handled very early in init, only PRODUCT_DEFAULT_PROPERTY_OVERRIDES properties can be used. `memcg.limit_in_bytes <value>` > Sets the child's memory.limit_in_bytes to the specified value (only if memcg is mounted), which must be equal or greater than 0. `memcg.limit_in_bytes <value>` and `memcg.limit_percent <value>` > Sets the child's memory.limit_in_bytes to the minimum of `limit_in_bytes` bytes and `limit_percent` which is interpreted as a percentage of the size of the device's physical memory (only if memcg is mounted). Values must be equal or greater than 0. `memcg.limit_property <value>` > Sets the child's memory.limit_in_bytes to the value of the specified property (only if memcg is mounted). This property will override the values specified via `memcg.limit_in_bytes` and `memcg.limit_percent`. `memcg.soft_limit_in_bytes <value>` > Sets the child's memory.soft_limit_in_bytes to the specified value (only if memcg is mounted), Loading
init/service.cpp +43 −7 Original line number Diff line number Diff line Loading @@ -235,9 +235,6 @@ Service::Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid, ioprio_pri_(0), priority_(0), oom_score_adjust_(-1000), swappiness_(-1), soft_limit_in_bytes_(-1), limit_in_bytes_(-1), start_order_(0), args_(args) {} Loading Loading @@ -630,6 +627,18 @@ Result<Success> Service::ParseMemcgLimitInBytes(std::vector<std::string>&& args) return Success(); } Result<Success> Service::ParseMemcgLimitPercent(std::vector<std::string>&& args) { if (!ParseInt(args[1], &limit_percent_, 0)) { return Error() << "limit_percent value must be equal or greater than 0"; } return Success(); } Result<Success> Service::ParseMemcgLimitProperty(std::vector<std::string>&& args) { limit_property_ = std::move(args[1]); return Success(); } Result<Success> Service::ParseMemcgSoftLimitInBytes(std::vector<std::string>&& args) { if (!ParseInt(args[1], &soft_limit_in_bytes_, 0)) { return Error() << "soft_limit_in_bytes value must be equal or greater than 0"; Loading Loading @@ -783,6 +792,10 @@ const Service::OptionParserMap::Map& Service::OptionParserMap::map() const { {"keycodes", {1, kMax, &Service::ParseKeycodes}}, {"memcg.limit_in_bytes", {1, 1, &Service::ParseMemcgLimitInBytes}}, {"memcg.limit_percent", {1, 1, &Service::ParseMemcgLimitPercent}}, {"memcg.limit_property", {1, 1, &Service::ParseMemcgLimitProperty}}, {"memcg.soft_limit_in_bytes", {1, 1, &Service::ParseMemcgSoftLimitInBytes}}, {"memcg.swappiness", Loading Loading @@ -1001,11 +1014,13 @@ Result<Success> Service::Start() { start_order_ = next_start_order_++; process_cgroup_empty_ = false; errno = -createProcessGroup(uid_, pid_); bool use_memcg = swappiness_ != -1 || soft_limit_in_bytes_ != -1 || limit_in_bytes_ != -1 || limit_percent_ != -1 || !limit_property_.empty(); errno = -createProcessGroup(uid_, pid_, use_memcg); if (errno != 0) { PLOG(ERROR) << "createProcessGroup(" << uid_ << ", " << pid_ << ") failed for service '" << name_ << "'"; } else { } else if (use_memcg) { if (swappiness_ != -1) { if (!setProcessGroupSwappiness(uid_, pid_, swappiness_)) { PLOG(ERROR) << "setProcessGroupSwappiness failed"; Loading @@ -1018,8 +1033,29 @@ Result<Success> Service::Start() { } } if (limit_in_bytes_ != -1) { if (!setProcessGroupLimit(uid_, pid_, limit_in_bytes_)) { size_t computed_limit_in_bytes = limit_in_bytes_; if (limit_percent_ != -1) { long page_size = sysconf(_SC_PAGESIZE); long num_pages = sysconf(_SC_PHYS_PAGES); if (page_size > 0 && num_pages > 0) { size_t max_mem = SIZE_MAX; if (size_t(num_pages) < SIZE_MAX / size_t(page_size)) { max_mem = size_t(num_pages) * size_t(page_size); } computed_limit_in_bytes = std::min(computed_limit_in_bytes, max_mem / 100 * limit_percent_); } } if (!limit_property_.empty()) { // This ends up overwriting computed_limit_in_bytes but only if the // property is defined. computed_limit_in_bytes = android::base::GetUintProperty( limit_property_, computed_limit_in_bytes, SIZE_MAX); } if (computed_limit_in_bytes != size_t(-1)) { if (!setProcessGroupLimit(uid_, pid_, computed_limit_in_bytes)) { PLOG(ERROR) << "setProcessGroupLimit failed"; } } Loading
init/service.h +8 −3 Original line number Diff line number Diff line Loading @@ -154,6 +154,8 @@ class Service { Result<Success> ParseOomScoreAdjust(std::vector<std::string>&& args); Result<Success> ParseOverride(std::vector<std::string>&& args); Result<Success> ParseMemcgLimitInBytes(std::vector<std::string>&& args); Result<Success> ParseMemcgLimitPercent(std::vector<std::string>&& args); Result<Success> ParseMemcgLimitProperty(std::vector<std::string>&& args); Result<Success> ParseMemcgSoftLimitInBytes(std::vector<std::string>&& args); Result<Success> ParseMemcgSwappiness(std::vector<std::string>&& args); Result<Success> ParseNamespace(std::vector<std::string>&& args); Loading Loading @@ -213,9 +215,12 @@ class Service { int oom_score_adjust_; int swappiness_; int soft_limit_in_bytes_; int limit_in_bytes_; int swappiness_ = -1; int soft_limit_in_bytes_ = -1; int limit_in_bytes_ = -1; int limit_percent_ = -1; std::string limit_property_; bool process_cgroup_empty_ = false; Loading
libprocessgroup/include/processgroup/processgroup.h +3 −1 Original line number Diff line number Diff line Loading @@ -31,8 +31,10 @@ int killProcessGroup(uid_t uid, int initialPid, int signal); // that it only returns 0 in the case that the cgroup exists and it contains no processes. int killProcessGroupOnce(uid_t uid, int initialPid, int signal); int createProcessGroup(uid_t uid, int initialPid); int createProcessGroup(uid_t uid, int initialPid, bool memControl = false); // Set various properties of a process group. For these functions to work, the process group must // have been created by passing memControl=true to createProcessGroup. bool setProcessGroupSwappiness(uid_t uid, int initialPid, int swappiness); bool setProcessGroupSoftLimit(uid_t uid, int initialPid, int64_t softLimitInBytes); bool setProcessGroupLimit(uid_t uid, int initialPid, int64_t limitInBytes); Loading
libprocessgroup/processgroup.cpp +58 −57 Original line number Diff line number Diff line Loading @@ -53,49 +53,31 @@ using android::base::WriteStringToFile; using namespace std::chrono_literals; #define MEM_CGROUP_PATH "/dev/memcg/apps" #define MEM_CGROUP_TASKS "/dev/memcg/apps/tasks" #define ACCT_CGROUP_PATH "/acct" static const char kCpuacctCgroup[] = "/acct"; static const char kMemoryCgroup[] = "/dev/memcg/apps"; #define PROCESSGROUP_CGROUP_PROCS_FILE "/cgroup.procs" std::once_flag init_path_flag; static const std::string& GetCgroupRootPath() { static std::string cgroup_root_path; std::call_once(init_path_flag, [&]() { // low-ram devices use per-app memcg by default, unlike high-end ones bool low_ram_device = GetBoolProperty("ro.config.low_ram", false); bool per_app_memcg = GetBoolProperty("ro.config.per_app_memcg", low_ram_device); if (per_app_memcg) { // Check if mem cgroup is mounted, only then check for // write-access to avoid SELinux denials cgroup_root_path = (access(MEM_CGROUP_TASKS, F_OK) || access(MEM_CGROUP_PATH, W_OK) ? ACCT_CGROUP_PATH : MEM_CGROUP_PATH); } else { cgroup_root_path = ACCT_CGROUP_PATH; } }); return cgroup_root_path; static bool isMemoryCgroupSupported() { static bool memcg_supported = !access("/dev/memcg/memory.limit_in_bytes", F_OK); return memcg_supported; } static std::string ConvertUidToPath(uid_t uid) { return StringPrintf("%s/uid_%d", GetCgroupRootPath().c_str(), uid); static std::string ConvertUidToPath(const char* cgroup, uid_t uid) { return StringPrintf("%s/uid_%d", cgroup, uid); } static std::string ConvertUidPidToPath(uid_t uid, int pid) { return StringPrintf("%s/uid_%d/pid_%d", GetCgroupRootPath().c_str(), uid, pid); static std::string ConvertUidPidToPath(const char* cgroup, uid_t uid, int pid) { return StringPrintf("%s/uid_%d/pid_%d", cgroup, uid, pid); } static int RemoveProcessGroup(uid_t uid, int pid) { static int RemoveProcessGroup(const char* cgroup, uid_t uid, int pid) { int ret; auto uid_pid_path = ConvertUidPidToPath(uid, pid); auto uid_pid_path = ConvertUidPidToPath(cgroup, uid, pid); ret = rmdir(uid_pid_path.c_str()); auto uid_path = ConvertUidToPath(uid); auto uid_path = ConvertUidToPath(cgroup, uid); rmdir(uid_path.c_str()); return ret; Loading Loading @@ -124,8 +106,8 @@ static void RemoveUidProcessGroups(const std::string& uid_path) { void removeAllProcessGroups() { LOG(VERBOSE) << "removeAllProcessGroups()"; const auto& cgroup_root_path = GetCgroupRootPath(); std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path.c_str()), closedir); for (const char* cgroup_root_path : {kCpuacctCgroup, kMemoryCgroup}) { std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path), closedir); if (root == NULL) { PLOG(ERROR) << "Failed to open " << cgroup_root_path; } else { Loading @@ -139,19 +121,20 @@ void removeAllProcessGroups() continue; } auto path = StringPrintf("%s/%s", cgroup_root_path.c_str(), dir->d_name); auto path = StringPrintf("%s/%s", cgroup_root_path, dir->d_name); RemoveUidProcessGroups(path); LOG(VERBOSE) << "Removing " << path; if (rmdir(path.c_str()) == -1) PLOG(WARNING) << "Failed to remove " << path; } } } } // 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 static int DoKillProcessGroupOnce(uid_t uid, int initialPid, int signal) { auto path = ConvertUidPidToPath(uid, initialPid) + PROCESSGROUP_CGROUP_PROCS_FILE; static int DoKillProcessGroupOnce(const char* cgroup, uid_t uid, int initialPid, int signal) { auto path = ConvertUidPidToPath(cgroup, uid, initialPid) + PROCESSGROUP_CGROUP_PROCS_FILE; std::unique_ptr<FILE, decltype(&fclose)> fd(fopen(path.c_str(), "re"), fclose); if (!fd) { PLOG(WARNING) << "Failed to open process cgroup uid " << uid << " pid " << initialPid; Loading Loading @@ -217,11 +200,16 @@ static int DoKillProcessGroupOnce(uid_t uid, int initialPid, int signal) { } static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries) { const char* cgroup = (!access(ConvertUidPidToPath(kCpuacctCgroup, uid, initialPid).c_str(), F_OK)) ? kCpuacctCgroup : kMemoryCgroup; std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); int retry = retries; int processes; while ((processes = DoKillProcessGroupOnce(uid, initialPid, signal)) > 0) { while ((processes = DoKillProcessGroupOnce(cgroup, uid, initialPid, signal)) > 0) { LOG(VERBOSE) << "Killed " << processes << " processes for processgroup " << initialPid; if (retry > 0) { std::this_thread::sleep_for(5ms); Loading Loading @@ -251,7 +239,7 @@ static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries) LOG(INFO) << "Successfully killed process cgroup uid " << uid << " pid " << initialPid << " in " << static_cast<int>(ms) << "ms"; } return RemoveProcessGroup(uid, initialPid); return RemoveProcessGroup(cgroup, uid, initialPid); } else { if (retries > 0) { LOG(ERROR) << "Failed to kill process cgroup uid " << uid << " pid " << initialPid Loading Loading @@ -285,16 +273,29 @@ static bool MkdirAndChown(const std::string& path, mode_t mode, uid_t uid, gid_t return true; } int createProcessGroup(uid_t uid, int initialPid) 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) { auto uid_path = ConvertUidToPath(uid); const char* cgroup; if (isMemoryCgroupSupported() && (memControl || isPerAppMemcgEnabled())) { cgroup = kMemoryCgroup; } else { cgroup = kCpuacctCgroup; } auto uid_path = ConvertUidToPath(cgroup, 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(uid, initialPid); auto uid_pid_path = ConvertUidPidToPath(cgroup, uid, initialPid); if (!MkdirAndChown(uid_pid_path, 0750, AID_SYSTEM, AID_SYSTEM)) { PLOG(ERROR) << "Failed to make and chown " << uid_pid_path; Loading @@ -313,12 +314,12 @@ int createProcessGroup(uid_t uid, int initialPid) } static bool SetProcessGroupValue(uid_t uid, int pid, const std::string& file_name, int64_t value) { if (GetCgroupRootPath() != MEM_CGROUP_PATH) { if (!isMemoryCgroupSupported()) { PLOG(ERROR) << "Memcg is not mounted."; return false; } auto path = ConvertUidPidToPath(uid, pid) + file_name; auto path = ConvertUidPidToPath(kMemoryCgroup, uid, pid) + file_name; if (!WriteStringToFile(std::to_string(value), path)) { PLOG(ERROR) << "Failed to write '" << value << "' to " << path; Loading