anitazha / rpms / systemd

Forked from rpms/systemd 3 years ago
Clone
923a60
From 7d44d0d43465892d4753ff50592588f49d56cf95 Mon Sep 17 00:00:00 2001
923a60
From: Lennart Poettering <lennart@poettering.net>
923a60
Date: Thu, 10 Sep 2015 12:32:16 +0200
923a60
Subject: [PATCH] core: add support for the "pids" cgroup controller
923a60
923a60
This adds support for the new "pids" cgroup controller of 4.3 kernels.
923a60
It allows accounting the number of tasks in a cgroup and enforcing
923a60
limits on it.
923a60
923a60
This adds two new setting TasksAccounting= and TasksMax= to each unit,
923a60
as well as a gloabl option DefaultTasksAccounting=.
923a60
923a60
This also updated "cgtop" to optionally make use of the new
923a60
kernel-provided accounting.
923a60
923a60
systemctl has been updated to show the number of tasks for each service
923a60
if it is available.
923a60
923a60
This patch also adds correct support for undoing memory limits for units
923a60
using a MemoryLimit=infinity syntax. We do the same for TasksMax= now
923a60
and hence keep things in sync here.
923a60
923a60
Cherry-picked from: 03a7b521e3ffb7f5d153d90480ba5d4bc29d1e8f
923a60
Resolves: #1337244
923a60
---
923a60
 man/systemd-system.conf.xml           | 22 +++++-----
923a60
 man/systemd.resource-control.xml      | 63 ++++++++++++++++++++++-----
923a60
 src/core/cgroup.c                     | 44 +++++++++++++++++++
923a60
 src/core/cgroup.h                     |  5 +++
923a60
 src/core/dbus-cgroup.c                | 41 ++++++++++++++++-
923a60
 src/core/dbus-unit.c                  | 25 +++++++++++
923a60
 src/core/load-fragment-gperf.gperf.m4 |  2 +
923a60
 src/core/load-fragment.c              | 37 ++++++++++++++--
923a60
 src/core/load-fragment.h              |  1 +
923a60
 src/core/main.c                       |  3 ++
923a60
 src/core/manager.h                    |  1 +
923a60
 src/core/unit.c                       |  1 +
923a60
 src/shared/cgroup-util.h              |  1 +
923a60
 src/systemctl/systemctl.c             | 17 ++++++++
923a60
 14 files changed, 236 insertions(+), 27 deletions(-)
923a60
923a60
diff --git a/man/systemd-system.conf.xml b/man/systemd-system.conf.xml
923a60
index 57b3b90be1..d367ccd130 100644
923a60
--- a/man/systemd-system.conf.xml
923a60
+++ b/man/systemd-system.conf.xml
923a60
@@ -51,14 +51,14 @@
923a60
   </refnamediv>
923a60
 
923a60
   <refsynopsisdiv>
