summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2015-09-10 12:32:16 +0200
committerLennart Poettering <lennart@poettering.net>2015-09-10 18:41:06 +0200
commit03a7b521e3ffb7f5d153d90480ba5d4bc29d1e8f (patch)
treed18052ac68aae77bcce3813e64bf1f956cc9ad7d /src/core
parent15af581253a3f25a71d4fa723bf1fdd22f842728 (diff)
core: add support for the "pids" cgroup controller
This adds support for the new "pids" cgroup controller of 4.3 kernels. It allows accounting the number of tasks in a cgroup and enforcing limits on it. This adds two new setting TasksAccounting= and TasksMax= to each unit, as well as a gloabl option DefaultTasksAccounting=. This also updated "cgtop" to optionally make use of the new kernel-provided accounting. systemctl has been updated to show the number of tasks for each service if it is available. This patch also adds correct support for undoing memory limits for units using a MemoryLimit=infinity syntax. We do the same for TasksMax= now and hence keep things in sync here.
Diffstat (limited to 'src/core')
-rw-r--r--src/core/cgroup.c49
-rw-r--r--src/core/cgroup.h4
-rw-r--r--src/core/dbus-cgroup.c41
-rw-r--r--src/core/dbus-unit.c25
-rw-r--r--src/core/load-fragment-gperf.gperf.m42
-rw-r--r--src/core/load-fragment.c37
-rw-r--r--src/core/load-fragment.h1
-rw-r--r--src/core/main.c3
-rw-r--r--src/core/manager.h1
-rw-r--r--src/core/unit.c1
10 files changed, 157 insertions, 7 deletions
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index baa7cc5488..48000d4e6e 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -22,10 +22,11 @@
#include <fcntl.h>
#include <fnmatch.h>
-#include "process-util.h"
+#include "cgroup-util.h"
#include "path-util.h"
+#include "process-util.h"
#include "special.h"
-#include "cgroup-util.h"
+
#include "cgroup.h"
#define CGROUP_CPU_QUOTA_PERIOD_USEC ((usec_t) 100 * USEC_PER_MSEC)
@@ -41,6 +42,7 @@ void cgroup_context_init(CGroupContext *c) {
c->memory_limit = (uint64_t) -1;
c->blockio_weight = (unsigned long) -1;
c->startup_blockio_weight = (unsigned long) -1;
+ c->tasks_max = (uint64_t) -1;
c->cpu_quota_per_sec_usec = USEC_INFINITY;
}
@@ -106,6 +108,7 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
"%sBlockIOWeight=%lu\n"
"%sStartupBlockIOWeight=%lu\n"
"%sMemoryLimit=%" PRIu64 "\n"
+ "%sTasksMax=%" PRIu64 "\n"
"%sDevicePolicy=%s\n"
"%sDelegate=%s\n",
prefix, yes_no(c->cpu_accounting),
@@ -117,6 +120,7 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
prefix, c->blockio_weight,
prefix, c->startup_blockio_weight,
prefix, c->memory_limit,
+ prefix, c->tasks_max,
prefix, cgroup_device_policy_to_string(c->device_policy),
prefix, yes_no(c->delegate));
@@ -466,6 +470,21 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, M
log_debug("Ignoring device %s while writing cgroup attribute.", a->path);
}
}
+
+ if ((mask & CGROUP_MASK_PIDS) && !is_root) {
+
+ if (c->tasks_max != (uint64_t) -1) {
+ char buf[DECIMAL_STR_MAX(uint64_t) + 2];
+
+ sprintf(buf, "%" PRIu64 "\n", c->tasks_max);
+ r = cg_set_attribute("pids", path, "pids.max", buf);
+ } else
+ r = cg_set_attribute("pids", path, "pids.max", "max");
+
+ if (r < 0)
+ log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to set pids.max on %s: %m", path);
+ }
}
CGroupMask cgroup_context_get_mask(CGroupContext *c) {
@@ -494,6 +513,10 @@ CGroupMask cgroup_context_get_mask(CGroupContext *c) {
c->device_policy != CGROUP_AUTO)
mask |= CGROUP_MASK_DEVICES;
+ if (c->tasks_accounting ||
+ c->tasks_max != (uint64_t) -1)
+ mask |= CGROUP_MASK_PIDS;
+
return mask;
}
@@ -1459,6 +1482,28 @@ int unit_get_memory_current(Unit *u, uint64_t *ret) {
return safe_atou64(v, ret);
}
+int unit_get_tasks_current(Unit *u, uint64_t *ret) {
+ _cleanup_free_ char *v = NULL;
+ int r;
+
+ assert(u);
+ assert(ret);
+
+ if (!u->cgroup_path)
+ return -ENODATA;
+
+ if ((u->cgroup_realized_mask & CGROUP_MASK_PIDS) == 0)
+ return -ENODATA;
+
+ r = cg_get_attribute("pids", u->cgroup_path, "pids.current", &v);
+ if (r == -ENOENT)
+ return -ENODATA;
+ if (r < 0)
+ return r;
+
+ return safe_atou64(v, ret);
+}
+
static int unit_get_cpu_usage_raw(Unit *u, nsec_t *ret) {
_cleanup_free_ char *v = NULL;
uint64_t ns;
diff --git a/src/core/cgroup.h b/src/core/cgroup.h
index 438f5bf50f..3ba09d56a4 100644
--- a/src/core/cgroup.h
+++ b/src/core/cgroup.h
@@ -72,6 +72,7 @@ struct CGroupContext {
bool cpu_accounting;
bool blockio_accounting;
bool memory_accounting;
+ bool tasks_accounting;
unsigned long cpu_shares;
unsigned long startup_cpu_shares;
@@ -88,6 +89,8 @@ struct CGroupContext {
LIST_HEAD(CGroupDeviceAllow, device_allow);
bool delegate;
+
+ uint64_t tasks_max;
};
#include "unit.h"
@@ -137,6 +140,7 @@ int unit_search_main_pid(Unit *u, pid_t *ret);
int unit_watch_all_pids(Unit *u);
int unit_get_memory_current(Unit *u, uint64_t *ret);
+int unit_get_tasks_current(Unit *u, uint64_t *ret);
int unit_get_cpu_usage(Unit *u, nsec_t *ret);
int unit_reset_cpu_usage(Unit *u);
diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c
index ba2f4e53b9..94de92c3dd 100644
--- a/src/core/dbus-cgroup.c
+++ b/src/core/dbus-cgroup.c
@@ -168,6 +168,8 @@ const sd_bus_vtable bus_cgroup_vtable[] = {
SD_BUS_PROPERTY("MemoryLimit", "t", NULL, offsetof(CGroupContext, memory_limit), 0),
SD_BUS_PROPERTY("DevicePolicy", "s", property_get_cgroup_device_policy, offsetof(CGroupContext, device_policy), 0),
SD_BUS_PROPERTY("DeviceAllow", "a(ss)", property_get_device_allow, 0, 0),
+ SD_BUS_PROPERTY("TasksAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, tasks_accounting), 0),
+ SD_BUS_PROPERTY("TasksMax", "t", NULL, offsetof(CGroupContext, tasks_max), 0),
SD_BUS_VTABLE_END
};
@@ -551,7 +553,11 @@ int bus_cgroup_set_property(
if (mode != UNIT_CHECK) {
c->memory_limit = limit;
u->cgroup_realized_mask &= ~CGROUP_MASK_MEMORY;
- unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64, name, limit);
+
+ if (limit == (uint64_t) -1)
+ unit_write_drop_in_private(u, mode, name, "MemoryLimit=infinity");
+ else
+ unit_write_drop_in_private_format(u, mode, name, "MemoryLimit=%" PRIu64, limit);
}
return 1;
@@ -667,6 +673,39 @@ int bus_cgroup_set_property(
return 1;
+ } else if (streq(name, "TasksAccounting")) {
+ int b;
+
+ r = sd_bus_message_read(message, "b", &b);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ c->tasks_accounting = b;
+ u->cgroup_realized_mask &= ~CGROUP_MASK_PIDS;
+ unit_write_drop_in_private(u, mode, name, b ? "TasksAccounting=yes" : "TasksAccounting=no");
+ }
+
+ return 1;
+
+ } else if (streq(name, "TasksMax")) {
+ uint64_t limit;
+
+ r = sd_bus_message_read(message, "t", &limit);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ c->tasks_max = limit;
+ u->cgroup_realized_mask &= ~CGROUP_MASK_PIDS;
+
+ if (limit == (uint64_t) -1)
+ unit_write_drop_in_private(u, mode, name, "TasksMax=infinity");
+ else
+ unit_write_drop_in_private_format(u, mode, name, "TasksMax=%" PRIu64, limit);
+ }
+
+ return 1;
}
if (u->transient && u->load_state == UNIT_STUB) {
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index f9275ed935..f7e9795928 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -736,6 +736,30 @@ static int property_get_current_memory(
return sd_bus_message_append(reply, "t", sz);
}
+static int property_get_current_tasks(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ uint64_t cn = (uint64_t) -1;
+ Unit *u = userdata;
+ int r;
+
+ assert(bus);
+ assert(reply);
+ assert(u);
+
+ r = unit_get_tasks_current(u, &cn);
+ if (r < 0 && r != -ENODATA)
+ log_unit_warning_errno(u, r, "Failed to get pids.current attribute: %m");
+
+ return sd_bus_message_append(reply, "t", cn);
+}
+
static int property_get_cpu_usage(
sd_bus *bus,
const char *path,
@@ -796,6 +820,7 @@ const sd_bus_vtable bus_unit_cgroup_vtable[] = {
SD_BUS_PROPERTY("ControlGroup", "s", property_get_cgroup, 0, 0),
SD_BUS_PROPERTY("MemoryCurrent", "t", property_get_current_memory, 0, 0),
SD_BUS_PROPERTY("CPUUsageNSec", "t", property_get_cpu_usage, 0, 0),
+ SD_BUS_PROPERTY("TasksCurrent", "t", property_get_current_tasks, 0, 0),
SD_BUS_VTABLE_END
};
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index edd55b9e45..e056fd863c 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -124,6 +124,8 @@ $1.StartupBlockIOWeight, config_parse_blockio_weight, 0,
$1.BlockIODeviceWeight, config_parse_blockio_device_weight, 0, offsetof($1, cgroup_context)
$1.BlockIOReadBandwidth, config_parse_blockio_bandwidth, 0, offsetof($1, cgroup_context)
$1.BlockIOWriteBandwidth, config_parse_blockio_bandwidth, 0, offsetof($1, cgroup_context)
+$1.TasksAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.tasks_accounting)
+$1.TasksMax, config_parse_tasks_max, 0, offsetof($1, cgroup_context)
$1.Delegate, config_parse_bool, 0, offsetof($1, cgroup_context.delegate)'
)m4_dnl
Unit.Description, config_parse_unit_string_printf, 0, offsetof(Unit, description)
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index 3b02cd8085..07f2770559 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -2686,7 +2686,7 @@ int config_parse_memory_limit(
off_t bytes;
int r;
- if (isempty(rvalue)) {
+ if (isempty(rvalue) || streq(rvalue, "infinity")) {
c->memory_limit = (uint64_t) -1;
return 0;
}
@@ -2694,9 +2694,8 @@ int config_parse_memory_limit(
assert_cc(sizeof(uint64_t) == sizeof(off_t));
r = parse_size(rvalue, 1024, &bytes);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Memory limit '%s' invalid. Ignoring.", rvalue);
+ if (r < 0 || bytes < 1) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Memory limit '%s' invalid. Ignoring.", rvalue);
return 0;
}
@@ -2704,6 +2703,36 @@ int config_parse_memory_limit(
return 0;
}
+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) {
+
+ CGroupContext *c = data;
+ uint64_t u;
+ int r;
+
+ if (isempty(rvalue) || streq(rvalue, "infinity")) {
+ c->tasks_max = (uint64_t) -1;
+ return 0;
+ }
+
+ r = safe_atou64(rvalue, &u);
+ if (r < 0 || u < 1) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Maximum tasks value '%s' invalid. Ignoring.", rvalue);
+ return 0;
+ }
+
+ return 0;
+}
+
int config_parse_device_allow(
const char *unit,
const char *filename,
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index 1d128716c4..638b343a6e 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -84,6 +84,7 @@ int config_parse_environ(const char *unit, const char *filename, unsigned line,
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);
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);
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);
+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);
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);
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);
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);
diff --git a/src/core/main.c b/src/core/main.c
index c9657505c3..200fe740da 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -114,6 +114,7 @@ static FILE* arg_serialization = NULL;
static bool arg_default_cpu_accounting = false;
static bool arg_default_blockio_accounting = false;
static bool arg_default_memory_accounting = false;
+static bool arg_default_tasks_accounting = false;
static void nop_handler(int sig) {}
@@ -676,6 +677,7 @@ static int parse_config_file(void) {
{ "Manager", "DefaultCPUAccounting", config_parse_bool, 0, &arg_default_cpu_accounting },
{ "Manager", "DefaultBlockIOAccounting", config_parse_bool, 0, &arg_default_blockio_accounting },
{ "Manager", "DefaultMemoryAccounting", config_parse_bool, 0, &arg_default_memory_accounting },
+ { "Manager", "DefaultTasksAccounting", config_parse_bool, 0, &arg_default_tasks_accounting },
{}
};
@@ -704,6 +706,7 @@ static void manager_set_defaults(Manager *m) {
m->default_cpu_accounting = arg_default_cpu_accounting;
m->default_blockio_accounting = arg_default_blockio_accounting;
m->default_memory_accounting = arg_default_memory_accounting;
+ m->default_tasks_accounting = arg_default_tasks_accounting;
manager_set_default_rlimits(m, arg_default_rlimit);
manager_environment_add(m, NULL, arg_default_environment);
diff --git a/src/core/manager.h b/src/core/manager.h
index 9956cb7700..78a0e50a33 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -256,6 +256,7 @@ struct Manager {
bool default_cpu_accounting;
bool default_memory_accounting;
bool default_blockio_accounting;
+ bool default_tasks_accounting;
usec_t default_timer_accuracy_usec;
diff --git a/src/core/unit.c b/src/core/unit.c
index e40ea24be8..2ebfb09a7a 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -125,6 +125,7 @@ static void unit_init(Unit *u) {
cc->cpu_accounting = u->manager->default_cpu_accounting;
cc->blockio_accounting = u->manager->default_blockio_accounting;
cc->memory_accounting = u->manager->default_memory_accounting;
+ cc->tasks_accounting = u->manager->default_tasks_accounting;
}
ec = unit_get_exec_context(u);