4cad4c
From 04f21709ff081a5f1a2b5ca746582a9c5c03db7f Mon Sep 17 00:00:00 2001
4cad4c
From: Tejun Heo <tj@kernel.org>
4cad4c
Date: Fri, 8 Jun 2018 17:33:14 -0700
4cad4c
Subject: [PATCH] core: add MemoryMin
4cad4c
4cad4c
The kernel added support for a new cgroup memory controller knob memory.min in
4cad4c
bf8d5d52ffe8 ("memcg: introduce memory.min") which was merged during v4.18
4cad4c
merge window.
4cad4c
4cad4c
Add MemoryMin to support memory.min.
4cad4c
4cad4c
(cherry picked from commit 484226357789991de0b3363beb69258be06b4c92)
4cad4c
4cad4c
Resolves: #1763435
4cad4c
---
4cad4c
 doc/TRANSIENT-SETTINGS.md             |  1 +
4cad4c
 man/systemd.resource-control.xml      | 21 +++++++++++++++++++++
4cad4c
 src/core/cgroup.c                     |  5 ++++-
4cad4c
 src/core/cgroup.h                     |  1 +
4cad4c
 src/core/dbus-cgroup.c                |  7 +++++++
4cad4c
 src/core/load-fragment-gperf.gperf.m4 |  1 +
4cad4c
 src/core/load-fragment.c              |  4 +++-
4cad4c
 src/shared/bus-unit-util.c            |  2 +-
4cad4c
 src/systemctl/systemctl.c             | 11 +++++++++--
4cad4c
 9 files changed, 48 insertions(+), 5 deletions(-)
4cad4c
4cad4c
diff --git a/doc/TRANSIENT-SETTINGS.md b/doc/TRANSIENT-SETTINGS.md
4cad4c
index 0d2d3e9065..93865c0333 100644
4cad4c
--- a/doc/TRANSIENT-SETTINGS.md
4cad4c
+++ b/doc/TRANSIENT-SETTINGS.md
4cad4c
@@ -222,6 +222,7 @@ All cgroup/resource control settings are available for transient units
4cad4c
 ✓ AllowedCPUs=
4cad4c
 ✓ AllowedMemoryNodes=
4cad4c
 ✓ MemoryAccounting=
4cad4c
+✓ MemoryMin=
4cad4c
 ✓ MemoryLow=
4cad4c
 ✓ MemoryHigh=
4cad4c
 ✓ MemoryMax=
4cad4c
diff --git a/man/systemd.resource-control.xml b/man/systemd.resource-control.xml
4cad4c
index cfe19a6574..63a0c87565 100644
4cad4c
--- a/man/systemd.resource-control.xml
4cad4c
+++ b/man/systemd.resource-control.xml
4cad4c
@@ -265,6 +265,27 @@
4cad4c
         </listitem>
4cad4c
       </varlistentry>
4cad4c
 
4cad4c
+      <varlistentry>
4cad4c
+        <term><varname>MemoryMin=<replaceable>bytes</replaceable></varname></term>
4cad4c
+
4cad4c
+        <listitem>
4cad4c
+          <para>Specify the memory usage protection of the executed processes in this unit. If the memory usages of
4cad4c
+          this unit and all its ancestors are below their minimum boundaries, this unit's memory won't be reclaimed.</para>
4cad4c
+
4cad4c
+          <para>Takes a memory size in bytes. If the value is suffixed with K, M, G or T, the specified memory size is
4cad4c
+          parsed as Kilobytes, Megabytes, Gigabytes, or Terabytes (with the base 1024), respectively. Alternatively, a
4cad4c
+          percentage value may be specified, which is taken relative to the installed physical memory on the
4cad4c
+          system. This controls the <literal>memory.min</literal> control group attribute. For details about this
4cad4c
+          control group attribute, see 
4cad4c
+          url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.</para>
4cad4c
+
4cad4c
+          <para>Implies <literal>MemoryAccounting=true</literal>.</para>
4cad4c
+
4cad4c
+          <para>This setting is supported only if the unified control group hierarchy is used and disables
4cad4c
+          <varname>MemoryLimit=</varname>.</para>
4cad4c
+        </listitem>
4cad4c
+      </varlistentry>
4cad4c
+
4cad4c
       <varlistentry>
