From d485226951013247dbb502fef7edbc6cf83acb0f Mon Sep 17 00:00:00 2001 From: Robert Benea Date: Sun, 16 Jul 2017 19:38:11 -0700 Subject: [PATCH] Add memcg related configs to init. Allow configuring memory.swappiness, memory.soft_limit_in_bytes and memory.limit_in_bytes by init; by doing so there is better control of memory consumption per native app. Test: tested on gobo branch. bug: 63765067 Change-Id: I8906f3ff5ef77f75a0f4cdfbf9d424a579ed52bb --- init/README.md | 12 +++++ init/service.cpp | 54 +++++++++++++++++++ init/service.h | 7 +++ .../include/processgroup/processgroup.h | 4 ++ libprocessgroup/processgroup.cpp | 47 +++++++++++----- 5 files changed, 111 insertions(+), 13 deletions(-) diff --git a/init/README.md b/init/README.md index 422fdad5a..f3b57bc33 100644 --- a/init/README.md +++ b/init/README.md @@ -260,6 +260,18 @@ runs the service. > Sets the child's /proc/self/oom\_score\_adj to the specified value, which must range from -1000 to 1000. +`memcg.swappiness ` +> Sets the child's memory.swappiness to the specified value (only if memcg is mounted), + which must be equal or greater than 0. + +`memcg.soft_limit_in_bytes ` +> Sets the child's memory.soft_limit_in_bytes to the specified value (only if memcg is mounted), + which must be equal or greater than 0. + +`memcg.limit_in_bytes ` +> 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. + `shutdown ` > Set shutdown behavior of the service process. When this is not specified, the service is killed during shutdown process by using SIGTERM and SIGKILL. diff --git a/init/service.cpp b/init/service.cpp index 7a657c8ae..82dd9b137 100644 --- a/init/service.cpp +++ b/init/service.cpp @@ -171,6 +171,9 @@ Service::Service(const std::string& name, const std::vector& args) ioprio_pri_(0), priority_(0), oom_score_adjust_(-1000), + swappiness_(-1), + soft_limit_in_bytes_(-1), + limit_in_bytes_(-1), args_(args) { onrestart_.InitSingleTrigger("onrestart"); } @@ -196,6 +199,9 @@ 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), args_(args) { onrestart_.InitSingleTrigger("onrestart"); } @@ -491,6 +497,30 @@ bool Service::ParseOomScoreAdjust(const std::vector& args, std::str return true; } +bool Service::ParseMemcgSwappiness(const std::vector& args, std::string* err) { + if (!ParseInt(args[1], &swappiness_, 0)) { + *err = "swappiness value must be equal or greater than 0"; + return false; + } + return true; +} + +bool Service::ParseMemcgLimitInBytes(const std::vector& args, std::string* err) { + if (!ParseInt(args[1], &limit_in_bytes_, 0)) { + *err = "limit_in_bytes value must be equal or greater than 0"; + return false; + } + return true; +} + +bool Service::ParseMemcgSoftLimitInBytes(const std::vector& args, std::string* err) { + if (!ParseInt(args[1], &soft_limit_in_bytes_, 0)) { + *err = "soft_limit_in_bytes value must be equal or greater than 0"; + return false; + } + return true; +} + bool Service::ParseSeclabel(const std::vector& args, std::string* err) { seclabel_ = args[1]; return true; @@ -609,6 +639,12 @@ const Service::OptionParserMap::Map& Service::OptionParserMap::map() const { {"onrestart", {1, kMax, &Service::ParseOnrestart}}, {"oom_score_adjust", {1, 1, &Service::ParseOomScoreAdjust}}, + {"memcg.swappiness", + {1, 1, &Service::ParseMemcgSwappiness}}, + {"memcg.soft_limit_in_bytes", + {1, 1, &Service::ParseMemcgSoftLimitInBytes}}, + {"memcg.limit_in_bytes", + {1, 1, &Service::ParseMemcgLimitInBytes}}, {"namespace", {1, 2, &Service::ParseNamespace}}, {"seclabel", {1, 1, &Service::ParseSeclabel}}, {"setenv", {2, 2, &Service::ParseSetenv}}, @@ -795,6 +831,24 @@ bool Service::Start() { if (errno != 0) { PLOG(ERROR) << "createProcessGroup(" << uid_ << ", " << pid_ << ") failed for service '" << name_ << "'"; + } else { + if (swappiness_ != -1) { + if (!setProcessGroupSwappiness(uid_, pid_, swappiness_)) { + PLOG(ERROR) << "setProcessGroupSwappiness failed"; + } + } + + if (soft_limit_in_bytes_ != -1) { + if (!setProcessGroupSoftLimit(uid_, pid_, soft_limit_in_bytes_)) { + PLOG(ERROR) << "setProcessGroupSoftLimit failed"; + } + } + + if (limit_in_bytes_ != -1) { + if (!setProcessGroupLimit(uid_, pid_, limit_in_bytes_)) { + PLOG(ERROR) << "setProcessGroupLimit failed"; + } + } } if ((flags_ & SVC_EXEC) != 0) { diff --git a/init/service.h b/init/service.h index f682abd36..62a3299e1 100644 --- a/init/service.h +++ b/init/service.h @@ -134,6 +134,9 @@ class Service { bool ParseOneshot(const std::vector& args, std::string* err); bool ParseOnrestart(const std::vector& args, std::string* err); bool ParseOomScoreAdjust(const std::vector& args, std::string* err); + bool ParseMemcgLimitInBytes(const std::vector& args, std::string* err); + bool ParseMemcgSoftLimitInBytes(const std::vector& args, std::string* err); + bool ParseMemcgSwappiness(const std::vector& args, std::string* err); bool ParseNamespace(const std::vector& args, std::string* err); bool ParseSeclabel(const std::vector& args, std::string* err); bool ParseSetenv(const std::vector& args, std::string* err); @@ -181,6 +184,10 @@ class Service { int oom_score_adjust_; + int swappiness_; + int soft_limit_in_bytes_; + int limit_in_bytes_; + bool process_cgroup_empty_ = false; std::vector args_; diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h index f0c37954c..9fa4154ab 100644 --- a/libprocessgroup/include/processgroup/processgroup.h +++ b/libprocessgroup/include/processgroup/processgroup.h @@ -33,6 +33,10 @@ int killProcessGroupOnce(uid_t uid, int initialPid, int signal); int createProcessGroup(uid_t uid, int initialPid); +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); + void removeAllProcessGroups(void); __END_DECLS diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp index 9b8248e88..8526b3a29 100644 --- a/libprocessgroup/processgroup.cpp +++ b/libprocessgroup/processgroup.cpp @@ -35,12 +35,15 @@ #include #include +#include #include #include #include #include +using android::base::WriteStringToFile; + using namespace std::chrono_literals; #define MEM_CGROUP_PATH "/dev/memcg/apps" @@ -402,22 +405,40 @@ int createProcessGroup(uid_t uid, int initialPid) strlcat(path, PROCESSGROUP_CGROUP_PROCS_FILE, sizeof(path)); - int fd = open(path, O_WRONLY); - if (fd == -1) { - int ret = -errno; - PLOG(ERROR) << "Failed to open " << path; - return ret; - } - - char pid[PROCESSGROUP_MAX_PID_LEN + 1] = {0}; - int len = snprintf(pid, sizeof(pid), "%d", initialPid); - int ret = 0; - if (write(fd, pid, len) < 0) { + if (!WriteStringToFile(std::to_string(initialPid), path)) { ret = -errno; - PLOG(ERROR) << "Failed to write '" << pid << "' to " << path; + PLOG(ERROR) << "Failed to write '" << initialPid << "' to " << path; } - close(fd); return ret; } + +static bool setProcessGroupValue(uid_t uid, int pid, const char* fileName, int64_t value) { + char path[PROCESSGROUP_MAX_PATH_LEN] = {0}; + if (strcmp(getCgroupRootPath(), MEM_CGROUP_PATH)) { + PLOG(ERROR) << "Memcg is not mounted." << path; + return false; + } + + convertUidPidToPath(path, sizeof(path), uid, pid); + strlcat(path, fileName, sizeof(path)); + + if (!WriteStringToFile(std::to_string(value), path)) { + PLOG(ERROR) << "Failed to write '" << value << "' to " << path; + return false; + } + return true; +} + +bool setProcessGroupSwappiness(uid_t uid, int pid, int swappiness) { + return setProcessGroupValue(uid, pid, "/memory.swappiness", 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 setProcessGroupLimit(uid_t uid, int pid, int64_t limit_in_bytes) { + return setProcessGroupValue(uid, pid, "/memory.limit_in_bytes", limit_in_bytes); +}