923a60
-    <para><filename>/etc/systemd/system.conf</filename></para>
923a60
-    <para><filename>/etc/systemd/system.conf.d/*.conf</filename></para>
923a60
-    <para><filename>/run/systemd/system.conf.d/*.conf</filename></para>
923a60
-    <para><filename>/usr/lib/systemd/system.conf.d/*.conf</filename></para>
923a60
-    <para><filename>/etc/systemd/user.conf</filename></para>
923a60
-    <para><filename>/etc/systemd/user.conf.d/*.conf</filename></para>
923a60
-    <para><filename>/run/systemd/user.conf.d/*.conf</filename></para>
923a60
-    <para><filename>/usr/lib/systemd/user.conf.d/*.conf</filename></para>
923a60
+    <para><filename>/etc/systemd/system.conf</filename>,
923a60
+    <filename>/etc/systemd/system.conf.d/*.conf</filename>,
923a60
+    <filename>/run/systemd/system.conf.d/*.conf</filename>,
923a60
+    <filename>/usr/lib/systemd/system.conf.d/*.conf</filename></para>
923a60
+    <para><filename>/etc/systemd/user.conf</filename>,
923a60
+    <filename>/etc/systemd/user.conf.d/*.conf</filename>,
923a60
+    <filename>/run/systemd/user.conf.d/*.conf</filename>,
923a60
+    <filename>/usr/lib/systemd/user.conf.d/*.conf</filename></para>
923a60
   </refsynopsisdiv>
923a60
 
923a60
   <refsect1>
923a60
@@ -307,12 +307,14 @@
923a60
         <term><varname>DefaultCPUAccounting=</varname></term>
923a60
         <term><varname>DefaultBlockIOAccounting=</varname></term>
923a60
         <term><varname>DefaultMemoryAccounting=</varname></term>
923a60
+        <term><varname>DefaultTasksAccounting=</varname></term>
923a60
 
923a60
         <listitem><para>Configure the default resource accounting
923a60
         settings, as configured per-unit by
923a60
         <varname>CPUAccounting=</varname>,
923a60
-        <varname>BlockIOAccounting=</varname> and
923a60
-        <varname>MemoryAccounting=</varname>. See
923a60
+        <varname>BlockIOAccounting=</varname>,
923a60
+        <varname>MemoryAccounting=</varname> and
923a60
+        <varname>TasksAccounting=</varname>. See
923a60
         <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>
923a60
         for details on the per-unit settings.</para></listitem>
923a60
       </varlistentry>
923a60
diff --git a/man/systemd.resource-control.xml b/man/systemd.resource-control.xml
923a60
index 8f4e7a3f16..6b9329bbee 100644
923a60
--- a/man/systemd.resource-control.xml
923a60
+++ b/man/systemd.resource-control.xml
923a60
@@ -103,10 +103,10 @@
923a60
         <listitem>
923a60
           <para>Turn on CPU usage accounting for this unit. Takes a
923a60
           boolean argument. Note that turning on CPU accounting for
923a60
-          one unit might also implicitly turn it on for all units
923a60
+          one unit will also implicitly turn it on for all units
923a60
           contained in the same slice and for all its parent slices
923a60
           and the units contained therein. The system default for this
923a60
-          setting maybe controlled with
923a60
+          setting may be controlled with
923a60
           <varname>DefaultCPUAccounting=</varname> in
923a60
           <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
923a60
         </listitem>
923a60
@@ -134,7 +134,7 @@
923a60
           prioritizing specific services at boot-up differently than
923a60
           during normal runtime.</para>
923a60
 
923a60
-          <para>Those options imply
923a60
+          <para>These options imply
923a60
           <literal>CPUAccounting=true</literal>.</para>
923a60
         </listitem>
923a60
       </varlistentry>
923a60
@@ -168,9 +168,10 @@
923a60
         <listitem>
923a60
           <para>Turn on process and kernel memory accounting for this
923a60
           unit. Takes a boolean argument. Note that turning on memory
923a60
-          accounting for one unit might also implicitly turn it on for
923a60
-          all its parent slices. The system default for this setting
923a60
-          maybe controlled with
923a60
+          accounting for one unit will also implicitly turn it on for
923a60
+          all units contained in the same slice and for all its parent
923a60
+          slices and the units contained therein. The system default
923a60
+          for this setting may be controlled with
923a60
           <varname>DefaultMemoryAccounting=</varname> in
923a60
           <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
923a60
         </listitem>
923a60
@@ -186,26 +187,64 @@
923a60
           memory size in bytes. If the value is suffixed with K, M, G
923a60
           or T, the specified memory size is parsed as Kilobytes,
923a60
           Megabytes, Gigabytes, or Terabytes (with the base 1024),
923a60
-          respectively. This controls the
923a60
-          <literal>memory.limit_in_bytes</literal> control group
923a60
-          attribute. For details about this control group attribute,
923a60
-          see 
923a60
+          respectively. If assigned the special value
923a60
+          <literal>infinity</literal> no memory limit is applied. This
923a60
+          controls the <literal>memory.limit_in_bytes</literal>
923a60
+          control group attribute. For details about this control
923a60
+          group attribute, see 
923a60
           url="https://www.kernel.org/doc/Documentation/cgroups/memory.txt">memory.txt</ulink>.</para>
923a60
 
923a60
           <para>Implies <literal>MemoryAccounting=true</literal>.</para>
923a60
         </listitem>
923a60
       </varlistentry>
923a60
 
923a60
+      <varlistentry>
923a60
+        <term><varname>TasksAccounting=</varname></term>
923a60
+
923a60
+        <listitem>
923a60
+          <para>Turn on task accounting for this unit. Takes a
923a60
+          boolean argument. If enabled, the system manager will keep
923a60
+          track of the number of tasks in the unit. The number of
923a60
+          tasks accounted this way includes both kernel threads and
923a60
+          userspace processes, with each thread counting
923a60
+          individually. Note that turning on tasks accounting for one
923a60
+          unit will also implicitly turn it on for all units contained
923a60
+          in the same slice and for all its parent slices and the
923a60
+          units contained therein. The system default for this setting
923a60
+          may be controlled with
923a60
+          <varname>DefaultTasksAccounting=</varname> in
923a60
+          <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
923a60
+        </listitem>
923a60
+      </varlistentry>
923a60
+
923a60
+      <varlistentry>
923a60
+        <term><varname>TasksMax=<replaceable>N</replaceable></varname></term>
923a60
+
923a60
+        <listitem>
923a60
+          <para>Specify the maximum number of tasks that may be
923a60
+          created in the unit. This ensures that the number of tasks
923a60
+          accounted for the unit (see above) stays below a specific
923a60
+          limit. If assigned the special value
923a60
+          <literal>infinity</literal> no tasks limit is applied. This
923a60
+          controls the <literal>pids.max</literal> control group
923a60
+          attribute. For details about this control group attribute,
923a60
+          see 
923a60
+          url="https://www.kernel.org/doc/Documentation/cgroups/pids.txt">pids.txt</ulink>.</para>
923a60
+
923a60
+          <para>Implies <literal>TasksAccounting=true</literal>.</para>
923a60
+        </listitem>
923a60
+      </varlistentry>
923a60
+
923a60
       <varlistentry>
923a60
         <term><varname>BlockIOAccounting=</varname></term>
923a60
 
923a60
         <listitem>
923a60
           <para>Turn on Block IO accounting for this unit. Takes a
923a60
           boolean argument. Note that turning on block IO accounting
923a60
-          for one unit might also implicitly turn it on for all units
923a60
+          for one unit will also implicitly turn it on for all units
923a60
           contained in the same slice and all for its parent slices
923a60
           and the units contained therein. The system default for this
923a60
-          setting maybe controlled with
923a60
+          setting may be controlled with
923a60
           <varname>DefaultBlockIOAccounting=</varname> in
923a60
           <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
923a60
         </listitem>
923a60
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
923a60
index b7f08fb420..d4a8f9cbe3 100644
923a60
--- a/src/core/cgroup.c
923a60
+++ b/src/core/cgroup.c
923a60
@@ -40,6 +40,7 @@ void cgroup_context_init(CGroupContext *c) {
923a60
         c->memory_limit = (uint64_t) -1;
923a60
         c->blockio_weight = (unsigned long) -1;
923a60
         c->startup_blockio_weight = (unsigned long) -1;
923a60
+        c->tasks_max = (uint64_t) -1;
923a60
 
923a60
         c->cpu_quota_per_sec_usec = USEC_INFINITY;
923a60
 }
923a60
@@ -105,6 +106,7 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
923a60
                 "%sBlockIOWeight=%lu\n"
923a60
                 "%sStartupBlockIOWeight=%lu\n"
923a60
                 "%sMemoryLimit=%" PRIu64 "\n"
923a60
+                "%sTasksMax=%" PRIu64 "\n"
923a60
                 "%sDevicePolicy=%s\n"
923a60
                 "%sDelegate=%s\n",
923a60
                 prefix, yes_no(c->cpu_accounting),
923a60
@@ -116,6 +118,7 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
923a60
                 prefix, c->blockio_weight,
923a60
                 prefix, c->startup_blockio_weight,
923a60
                 prefix, c->memory_limit,
923a60
+                prefix, c->tasks_max,
923a60
                 prefix, cgroup_device_policy_to_string(c->device_policy),
923a60
                 prefix, yes_no(c->delegate));
923a60
 
923a60
@@ -456,6 +459,21 @@ void cgroup_context_apply(CGroupContext *c, CGroupControllerMask mask, const cha
923a60
                                 log_debug("Ignoring device %s while writing cgroup attribute.", a->path);
923a60
                 }
923a60
         }
923a60
+
923a60
+        if ((mask & CGROUP_PIDS) && !is_root) {
923a60
+
923a60
+                if (c->tasks_max != (uint64_t) -1) {
923a60
+                        char buf[DECIMAL_STR_MAX(uint64_t) + 2];
923a60
+
923a60
+                        sprintf(buf, "%" PRIu64 "\n", c->tasks_max);
923a60
+                        r = cg_set_attribute("pids", path, "pids.max", buf);
923a60
+                } else
923a60
+                        r = cg_set_attribute("pids", path, "pids.max", "max");
923a60
+
923a60
+                if (r < 0)
923a60
+                        log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r,
923a60
+                                       "Failed to set pids.max on %s: %m", path);
923a60
+        }
923a60
 }
923a60
 
923a60
 CGroupControllerMask cgroup_context_get_mask(CGroupContext *c) {
923a60
@@ -484,6 +502,10 @@ CGroupControllerMask cgroup_context_get_mask(CGroupContext *c) {
923a60
             c->device_policy != CGROUP_AUTO)
923a60
                 mask |= CGROUP_DEVICE;
923a60
 
923a60
+        if (c->tasks_accounting ||
923a60
+            c->tasks_max != (uint64_t) -1)
923a60
+                mask |= CGROUP_PIDS;
923a60
+
923a60
         return mask;
923a60
 }
923a60
 
923a60
@@ -1044,6 +1066,28 @@ int manager_notify_cgroup_empty(Manager *m, const char *cgroup) {
923a60
         return 0;
923a60
 }
923a60
 
923a60
+int unit_get_tasks_current(Unit *u, uint64_t *ret) {
923a60
+        _cleanup_free_ char *v = NULL;
923a60
+        int r;
923a60
+
923a60
+        assert(u);
923a60
+       assert(ret);
923a60
+
923a60
+        if (!u->cgroup_path)
923a60
+                return -ENODATA;
923a60
+
923a60
+        if ((u->cgroup_realized_mask & CGROUP_PIDS) == 0)
923a60
+                return -ENODATA;
923a60
+
923a60
+        r = cg_get_attribute("pids", u->cgroup_path, "pids.current", &v);
923a60
+        if (r == -ENOENT)
923a60
+                return -ENODATA;
923a60
+        if (r < 0)
923a60
+                return r;
923a60
+
923a60
+        return safe_atou64(v, ret);
923a60
+}
923a60
+
923a60
 static const char* const cgroup_device_policy_table[_CGROUP_DEVICE_POLICY_MAX] = {
923a60
         [CGROUP_AUTO] = "auto",
923a60
         [CGROUP_CLOSED] = "closed",
923a60
diff --git a/src/core/cgroup.h b/src/core/cgroup.h
923a60
index 8fa851de32..8af3eaa3ae 100644
923a60
--- a/src/core/cgroup.h
923a60
+++ b/src/core/cgroup.h
923a60
@@ -72,6 +72,7 @@ struct CGroupContext {
923a60
         bool cpu_accounting;
923a60
         bool blockio_accounting;
923a60
         bool memory_accounting;
923a60
+        bool tasks_accounting;
923a60
 
923a60
         unsigned long cpu_shares;
923a60
         unsigned long startup_cpu_shares;
923a60
@@ -88,6 +89,8 @@ struct CGroupContext {
923a60
         LIST_HEAD(CGroupDeviceAllow, device_allow);
923a60
 
923a60
         bool delegate;
923a60
+
923a60
+        uint64_t tasks_max;
923a60
 };
923a60
 
923a60
 #include "unit.h"
923a60
@@ -127,5 +130,7 @@ pid_t unit_search_main_pid(Unit *u);
923a60
 
923a60
 int manager_notify_cgroup_empty(Manager *m, const char *group);
923a60
 
923a60
+int unit_get_tasks_current(Unit *u, uint64_t *ret);
923a60
+
923a60
 const char* cgroup_device_policy_to_string(CGroupDevicePolicy i) _const_;
923a60
 CGroupDevicePolicy cgroup_device_policy_from_string(const char *s) _pure_;
923a60
diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c
923a60
index 4a9df06016..a4465dc7aa 100644
923a60
--- a/src/core/dbus-cgroup.c
923a60
+++ b/src/core/dbus-cgroup.c
923a60
@@ -168,6 +168,8 @@ const sd_bus_vtable bus_cgroup_vtable[] = {
923a60
         SD_BUS_PROPERTY("MemoryLimit", "t", NULL, offsetof(CGroupContext, memory_limit), 0),
923a60
         SD_BUS_PROPERTY("DevicePolicy", "s", property_get_cgroup_device_policy, offsetof(CGroupContext, device_policy), 0),
923a60
         SD_BUS_PROPERTY("DeviceAllow", "a(ss)", property_get_device_allow, 0, 0),
923a60
+        SD_BUS_PROPERTY("TasksAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, tasks_accounting), 0),
923a60
+        SD_BUS_PROPERTY("TasksMax", "t", NULL, offsetof(CGroupContext, tasks_max), 0),
923a60
         SD_BUS_VTABLE_END
923a60
 };
923a60
 
923a60
@@ -551,7 +553,11 @@ int bus_cgroup_set_property(
923a60
                 if (mode != UNIT_CHECK) {
923a60
                         c->memory_limit = limit;
923a60
                         u->cgroup_realized_mask &= ~CGROUP_MEMORY;
923a60
-                        unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64, name, limit);
923a60
+
923a60
+                        if (limit == (uint64_t) -1)
923a60
+                                unit_write_drop_in_private(u, mode, name, "MemoryLimit=infinity");
923a60
+                        else
923a60
+                                unit_write_drop_in_private_format(u, mode, name, "MemoryLimit=%" PRIu64, limit);
923a60
                 }
923a60
 
923a60
                 return 1;
923a60
@@ -667,6 +673,39 @@ int bus_cgroup_set_property(
923a60
 
923a60
                 return 1;
923a60
 
923a60
+        } else if (streq(name, "TasksAccounting")) {
923a60
+                int b;
923a60
+
923a60
+                r = sd_bus_message_read(message, "b", &b);
923a60
+                if (r < 0)
923a60
+                        return r;
923a60
+
923a60
+                if (mode != UNIT_CHECK) {
923a60
+                        c->tasks_accounting = b;
923a60
+                        u->cgroup_realized_mask &= ~CGROUP_PIDS;
923a60
+                        unit_write_drop_in_private(u, mode, name, b ? "TasksAccounting=yes" : "TasksAccounting=no");
923a60
+                }
923a60
+
923a60
+                return 1;
923a60
+
923a60
+        } else if (streq(name, "TasksMax")) {
923a60
+                uint64_t limit;
923a60
+
923a60
+                r = sd_bus_message_read(message, "t", &limit);
923a60
+                if (r < 0)
923a60
+                        return r;
923a60
+
923a60
+                if (mode != UNIT_CHECK) {
923a60
+                        c->tasks_max = limit;
923a60
+                        u->cgroup_realized_mask &= ~CGROUP_PIDS;
923a60
+
923a60
+                        if (limit == (uint64_t) -1)
923a60
+                                unit_write_drop_in_private(u, mode, name, "TasksMax=infinity");
923a60
+                        else
923a60
+                                unit_write_drop_in_private_format(u, mode, name, "TasksMax=%" PRIu64, limit);
923a60
+                }
923a60
+
923a60
+                return 1;
923a60
         }
923a60
 
923a60
         if (u->transient && u->load_state == UNIT_STUB) {
923a60
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
923a60
index 056a17ac1f..1d0d6f67c7 100644
923a60
--- a/src/core/dbus-unit.c
923a60
+++ b/src/core/dbus-unit.c
923a60
@@ -673,11 +673,36 @@ static int property_get_current_memory(
923a60
         return sd_bus_message_append(reply, "t", sz);
923a60
 }
923a60
 
923a60
+static int property_get_current_tasks(
923a60
+                sd_bus *bus,
923a60
+                const char *path,
923a60
+                const char *interface,
923a60
+                const char *property,
923a60
+                sd_bus_message *reply,
923a60
+                void *userdata,
923a60
+                sd_bus_error *error) {
923a60
+
923a60
+        uint64_t cn = (uint64_t) -1;
923a60
+        Unit *u = userdata;
923a60
+        int r;
923a60
+
923a60
+        assert(bus);
923a60
+        assert(reply);
923a60
+        assert(u);
923a60
+
923a60
+        r = unit_get_tasks_current(u, &cn;;
923a60
+        if (r < 0 && r != -ENODATA)
923a60
+                log_unit_warning_errno(u->id, r, "Failed to get pids.current attribute: %m");
923a60
+
923a60
+        return sd_bus_message_append(reply, "t", cn);
923a60
+}
923a60
+
923a60
 const sd_bus_vtable bus_unit_cgroup_vtable[] = {
923a60
         SD_BUS_VTABLE_START(0),
923a60
         SD_BUS_PROPERTY("Slice", "s", property_get_slice, 0, 0),
923a60
         SD_BUS_PROPERTY("ControlGroup", "s", NULL, offsetof(Unit, cgroup_path), 0),
923a60
         SD_BUS_PROPERTY("MemoryCurrent", "t", property_get_current_memory, 0, 0),
923a60
+        SD_BUS_PROPERTY("TasksCurrent", "t", property_get_current_tasks, 0, 0),
923a60
         SD_BUS_VTABLE_END
923a60
 };
923a60
 
923a60
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
923a60
index f996032cf2..26e4c618ee 100644
923a60
--- a/src/core/load-fragment-gperf.gperf.m4
923a60
+++ b/src/core/load-fragment-gperf.gperf.m4
923a60
@@ -125,6 +125,8 @@ $1.StartupBlockIOWeight,         config_parse_blockio_weight,        0,
923a60
 $1.BlockIODeviceWeight,          config_parse_blockio_device_weight, 0,                             offsetof($1, cgroup_context)
923a60
 $1.BlockIOReadBandwidth,         config_parse_blockio_bandwidth,     0,                             offsetof($1, cgroup_context)
923a60
 $1.BlockIOWriteBandwidth,        config_parse_blockio_bandwidth,     0,                             offsetof($1, cgroup_context)
923a60
+$1.TasksAccounting,              config_parse_bool,                  0,                             offsetof($1, cgroup_context.tasks_accounting)
923a60
+$1.TasksMax,                     config_parse_tasks_max,             0,                             offsetof($1, cgroup_context)
923a60
 $1.Delegate,                     config_parse_bool,                  0,                             offsetof($1, cgroup_context.delegate)'
923a60
 )m4_dnl
923a60
 Unit.Description,                config_parse_unit_string_printf,    0,                             offsetof(Unit, description)
923a60
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
923a60
index 7d1ac6c251..7d2e737d05 100644
923a60
--- a/src/core/load-fragment.c
923a60
+++ b/src/core/load-fragment.c
923a60
@@ -3052,7 +3052,7 @@ int config_parse_memory_limit(
923a60
         off_t bytes;
923a60
         int r;
923a60
 
923a60
-        if (isempty(rvalue)) {
923a60
+        if (isempty(rvalue) || streq(rvalue, "infinity")) {
923a60
                 c->memory_limit = (uint64_t) -1;
923a60
                 return 0;
923a60
         }
923a60
@@ -3060,9 +3060,8 @@ int config_parse_memory_limit(
923a60
         assert_cc(sizeof(uint64_t) == sizeof(off_t));
923a60
 
923a60
         r = parse_size(rvalue, 1024, &bytes);
923a60
-        if (r < 0) {
923a60
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
923a60
-                           "Memory limit '%s' invalid. Ignoring.", rvalue);
923a60
+        if (r < 0 || bytes < 1) {
923a60
+                log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Memory limit '%s' invalid. Ignoring.", rvalue);
923a60
                 return 0;
923a60
         }
923a60
 
923a60
@@ -3070,6 +3069,36 @@ int config_parse_memory_limit(
923a60
         return 0;
923a60
 }
923a60
 
923a60
+int config_parse_tasks_max(
923a60
+                const char *unit,
923a60
+                const char *filename,
923a60
+                unsigned line,
923a60
+                const char *section,
923a60
+                unsigned section_line,
923a60
+                const char *lvalue,
923a60
+                int ltype,
923a60
+                const char *rvalue,
923a60
+                void *data,
923a60
+                void *userdata) {
923a60
+
923a60
+        CGroupContext *c = data;
923a60
+        uint64_t u;
923a60
+        int r;
923a60
+
923a60
+        if (isempty(rvalue) || streq(rvalue, "infinity")) {
923a60
+                c->tasks_max = (uint64_t) -1;
923a60
+                return 0;
923a60
+        }
923a60
+
923a60
+        r = safe_atou64(rvalue, &u);
923a60
+        if (r < 0 || u < 1) {
923a60
+                log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Maximum tasks value '%s' invalid. Ignoring.", rvalue);
923a60
+                return 0;
923a60
+        }
923a60
+
923a60
+        return 0;
923a60
+}
923a60
+
923a60
 int config_parse_device_allow(
923a60
                 const char *unit,
923a60
                 const char *filename,
923a60
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
923a60
index 2059353d38..8d334f2c86 100644
923a60
--- a/src/core/load-fragment.h
923a60
+++ b/src/core/load-fragment.h
923a60
@@ -89,6 +89,7 @@ int config_parse_pass_environ(const char *unit, const char *filename, unsigned l
923a60
 int config_parse_unit_slice(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
923a60
 int config_parse_cpu_shares(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
923a60
 int config_parse_memory_limit(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
923a60
+int config_parse_tasks_max(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
923a60
 int config_parse_device_policy(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
923a60
 int config_parse_device_allow(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
923a60
 int config_parse_blockio_weight(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
923a60
diff --git a/src/core/main.c b/src/core/main.c
923a60
index cba992cea2..aca05a5358 100644
923a60
--- a/src/core/main.c
923a60
+++ b/src/core/main.c
923a60
@@ -117,6 +117,7 @@ static bool arg_default_cpu_accounting = false;
923a60
 static bool arg_default_blockio_accounting = false;
923a60
 static bool arg_default_memory_accounting = false;
923a60
 static EmergencyAction arg_cad_burst_action = EMERGENCY_ACTION_REBOOT_FORCE;
923a60
+static bool arg_default_tasks_accounting = false;
923a60
 
923a60
 static void nop_handler(int sig) {}
923a60
 
923a60
@@ -676,6 +677,7 @@ static int parse_config_file(void) {
923a60
                 { "Manager", "DefaultBlockIOAccounting",  config_parse_bool,             0, &arg_default_blockio_accounting        },
923a60
                 { "Manager", "DefaultMemoryAccounting",   config_parse_bool,             0, &arg_default_memory_accounting         },
923a60
                 { "Manager", "CtrlAltDelBurstAction",     config_parse_emergency_action, 0, &arg_cad_burst_action                  },
923a60
+                { "Manager", "DefaultTasksAccounting",    config_parse_bool,             0, &arg_default_tasks_accounting          },
923a60
                 {}
923a60
         };
923a60
 
923a60
@@ -1685,6 +1687,7 @@ int main(int argc, char *argv[]) {
923a60
         m->default_cpu_accounting = arg_default_cpu_accounting;
923a60
         m->default_blockio_accounting = arg_default_blockio_accounting;
923a60
         m->default_memory_accounting = arg_default_memory_accounting;
923a60
+        m->default_tasks_accounting = arg_default_tasks_accounting;
923a60
         m->runtime_watchdog = arg_runtime_watchdog;
923a60
         m->shutdown_watchdog = arg_shutdown_watchdog;
923a60
 
923a60
diff --git a/src/core/manager.h b/src/core/manager.h
923a60
index 231c076b10..96dcd83dc0 100644
923a60
--- a/src/core/manager.h
923a60
+++ b/src/core/manager.h
923a60
@@ -261,6 +261,7 @@ struct Manager {
923a60
         bool default_cpu_accounting;
923a60
         bool default_memory_accounting;
923a60
         bool default_blockio_accounting;
923a60
+        bool default_tasks_accounting;
923a60
 
923a60
         usec_t default_timer_accuracy_usec;
923a60
 
923a60
diff --git a/src/core/unit.c b/src/core/unit.c
923a60
index 103f92084c..2fcb4fbf07 100644
923a60
--- a/src/core/unit.c
923a60
+++ b/src/core/unit.c
923a60
@@ -126,6 +126,7 @@ static void unit_init(Unit *u) {
923a60
                 cc->cpu_accounting = u->manager->default_cpu_accounting;
923a60
                 cc->blockio_accounting = u->manager->default_blockio_accounting;
923a60
                 cc->memory_accounting = u->manager->default_memory_accounting;
923a60
+                cc->tasks_accounting = u->manager->default_tasks_accounting;
923a60
         }
923a60
 
923a60
         ec = unit_get_exec_context(u);
923a60
diff --git a/src/shared/cgroup-util.h b/src/shared/cgroup-util.h
923a60
index 96a3d3bafa..31bd8d311f 100644
923a60
--- a/src/shared/cgroup-util.h
923a60
+++ b/src/shared/cgroup-util.h
923a60
@@ -35,6 +35,7 @@ typedef enum CGroupControllerMask {
923a60
         CGROUP_BLKIO = 4,
923a60
         CGROUP_MEMORY = 8,
923a60
         CGROUP_DEVICE = 16,
923a60
+        CGROUP_PIDS = 32,
923a60
         _CGROUP_CONTROLLER_MASK_ALL = 31
923a60
 } CGroupControllerMask;
923a60
 
923a60
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
923a60
index 0333599c87..b1862b5676 100644
923a60
--- a/src/systemctl/systemctl.c
923a60
+++ b/src/systemctl/systemctl.c
923a60
@@ -3280,6 +3280,8 @@ typedef struct UnitStatusInfo {
923a60
         /* CGroup */