4cad4c
         <term><varname>MemoryLow=<replaceable>bytes</replaceable></varname></term>
4cad4c
 
4cad4c
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
4cad4c
index f8b351a65d..9d09c65453 100644
4cad4c
--- a/src/core/cgroup.c
4cad4c
+++ b/src/core/cgroup.c
4cad4c
@@ -220,6 +220,7 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
4cad4c
                 "%sStartupIOWeight=%" PRIu64 "\n"
4cad4c
                 "%sBlockIOWeight=%" PRIu64 "\n"
4cad4c
                 "%sStartupBlockIOWeight=%" PRIu64 "\n"
4cad4c
+                "%sMemoryMin=%" PRIu64 "\n"
4cad4c
                 "%sMemoryLow=%" PRIu64 "\n"
4cad4c
                 "%sMemoryHigh=%" PRIu64 "\n"
4cad4c
                 "%sMemoryMax=%" PRIu64 "\n"
4cad4c
@@ -246,6 +247,7 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
4cad4c
                 prefix, c->startup_io_weight,
4cad4c
                 prefix, c->blockio_weight,
4cad4c
                 prefix, c->startup_blockio_weight,
4cad4c
+                prefix, c->memory_min,
4cad4c
                 prefix, c->memory_low,
4cad4c
                 prefix, c->memory_high,
4cad4c
                 prefix, c->memory_max,
4cad4c
@@ -777,7 +779,7 @@ static void cgroup_apply_blkio_device_limit(Unit *u, const char *dev_path, uint6
4cad4c
 }
4cad4c
 
4cad4c
 static bool cgroup_context_has_unified_memory_config(CGroupContext *c) {
4cad4c
-        return c->memory_low > 0 || c->memory_high != CGROUP_LIMIT_MAX || c->memory_max != CGROUP_LIMIT_MAX || c->memory_swap_max != CGROUP_LIMIT_MAX;
4cad4c
+        return c->memory_min > 0 || c->memory_low > 0 || c->memory_high != CGROUP_LIMIT_MAX || c->memory_max != CGROUP_LIMIT_MAX || c->memory_swap_max != CGROUP_LIMIT_MAX;
4cad4c
 }
4cad4c
 
