Introduce additional service options for controlling memory cgroups.
The memcg.limit_percent option can be used to limit the cgroup's
max RSS to the given value as a percentage of the device's physical
memory. The memcg.limit_property option specifies the name of a
property that can be used to control the cgroup's max RSS. These
new options correspond to the arguments to the limitProcessMemory
function in frameworks/av/media/libmedia/MediaUtils.cpp; this will
allow us to add these options to the rc files for the programs that
call this function and then remove the callers in a later change.
There is also a change in semantics: the memcg.* options now have
an effect on all devices which support memory cgroups, not just
those with ro.config.low_ram or ro.config.per_app_memcg set to true.
This change also brings the semantics in line with the documentation,
so it looks like the previous semantics were unintentional.
Change-Id: I9495826de6e477b952e23866743b5fa600adcacb
Bug: 118642754
diff --git a/init/README.md b/init/README.md
index 6c51b37..2c531df 100644
--- a/init/README.md
+++ b/init/README.md
@@ -235,9 +235,16 @@
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),
diff --git a/init/service.cpp b/init/service.cpp
index 7f49423..1bda7ec 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -235,9 +235,6 @@
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) {}
@@ -630,6 +627,18 @@
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";
@@ -783,6 +792,10 @@
{"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",
@@ -1001,11 +1014,13 @@
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";
@@ -1018,8 +1033,29 @@
}
}
- 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";
}
}
diff --git a/init/service.h b/init/service.h
index c7beee9..49b09ce 100644
--- a/init/service.h
+++ b/init/service.h
@@ -154,6 +154,8 @@
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);
@@ -213,9 +215,12 @@
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;