923a60
         uint64_t memory_current;
923a60
         uint64_t memory_limit;
923a60
+        uint64_t tasks_current;
923a60
+        uint64_t tasks_max;
923a60
 
923a60
         LIST_HEAD(ExecStatusInfo, exec);
923a60
 } UnitStatusInfo;
923a60
@@ -3539,6 +3541,15 @@ static void print_status_info(
923a60
         if (i->status_errno > 0)
923a60
                 printf("    Error: %i (%s)\n", i->status_errno, strerror(i->status_errno));
923a60
 
923a60
+        if (i->tasks_current != (uint64_t) -1) {
923a60
+                printf("    Tasks: %" PRIu64, i->tasks_current);
923a60
+
923a60
+                if (i->tasks_max != (uint64_t) -1)
923a60
+                        printf(" (limit: %" PRIi64 ")\n", i->tasks_max);
923a60
+                else
923a60
+                        printf("\n");
923a60
+        }
923a60
+
923a60
         if (i->memory_current != (uint64_t) -1) {
923a60
                 char buf[FORMAT_BYTES_MAX];
923a60
 
923a60
@@ -3768,6 +3779,10 @@ static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo *
923a60
                         i->memory_current = u;
923a60
                 else if (streq(name, "MemoryLimit"))
923a60
                         i->memory_limit = u;
923a60
+                else if (streq(name, "TasksCurrent"))
923a60
+                        i->tasks_current = u;
923a60
+                else if (streq(name, "TasksMax"))
923a60
+                        i->tasks_max = u;
923a60
 
923a60
                 break;
923a60
         }
923a60
@@ -4248,6 +4263,8 @@ static int show_one(
923a60
         UnitStatusInfo info = {
923a60
                 .memory_current = (uint64_t) -1,
923a60
                 .memory_limit = (uint64_t) -1,
923a60
+                .tasks_current = (uint64_t) -1,
923a60
+                .tasks_max = (uint64_t) -1,
923a60
         };
923a60
         ExecStatusInfo *p;
923a60
         int r;