4cad4c
 static void cgroup_apply_unified_memory_limit(Unit *u, const char *file, uint64_t v) {
4cad4c
@@ -1035,6 +1037,7 @@ static void cgroup_context_apply(
4cad4c
                                         log_cgroup_compat(u, "Applying MemoryLimit %" PRIu64 " as MemoryMax", max);
4cad4c
                         }
4cad4c
 
4cad4c
+                        cgroup_apply_unified_memory_limit(u, "memory.min", c->memory_min);
4cad4c
                         cgroup_apply_unified_memory_limit(u, "memory.low", c->memory_low);
4cad4c
                         cgroup_apply_unified_memory_limit(u, "memory.high", c->memory_high);
4cad4c
                         cgroup_apply_unified_memory_limit(u, "memory.max", max);
4cad4c
diff --git a/src/core/cgroup.h b/src/core/cgroup.h
4cad4c
index 2ba57d3ded..5e1be87b20 100644
4cad4c
--- a/src/core/cgroup.h
4cad4c
+++ b/src/core/cgroup.h
4cad4c
@@ -95,6 +95,7 @@ struct CGroupContext {
4cad4c
         LIST_HEAD(CGroupIODeviceLimit, io_device_limits);
4cad4c
         LIST_HEAD(CGroupIODeviceLatency, io_device_latencies);
4cad4c
 
4cad4c
+        uint64_t memory_min;
4cad4c
         uint64_t memory_low;
4cad4c
         uint64_t memory_high;
4cad4c
         uint64_t memory_max;
4cad4c
diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c
4cad4c
index 4555b33b1f..6ce5984a02 100644
4cad4c
--- a/src/core/dbus-cgroup.c
4cad4c
+++ b/src/core/dbus-cgroup.c
4cad4c
@@ -353,6 +353,7 @@ const sd_bus_vtable bus_cgroup_vtable[] = {
4cad4c
         SD_BUS_PROPERTY("BlockIOReadBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0),
4cad4c
         SD_BUS_PROPERTY("BlockIOWriteBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0),
4cad4c
         SD_BUS_PROPERTY("MemoryAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, memory_accounting), 0),
4cad4c
+        SD_BUS_PROPERTY("MemoryMin", "t", NULL, offsetof(CGroupContext, memory_min), 0),
4cad4c
         SD_BUS_PROPERTY("MemoryLow", "t", NULL, offsetof(CGroupContext, memory_low), 0),
4cad4c
         SD_BUS_PROPERTY("MemoryHigh", "t", NULL, offsetof(CGroupContext, memory_high), 0),
4cad4c
         SD_BUS_PROPERTY("MemoryMax", "t", NULL, offsetof(CGroupContext, memory_max), 0),
4cad4c
@@ -661,6 +662,9 @@ int bus_cgroup_set_property(
4cad4c
         if (streq(name, "MemoryAccounting"))
4cad4c
                 return bus_cgroup_set_boolean(u, name, &c->memory_accounting, CGROUP_MASK_MEMORY, message, flags, error);
4cad4c
 
4cad4c
+        if (streq(name, "MemoryMin"))
4cad4c
+                return bus_cgroup_set_memory(u, name, &c->memory_min, message, flags, error);
4cad4c
+
4cad4c
         if (streq(name, "MemoryLow"))
4cad4c
                 return bus_cgroup_set_memory(u, name, &c->memory_low, message, flags, error);
4cad4c
 
4cad4c
@@ -676,6 +680,9 @@ int bus_cgroup_set_property(
4cad4c
         if (streq(name, "MemoryLimit"))
4cad4c
                 return bus_cgroup_set_memory(u, name, &c->memory_limit, message, flags, error);
4cad4c
 
4cad4c
+        if (streq(name, "MemoryMinScale"))
4cad4c
+                return bus_cgroup_set_memory_scale(u, name, &c->memory_min, message, flags, error);
4cad4c
+
4cad4c
         if (streq(name, "MemoryLowScale"))
4cad4c
                 return bus_cgroup_set_memory_scale(u, name, &c->memory_low, message, flags, error);
4cad4c
 
4cad4c
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
4cad4c
index 4defa82ac1..1c6e539f30 100644
4cad4c
--- a/src/core/load-fragment-gperf.gperf.m4
4cad4c
+++ b/src/core/load-fragment-gperf.gperf.m4
4cad4c
@@ -171,6 +171,7 @@ $1.StartupCPUShares,             config_parse_cpu_shares,            0,
4cad4c
 $1.CPUQuota,                     config_parse_cpu_quota,             0,                             offsetof($1, cgroup_context)
4cad4c
 $1.CPUQuotaPeriodSec,            config_parse_sec_def_infinity,      0,                             offsetof($1, cgroup_context.cpu_quota_period_usec)
4cad4c
 $1.MemoryAccounting,             config_parse_bool,                  0,                             offsetof($1, cgroup_context.memory_accounting)
4cad4c
+$1.MemoryMin,                    config_parse_memory_limit,          0,                             offsetof($1, cgroup_context)
4cad4c
 $1.MemoryLow,                    config_parse_memory_limit,          0,                             offsetof($1, cgroup_context)
4cad4c
 $1.MemoryHigh,                   config_parse_memory_limit,          0,                             offsetof($1, cgroup_context)
4cad4c
 $1.MemoryMax,                    config_parse_memory_limit,          0,                             offsetof($1, cgroup_context)
4cad4c
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
4cad4c
index 762b106007..d43b0f08f9 100644
4cad4c
--- a/src/core/load-fragment.c
4cad4c
+++ b/src/core/load-fragment.c
4cad4c
@@ -3096,7 +3096,9 @@ int config_parse_memory_limit(
4cad4c
                 }
4cad4c
         }
4cad4c
 
4cad4c
-        if (streq(lvalue, "MemoryLow"))
4cad4c
+        if (streq(lvalue, "MemoryMin"))
4cad4c
+                c->memory_min = bytes;
4cad4c
+        else if (streq(lvalue, "MemoryLow"))
4cad4c
                 c->memory_low = bytes;
4cad4c
         else if (streq(lvalue, "MemoryHigh"))
4cad4c
                 c->memory_high = bytes;
4cad4c
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
4cad4c
index ec45d6f86d..203c068d02 100644
4cad4c
--- a/src/shared/bus-unit-util.c
4cad4c
+++ b/src/shared/bus-unit-util.c
4cad4c
@@ -429,7 +429,7 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
4cad4c
                 return 1;
4cad4c
         }
4cad4c
 
4cad4c
-        if (STR_IN_SET(field, "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit", "TasksMax")) {
4cad4c
+        if (STR_IN_SET(field, "MemoryMin", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit", "TasksMax")) {
4cad4c
 
4cad4c
                 if (isempty(eq) || streq(eq, "infinity")) {
4cad4c
                         r = sd_bus_message_append(m, "(sv)", field, "t", CGROUP_LIMIT_MAX);
4cad4c
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
4cad4c
index 559e49f104..35ad20f510 100644
4cad4c
--- a/src/systemctl/systemctl.c
4cad4c
+++ b/src/systemctl/systemctl.c
4cad4c
@@ -3905,6 +3905,7 @@ typedef struct UnitStatusInfo {
4cad4c
 
4cad4c
         /* CGroup */
4cad4c
         uint64_t memory_current;
4cad4c
+        uint64_t memory_min;
4cad4c
         uint64_t memory_low;
4cad4c
         uint64_t memory_high;
4cad4c
         uint64_t memory_max;
4cad4c
@@ -4284,12 +4285,17 @@ static void print_status_info(
4cad4c
 
4cad4c
                 printf("   Memory: %s", format_bytes(buf, sizeof(buf), i->memory_current));
4cad4c
 
4cad4c
-                if (i->memory_low > 0 || i->memory_high != CGROUP_LIMIT_MAX ||
4cad4c
-                    i->memory_max != CGROUP_LIMIT_MAX || i->memory_swap_max != CGROUP_LIMIT_MAX ||
4cad4c
+                if (i->memory_min > 0 || i->memory_low > 0 ||
4cad4c
+                    i->memory_high != CGROUP_LIMIT_MAX || i->memory_max != CGROUP_LIMIT_MAX ||
4cad4c
+                    i->memory_swap_max != CGROUP_LIMIT_MAX ||
4cad4c
                     i->memory_limit != CGROUP_LIMIT_MAX) {
4cad4c
                         const char *prefix = "";
4cad4c
 
4cad4c
                         printf(" (");
4cad4c
+                        if (i->memory_min > 0) {
4cad4c
+                                printf("%smin: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_min));
4cad4c
+                                prefix = " ";
4cad4c
+                        }
4cad4c
                         if (i->memory_low > 0) {
4cad4c
                                 printf("%slow: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_low));
4cad4c
                                 prefix = " ";
4cad4c
@@ -5022,6 +5028,7 @@ static int show_one(
4cad4c
                 { "Where",                          "s",              NULL,           offsetof(UnitStatusInfo, where)                             },
4cad4c
                 { "What",                           "s",              NULL,           offsetof(UnitStatusInfo, what)                              },
4cad4c
                 { "MemoryCurrent",                  "t",              NULL,           offsetof(UnitStatusInfo, memory_current)                    },
4cad4c
+                { "MemoryMin",                      "t",              NULL,           offsetof(UnitStatusInfo, memory_min)                        },
4cad4c
                 { "MemoryLow",                      "t",              NULL,           offsetof(UnitStatusInfo, memory_low)                        },
4cad4c
                 { "MemoryHigh",                     "t",              NULL,           offsetof(UnitStatusInfo, memory_high)                       },
4cad4c
                 { "MemoryMax",                      "t",              NULL,           offsetof(UnitStatusInfo, memory_max)                        },