X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fcore%2Fcgroup.c;h=b53b35dad0ac5767205fcc7ee08d81a5a6340423;hp=c0bfecbe818f6680316abcd3fab06ff920e5bc57;hb=e916c1378b697b1f267d021a823bbfcb5ac8376c;hpb=c6999740c1a68767c5397306b8d7e14e08533111 diff --git a/src/core/cgroup.c b/src/core/cgroup.c index c0bfecbe8..b53b35dad 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -1,5 +1,3 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - /*** This file is part of systemd. @@ -22,29 +20,63 @@ #include #include -#include "process-util.h" -#include "path-util.h" -// #include "special.h" +#include "alloc-util.h" #include "cgroup-util.h" #include "cgroup.h" +#include "fd-util.h" +#include "fileio.h" +#include "fs-util.h" +#include "parse-util.h" +#include "path-util.h" +#include "process-util.h" +//#include "special.h" +#include "string-table.h" +#include "string-util.h" +#include "stdio-util.h" #define CGROUP_CPU_QUOTA_PERIOD_USEC ((usec_t) 100 * USEC_PER_MSEC) -// UNNEEDED by elogind -#if 0 +#if 0 /// UNNEEDED by elogind +static void cgroup_compat_warn(void) { + static bool cgroup_compat_warned = false; + + if (cgroup_compat_warned) + return; + + log_warning("cgroup compatibility translation between legacy and unified hierarchy settings activated. See cgroup-compat debug messages for details."); + cgroup_compat_warned = true; +} + +#define log_cgroup_compat(unit, fmt, ...) do { \ + cgroup_compat_warn(); \ + log_unit_debug(unit, "cgroup-compat: " fmt, ##__VA_ARGS__); \ + } while (false) + void cgroup_context_init(CGroupContext *c) { assert(c); /* Initialize everything to the kernel defaults, assuming the * structure is preinitialized to 0 */ - c->cpu_shares = (unsigned long) -1; - c->startup_cpu_shares = (unsigned long) -1; - c->memory_limit = (uint64_t) -1; - c->blockio_weight = (unsigned long) -1; - c->startup_blockio_weight = (unsigned long) -1; - + c->cpu_weight = CGROUP_WEIGHT_INVALID; + c->startup_cpu_weight = CGROUP_WEIGHT_INVALID; c->cpu_quota_per_sec_usec = USEC_INFINITY; + + c->cpu_shares = CGROUP_CPU_SHARES_INVALID; + c->startup_cpu_shares = CGROUP_CPU_SHARES_INVALID; + + c->memory_high = CGROUP_LIMIT_MAX; + c->memory_max = CGROUP_LIMIT_MAX; + + c->memory_limit = CGROUP_LIMIT_MAX; + + c->io_weight = CGROUP_WEIGHT_INVALID; + c->startup_io_weight = CGROUP_WEIGHT_INVALID; + + c->blockio_weight = CGROUP_BLKIO_WEIGHT_INVALID; + c->startup_blockio_weight = CGROUP_BLKIO_WEIGHT_INVALID; + + c->tasks_max = (uint64_t) -1; } void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a) { @@ -56,6 +88,24 @@ void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a) { free(a); } +void cgroup_context_free_io_device_weight(CGroupContext *c, CGroupIODeviceWeight *w) { + assert(c); + assert(w); + + LIST_REMOVE(device_weights, c->io_device_weights, w); + free(w->path); + free(w); +} + +void cgroup_context_free_io_device_limit(CGroupContext *c, CGroupIODeviceLimit *l) { + assert(c); + assert(l); + + LIST_REMOVE(device_limits, c->io_device_limits, l); + free(l->path); + free(l); +} + void cgroup_context_free_blockio_device_weight(CGroupContext *c, CGroupBlockIODeviceWeight *w) { assert(c); assert(w); @@ -77,6 +127,12 @@ void cgroup_context_free_blockio_device_bandwidth(CGroupContext *c, CGroupBlockI void cgroup_context_done(CGroupContext *c) { assert(c); + while (c->io_device_weights) + cgroup_context_free_io_device_weight(c, c->io_device_weights); + + while (c->io_device_limits) + cgroup_context_free_io_device_limit(c, c->io_device_limits); + while (c->blockio_device_weights) cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights); @@ -88,6 +144,8 @@ void cgroup_context_done(CGroupContext *c) { } void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { + CGroupIODeviceLimit *il; + CGroupIODeviceWeight *iw; CGroupBlockIODeviceBandwidth *b; CGroupBlockIODeviceWeight *w; CGroupDeviceAllow *a; @@ -100,25 +158,45 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { fprintf(f, "%sCPUAccounting=%s\n" + "%sIOAccounting=%s\n" "%sBlockIOAccounting=%s\n" "%sMemoryAccounting=%s\n" - "%sCPUShares=%lu\n" - "%sStartupCPUShares=%lu\n" + "%sTasksAccounting=%s\n" + "%sCPUWeight=%" PRIu64 "\n" + "%sStartupCPUWeight=%" PRIu64 "\n" + "%sCPUShares=%" PRIu64 "\n" + "%sStartupCPUShares=%" PRIu64 "\n" "%sCPUQuotaPerSecSec=%s\n" - "%sBlockIOWeight=%lu\n" - "%sStartupBlockIOWeight=%lu\n" + "%sIOWeight=%" PRIu64 "\n" + "%sStartupIOWeight=%" PRIu64 "\n" + "%sBlockIOWeight=%" PRIu64 "\n" + "%sStartupBlockIOWeight=%" PRIu64 "\n" + "%sMemoryLow=%" PRIu64 "\n" + "%sMemoryHigh=%" PRIu64 "\n" + "%sMemoryMax=%" PRIu64 "\n" "%sMemoryLimit=%" PRIu64 "\n" + "%sTasksMax=%" PRIu64 "\n" "%sDevicePolicy=%s\n" "%sDelegate=%s\n", prefix, yes_no(c->cpu_accounting), + prefix, yes_no(c->io_accounting), prefix, yes_no(c->blockio_accounting), prefix, yes_no(c->memory_accounting), + prefix, yes_no(c->tasks_accounting), + prefix, c->cpu_weight, + prefix, c->startup_cpu_weight, prefix, c->cpu_shares, prefix, c->startup_cpu_shares, prefix, format_timespan(u, sizeof(u), c->cpu_quota_per_sec_usec, 1), + prefix, c->io_weight, + prefix, c->startup_io_weight, prefix, c->blockio_weight, prefix, c->startup_blockio_weight, + prefix, c->memory_low, + prefix, c->memory_high, + prefix, c->memory_max, prefix, c->memory_limit, + prefix, c->tasks_max, prefix, cgroup_device_policy_to_string(c->device_policy), prefix, yes_no(c->delegate)); @@ -129,9 +207,30 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { a->path, a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : ""); + LIST_FOREACH(device_weights, iw, c->io_device_weights) + fprintf(f, + "%sIODeviceWeight=%s %" PRIu64, + prefix, + iw->path, + iw->weight); + + LIST_FOREACH(device_limits, il, c->io_device_limits) { + char buf[FORMAT_BYTES_MAX]; + CGroupIOLimitType type; + + for (type = 0; type < _CGROUP_IO_LIMIT_TYPE_MAX; type++) + if (il->limits[type] != cgroup_io_limit_defaults[type]) + fprintf(f, + "%s%s=%s %s\n", + prefix, + cgroup_io_limit_type_to_string(type), + il->path, + format_bytes(buf, sizeof(buf), il->limits[type])); + } + LIST_FOREACH(device_weights, w, c->blockio_device_weights) fprintf(f, - "%sBlockIODeviceWeight=%s %lu", + "%sBlockIODeviceWeight=%s %" PRIu64, prefix, w->path, w->weight); @@ -139,16 +238,22 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) { char buf[FORMAT_BYTES_MAX]; - fprintf(f, - "%s%s=%s %s\n", - prefix, - b->read ? "BlockIOReadBandwidth" : "BlockIOWriteBandwidth", - b->path, - format_bytes(buf, sizeof(buf), b->bandwidth)); + if (b->rbps != CGROUP_LIMIT_MAX) + fprintf(f, + "%sBlockIOReadBandwidth=%s %s\n", + prefix, + b->path, + format_bytes(buf, sizeof(buf), b->rbps)); + if (b->wbps != CGROUP_LIMIT_MAX) + fprintf(f, + "%sBlockIOWriteBandwidth=%s %s\n", + prefix, + b->path, + format_bytes(buf, sizeof(buf), b->wbps)); } } -static int lookup_blkio_device(const char *p, dev_t *dev) { +static int lookup_block_device(const char *p, dev_t *dev) { struct stat st; int r; @@ -203,7 +308,7 @@ static int whitelist_device(const char *path, const char *node, const char *acc) r = cg_set_attribute("devices", path, "devices.allow", buf); if (r < 0) - log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL) ? LOG_DEBUG : LOG_WARNING, r, + log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to set devices.allow on %s: %m", path); return r; @@ -274,7 +379,7 @@ static int whitelist_major(const char *path, const char *name, char type, const r = cg_set_attribute("devices", path, "devices.allow", buf); if (r < 0) - log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL) ? LOG_DEBUG : LOG_WARNING, r, + log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to set devices.allow on %s: %m", path); } @@ -285,10 +390,261 @@ fail: return -errno; } -void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, ManagerState state) { +static bool cgroup_context_has_cpu_weight(CGroupContext *c) { + return c->cpu_weight != CGROUP_WEIGHT_INVALID || + c->startup_cpu_weight != CGROUP_WEIGHT_INVALID; +} + +static bool cgroup_context_has_cpu_shares(CGroupContext *c) { + return c->cpu_shares != CGROUP_CPU_SHARES_INVALID || + c->startup_cpu_shares != CGROUP_CPU_SHARES_INVALID; +} + +static uint64_t cgroup_context_cpu_weight(CGroupContext *c, ManagerState state) { + if (IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) && + c->startup_cpu_weight != CGROUP_WEIGHT_INVALID) + return c->startup_cpu_weight; + else if (c->cpu_weight != CGROUP_WEIGHT_INVALID) + return c->cpu_weight; + else + return CGROUP_WEIGHT_DEFAULT; +} + +static uint64_t cgroup_context_cpu_shares(CGroupContext *c, ManagerState state) { + if (IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) && + c->startup_cpu_shares != CGROUP_CPU_SHARES_INVALID) + return c->startup_cpu_shares; + else if (c->cpu_shares != CGROUP_CPU_SHARES_INVALID) + return c->cpu_shares; + else + return CGROUP_CPU_SHARES_DEFAULT; +} + +static void cgroup_apply_unified_cpu_config(Unit *u, uint64_t weight, uint64_t quota) { + char buf[MAX(DECIMAL_STR_MAX(uint64_t) + 1, (DECIMAL_STR_MAX(usec_t) + 1) * 2)]; + int r; + + xsprintf(buf, "%" PRIu64 "\n", weight); + r = cg_set_attribute("cpu", u->cgroup_path, "cpu.weight", buf); + if (r < 0) + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set cpu.weight: %m"); + + if (quota != USEC_INFINITY) + xsprintf(buf, USEC_FMT " " USEC_FMT "\n", + quota * CGROUP_CPU_QUOTA_PERIOD_USEC / USEC_PER_SEC, CGROUP_CPU_QUOTA_PERIOD_USEC); + else + xsprintf(buf, "max " USEC_FMT "\n", CGROUP_CPU_QUOTA_PERIOD_USEC); + + r = cg_set_attribute("cpu", u->cgroup_path, "cpu.max", buf); + + if (r < 0) + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set cpu.max: %m"); +} + +static void cgroup_apply_legacy_cpu_config(Unit *u, uint64_t shares, uint64_t quota) { + char buf[MAX(DECIMAL_STR_MAX(uint64_t), DECIMAL_STR_MAX(usec_t)) + 1]; + int r; + + xsprintf(buf, "%" PRIu64 "\n", shares); + r = cg_set_attribute("cpu", u->cgroup_path, "cpu.shares", buf); + if (r < 0) + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set cpu.shares: %m"); + + xsprintf(buf, USEC_FMT "\n", CGROUP_CPU_QUOTA_PERIOD_USEC); + r = cg_set_attribute("cpu", u->cgroup_path, "cpu.cfs_period_us", buf); + if (r < 0) + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set cpu.cfs_period_us: %m"); + + if (quota != USEC_INFINITY) { + xsprintf(buf, USEC_FMT "\n", quota * CGROUP_CPU_QUOTA_PERIOD_USEC / USEC_PER_SEC); + r = cg_set_attribute("cpu", u->cgroup_path, "cpu.cfs_quota_us", buf); + } else + r = cg_set_attribute("cpu", u->cgroup_path, "cpu.cfs_quota_us", "-1"); + if (r < 0) + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set cpu.cfs_quota_us: %m"); +} + +static uint64_t cgroup_cpu_shares_to_weight(uint64_t shares) { + return CLAMP(shares * CGROUP_WEIGHT_DEFAULT / CGROUP_CPU_SHARES_DEFAULT, + CGROUP_WEIGHT_MIN, CGROUP_WEIGHT_MAX); +} + +static uint64_t cgroup_cpu_weight_to_shares(uint64_t weight) { + return CLAMP(weight * CGROUP_CPU_SHARES_DEFAULT / CGROUP_WEIGHT_DEFAULT, + CGROUP_CPU_SHARES_MIN, CGROUP_CPU_SHARES_MAX); +} + +static bool cgroup_context_has_io_config(CGroupContext *c) { + return c->io_accounting || + c->io_weight != CGROUP_WEIGHT_INVALID || + c->startup_io_weight != CGROUP_WEIGHT_INVALID || + c->io_device_weights || + c->io_device_limits; +} + +static bool cgroup_context_has_blockio_config(CGroupContext *c) { + return c->blockio_accounting || + c->blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID || + c->startup_blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID || + c->blockio_device_weights || + c->blockio_device_bandwidths; +} + +static uint64_t cgroup_context_io_weight(CGroupContext *c, ManagerState state) { + if (IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) && + c->startup_io_weight != CGROUP_WEIGHT_INVALID) + return c->startup_io_weight; + else if (c->io_weight != CGROUP_WEIGHT_INVALID) + return c->io_weight; + else + return CGROUP_WEIGHT_DEFAULT; +} + +static uint64_t cgroup_context_blkio_weight(CGroupContext *c, ManagerState state) { + if (IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) && + c->startup_blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID) + return c->startup_blockio_weight; + else if (c->blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID) + return c->blockio_weight; + else + return CGROUP_BLKIO_WEIGHT_DEFAULT; +} + +static uint64_t cgroup_weight_blkio_to_io(uint64_t blkio_weight) { + return CLAMP(blkio_weight * CGROUP_WEIGHT_DEFAULT / CGROUP_BLKIO_WEIGHT_DEFAULT, + CGROUP_WEIGHT_MIN, CGROUP_WEIGHT_MAX); +} + +static uint64_t cgroup_weight_io_to_blkio(uint64_t io_weight) { + return CLAMP(io_weight * CGROUP_BLKIO_WEIGHT_DEFAULT / CGROUP_WEIGHT_DEFAULT, + CGROUP_BLKIO_WEIGHT_MIN, CGROUP_BLKIO_WEIGHT_MAX); +} + +static void cgroup_apply_io_device_weight(Unit *u, const char *dev_path, uint64_t io_weight) { + char buf[DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(uint64_t)+1]; + dev_t dev; + int r; + + r = lookup_block_device(dev_path, &dev); + if (r < 0) + return; + + xsprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), io_weight); + r = cg_set_attribute("io", u->cgroup_path, "io.weight", buf); + if (r < 0) + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set io.weight: %m"); +} + +static void cgroup_apply_blkio_device_weight(Unit *u, const char *dev_path, uint64_t blkio_weight) { + char buf[DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(uint64_t)+1]; + dev_t dev; + int r; + + r = lookup_block_device(dev_path, &dev); + if (r < 0) + return; + + xsprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), blkio_weight); + r = cg_set_attribute("blkio", u->cgroup_path, "blkio.weight_device", buf); + if (r < 0) + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set blkio.weight_device: %m"); +} + +static unsigned cgroup_apply_io_device_limit(Unit *u, const char *dev_path, uint64_t *limits) { + char limit_bufs[_CGROUP_IO_LIMIT_TYPE_MAX][DECIMAL_STR_MAX(uint64_t)]; + char buf[DECIMAL_STR_MAX(dev_t)*2+2+(6+DECIMAL_STR_MAX(uint64_t)+1)*4]; + CGroupIOLimitType type; + dev_t dev; + unsigned n = 0; + int r; + + r = lookup_block_device(dev_path, &dev); + if (r < 0) + return 0; + + for (type = 0; type < _CGROUP_IO_LIMIT_TYPE_MAX; type++) { + if (limits[type] != cgroup_io_limit_defaults[type]) { + xsprintf(limit_bufs[type], "%" PRIu64, limits[type]); + n++; + } else { + xsprintf(limit_bufs[type], "%s", limits[type] == CGROUP_LIMIT_MAX ? "max" : "0"); + } + } + + xsprintf(buf, "%u:%u rbps=%s wbps=%s riops=%s wiops=%s\n", major(dev), minor(dev), + limit_bufs[CGROUP_IO_RBPS_MAX], limit_bufs[CGROUP_IO_WBPS_MAX], + limit_bufs[CGROUP_IO_RIOPS_MAX], limit_bufs[CGROUP_IO_WIOPS_MAX]); + r = cg_set_attribute("io", u->cgroup_path, "io.max", buf); + if (r < 0) + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set io.max: %m"); + return n; +} + +static unsigned cgroup_apply_blkio_device_limit(Unit *u, const char *dev_path, uint64_t rbps, uint64_t wbps) { + char buf[DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(uint64_t)+1]; + dev_t dev; + unsigned n = 0; + int r; + + r = lookup_block_device(dev_path, &dev); + if (r < 0) + return 0; + + if (rbps != CGROUP_LIMIT_MAX) + n++; + sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), rbps); + r = cg_set_attribute("blkio", u->cgroup_path, "blkio.throttle.read_bps_device", buf); + if (r < 0) + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set blkio.throttle.read_bps_device: %m"); + + if (wbps != CGROUP_LIMIT_MAX) + n++; + sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), wbps); + r = cg_set_attribute("blkio", u->cgroup_path, "blkio.throttle.write_bps_device", buf); + if (r < 0) + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set blkio.throttle.write_bps_device: %m"); + + return n; +} + +static bool cgroup_context_has_unified_memory_config(CGroupContext *c) { + return c->memory_low > 0 || c->memory_high != CGROUP_LIMIT_MAX || c->memory_max != CGROUP_LIMIT_MAX; +} + +static void cgroup_apply_unified_memory_limit(Unit *u, const char *file, uint64_t v) { + char buf[DECIMAL_STR_MAX(uint64_t) + 1] = "max"; + int r; + + if (v != CGROUP_LIMIT_MAX) + xsprintf(buf, "%" PRIu64 "\n", v); + + r = cg_set_attribute("memory", u->cgroup_path, file, buf); + if (r < 0) + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set %s: %m", file); +} + +static void cgroup_context_apply(Unit *u, CGroupMask mask, ManagerState state) { + const char *path; + CGroupContext *c; bool is_root; int r; + assert(u); + + c = unit_get_cgroup_context(u); + path = u->cgroup_path; + assert(c); assert(path); @@ -307,106 +663,229 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, M * and missing cgroups, i.e. EROFS and ENOENT. */ if ((mask & CGROUP_MASK_CPU) && !is_root) { - char buf[MAX(DECIMAL_STR_MAX(unsigned long), DECIMAL_STR_MAX(usec_t)) + 1]; + bool has_weight = cgroup_context_has_cpu_weight(c); + bool has_shares = cgroup_context_has_cpu_shares(c); - sprintf(buf, "%lu\n", - IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) && c->startup_cpu_shares != (unsigned long) -1 ? c->startup_cpu_shares : - c->cpu_shares != (unsigned long) -1 ? c->cpu_shares : 1024); - r = cg_set_attribute("cpu", path, "cpu.shares", buf); - if (r < 0) - log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, - "Failed to set cpu.shares on %s: %m", path); + if (cg_unified() > 0) { + uint64_t weight; - sprintf(buf, USEC_FMT "\n", CGROUP_CPU_QUOTA_PERIOD_USEC); - r = cg_set_attribute("cpu", path, "cpu.cfs_period_us", buf); - if (r < 0) - log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, - "Failed to set cpu.cfs_period_us on %s: %m", path); + if (has_weight) + weight = cgroup_context_cpu_weight(c, state); + else if (has_shares) { + uint64_t shares = cgroup_context_cpu_shares(c, state); - if (c->cpu_quota_per_sec_usec != USEC_INFINITY) { - sprintf(buf, USEC_FMT "\n", c->cpu_quota_per_sec_usec * CGROUP_CPU_QUOTA_PERIOD_USEC / USEC_PER_SEC); - r = cg_set_attribute("cpu", path, "cpu.cfs_quota_us", buf); - } else - r = cg_set_attribute("cpu", path, "cpu.cfs_quota_us", "-1"); - if (r < 0) - log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, - "Failed to set cpu.cfs_quota_us on %s: %m", path); + weight = cgroup_cpu_shares_to_weight(shares); + + log_cgroup_compat(u, "Applying [Startup]CpuShares %" PRIu64 " as [Startup]CpuWeight %" PRIu64 " on %s", + shares, weight, path); + } else + weight = CGROUP_WEIGHT_DEFAULT; + + cgroup_apply_unified_cpu_config(u, weight, c->cpu_quota_per_sec_usec); + } else { + uint64_t shares; + + if (has_shares) + shares = cgroup_context_cpu_shares(c, state); + else if (has_weight) { + uint64_t weight = cgroup_context_cpu_weight(c, state); + + shares = cgroup_cpu_weight_to_shares(weight); + + log_cgroup_compat(u, "Applying [Startup]CpuWeight %" PRIu64 " as [Startup]CpuShares %" PRIu64 " on %s", + weight, shares, path); + } else + shares = CGROUP_CPU_SHARES_DEFAULT; + + cgroup_apply_legacy_cpu_config(u, shares, c->cpu_quota_per_sec_usec); + } + } + + if (mask & CGROUP_MASK_IO) { + bool has_io = cgroup_context_has_io_config(c); + bool has_blockio = cgroup_context_has_blockio_config(c); + + if (!is_root) { + char buf[8+DECIMAL_STR_MAX(uint64_t)+1]; + uint64_t weight; + + if (has_io) + weight = cgroup_context_io_weight(c, state); + else if (has_blockio) { + uint64_t blkio_weight = cgroup_context_blkio_weight(c, state); + + weight = cgroup_weight_blkio_to_io(blkio_weight); + + log_cgroup_compat(u, "Applying [Startup]BlockIOWeight %" PRIu64 " as [Startup]IOWeight %" PRIu64, + blkio_weight, weight); + } else + weight = CGROUP_WEIGHT_DEFAULT; + + xsprintf(buf, "default %" PRIu64 "\n", weight); + r = cg_set_attribute("io", path, "io.weight", buf); + if (r < 0) + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set io.weight: %m"); + + if (has_io) { + CGroupIODeviceWeight *w; + + /* FIXME: no way to reset this list */ + LIST_FOREACH(device_weights, w, c->io_device_weights) + cgroup_apply_io_device_weight(u, w->path, w->weight); + } else if (has_blockio) { + CGroupBlockIODeviceWeight *w; + + /* FIXME: no way to reset this list */ + LIST_FOREACH(device_weights, w, c->blockio_device_weights) { + weight = cgroup_weight_blkio_to_io(w->weight); + + log_cgroup_compat(u, "Applying BlockIODeviceWeight %" PRIu64 " as IODeviceWeight %" PRIu64 " for %s", + w->weight, weight, w->path); + + cgroup_apply_io_device_weight(u, w->path, weight); + } + } + } + + /* Apply limits and free ones without config. */ + if (has_io) { + CGroupIODeviceLimit *l, *next; + + LIST_FOREACH_SAFE(device_limits, l, next, c->io_device_limits) { + if (!cgroup_apply_io_device_limit(u, l->path, l->limits)) + cgroup_context_free_io_device_limit(c, l); + } + } else if (has_blockio) { + CGroupBlockIODeviceBandwidth *b, *next; + + LIST_FOREACH_SAFE(device_bandwidths, b, next, c->blockio_device_bandwidths) { + uint64_t limits[_CGROUP_IO_LIMIT_TYPE_MAX]; + CGroupIOLimitType type; + + for (type = 0; type < _CGROUP_IO_LIMIT_TYPE_MAX; type++) + limits[type] = cgroup_io_limit_defaults[type]; + + limits[CGROUP_IO_RBPS_MAX] = b->rbps; + limits[CGROUP_IO_WBPS_MAX] = b->wbps; + + log_cgroup_compat(u, "Applying BlockIO{Read|Write}Bandwidth %" PRIu64 " %" PRIu64 " as IO{Read|Write}BandwidthMax for %s", + b->rbps, b->wbps, b->path); + + if (!cgroup_apply_io_device_limit(u, b->path, limits)) + cgroup_context_free_blockio_device_bandwidth(c, b); + } + } } if (mask & CGROUP_MASK_BLKIO) { - char buf[MAX3(DECIMAL_STR_MAX(unsigned long)+1, - DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(unsigned long)*1, - DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(uint64_t)+1)]; - CGroupBlockIODeviceWeight *w; - CGroupBlockIODeviceBandwidth *b; + bool has_io = cgroup_context_has_io_config(c); + bool has_blockio = cgroup_context_has_blockio_config(c); if (!is_root) { - sprintf(buf, "%lu\n", IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) && c->startup_blockio_weight != (unsigned long) -1 ? c->startup_blockio_weight : - c->blockio_weight != (unsigned long) -1 ? c->blockio_weight : 1000); + char buf[DECIMAL_STR_MAX(uint64_t)+1]; + uint64_t weight; + + if (has_blockio) + weight = cgroup_context_blkio_weight(c, state); + else if (has_io) { + uint64_t io_weight = cgroup_context_io_weight(c, state); + + weight = cgroup_weight_io_to_blkio(cgroup_context_io_weight(c, state)); + + log_cgroup_compat(u, "Applying [Startup]IOWeight %" PRIu64 " as [Startup]BlockIOWeight %" PRIu64, + io_weight, weight); + } else + weight = CGROUP_BLKIO_WEIGHT_DEFAULT; + + xsprintf(buf, "%" PRIu64 "\n", weight); r = cg_set_attribute("blkio", path, "blkio.weight", buf); if (r < 0) - log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, - "Failed to set blkio.weight on %s: %m", path); - - /* FIXME: no way to reset this list */ - LIST_FOREACH(device_weights, w, c->blockio_device_weights) { - dev_t dev; - - r = lookup_blkio_device(w->path, &dev); - if (r < 0) - continue; - - sprintf(buf, "%u:%u %lu", major(dev), minor(dev), w->weight); - r = cg_set_attribute("blkio", path, "blkio.weight_device", buf); - if (r < 0) - log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, - "Failed to set blkio.weight_device on %s: %m", path); + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set blkio.weight: %m"); + + if (has_blockio) { + CGroupBlockIODeviceWeight *w; + + /* FIXME: no way to reset this list */ + LIST_FOREACH(device_weights, w, c->blockio_device_weights) + cgroup_apply_blkio_device_weight(u, w->path, w->weight); + } else if (has_io) { + CGroupIODeviceWeight *w; + + /* FIXME: no way to reset this list */ + LIST_FOREACH(device_weights, w, c->io_device_weights) { + weight = cgroup_weight_io_to_blkio(w->weight); + + log_cgroup_compat(u, "Applying IODeviceWeight %" PRIu64 " as BlockIODeviceWeight %" PRIu64 " for %s", + w->weight, weight, w->path); + + cgroup_apply_blkio_device_weight(u, w->path, weight); + } } } - /* FIXME: no way to reset this list */ - LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) { - const char *a; - dev_t dev; + /* Apply limits and free ones without config. */ + if (has_blockio) { + CGroupBlockIODeviceBandwidth *b, *next; - r = lookup_blkio_device(b->path, &dev); - if (r < 0) - continue; + LIST_FOREACH_SAFE(device_bandwidths, b, next, c->blockio_device_bandwidths) { + if (!cgroup_apply_blkio_device_limit(u, b->path, b->rbps, b->wbps)) + cgroup_context_free_blockio_device_bandwidth(c, b); + } + } else if (has_io) { + CGroupIODeviceLimit *l, *next; - a = b->read ? "blkio.throttle.read_bps_device" : "blkio.throttle.write_bps_device"; + LIST_FOREACH_SAFE(device_limits, l, next, c->io_device_limits) { + log_cgroup_compat(u, "Applying IO{Read|Write}Bandwidth %" PRIu64 " %" PRIu64 " as BlockIO{Read|Write}BandwidthMax for %s", + l->limits[CGROUP_IO_RBPS_MAX], l->limits[CGROUP_IO_WBPS_MAX], l->path); - sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), b->bandwidth); - r = cg_set_attribute("blkio", path, a, buf); - if (r < 0) - log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, - "Failed to set %s on %s: %m", a, path); + if (!cgroup_apply_blkio_device_limit(u, l->path, l->limits[CGROUP_IO_RBPS_MAX], l->limits[CGROUP_IO_WBPS_MAX])) + cgroup_context_free_io_device_limit(c, l); + } } } if ((mask & CGROUP_MASK_MEMORY) && !is_root) { - if (c->memory_limit != (uint64_t) -1) { - char buf[DECIMAL_STR_MAX(uint64_t) + 1]; + if (cg_unified() > 0) { + uint64_t max = c->memory_max; - sprintf(buf, "%" PRIu64 "\n", c->memory_limit); + if (cgroup_context_has_unified_memory_config(c)) + max = c->memory_max; + else { + max = c->memory_limit; - if (cg_unified() <= 0) - r = cg_set_attribute("memory", path, "memory.limit_in_bytes", buf); - else - r = cg_set_attribute("memory", path, "memory.max", buf); + if (max != CGROUP_LIMIT_MAX) + log_cgroup_compat(u, "Applying MemoryLimit %" PRIu64 " as MemoryMax", max); + } + cgroup_apply_unified_memory_limit(u, "memory.low", c->memory_low); + cgroup_apply_unified_memory_limit(u, "memory.high", c->memory_high); + cgroup_apply_unified_memory_limit(u, "memory.max", max); } else { - if (cg_unified() <= 0) - r = cg_set_attribute("memory", path, "memory.limit_in_bytes", "-1"); + char buf[DECIMAL_STR_MAX(uint64_t) + 1]; + uint64_t val = c->memory_limit; + + if (val == CGROUP_LIMIT_MAX) { + val = c->memory_max; + + if (val != CGROUP_LIMIT_MAX) + log_cgroup_compat(u, "Applying MemoryMax %" PRIi64 " as MemoryLimit", c->memory_max); + } + + if (val == CGROUP_LIMIT_MAX) + strncpy(buf, "-1\n", sizeof(buf)); else - r = cg_set_attribute("memory", path, "memory.max", "max"); - } + xsprintf(buf, "%" PRIu64 "\n", val); - if (r < 0) - log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, - "Failed to set memory.limit_in_bytes/memory.max on %s: %m", path); + r = cg_set_attribute("memory", path, "memory.limit_in_bytes", buf); + if (r < 0) + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set memory.limit_in_bytes: %m"); + } } - if ((mask & CGROUP_MASK_DEVICE) && !is_root) { + if ((mask & CGROUP_MASK_DEVICES) && !is_root) { CGroupDeviceAllow *a; /* Changing the devices list of a populated cgroup @@ -418,8 +897,8 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, M else r = cg_set_attribute("devices", path, "devices.allow", "a"); if (r < 0) - log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL) ? LOG_DEBUG : LOG_WARNING, r, - "Failed to reset devices.list on %s: %m", path); + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to reset devices.list: %m"); if (c->device_policy == CGROUP_CLOSED || (c->device_policy == CGROUP_AUTO && c->device_allow)) { @@ -430,7 +909,11 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, M "/dev/random\0" "rwm\0" "/dev/urandom\0" "rwm\0" "/dev/tty\0" "rwm\0" - "/dev/pts/ptmx\0" "rw\0"; /* /dev/pts/ptmx may not be duplicated, but accessed */ + "/dev/pts/ptmx\0" "rw\0" /* /dev/pts/ptmx may not be duplicated, but accessed */ + /* Allow /run/elogind/inaccessible/{chr,blk} devices for mapping InaccessiblePaths */ + /* Allow /run/systemd/inaccessible/{chr,blk} devices for mapping InaccessiblePaths */ + "/run/systemd/inaccessible/chr\0" "rwm\0" + "/run/systemd/inaccessible/blk\0" "rwm\0"; const char *x, *y; @@ -465,9 +948,24 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, M else if (startswith(a->path, "char-")) whitelist_major(path, a->path + 5, 'c', acc); else - log_debug("Ignoring device %s while writing cgroup attribute.", a->path); + log_unit_debug(u, "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_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set pids.max: %m"); + } } CGroupMask cgroup_context_get_mask(CGroupContext *c) { @@ -476,25 +974,26 @@ CGroupMask cgroup_context_get_mask(CGroupContext *c) { /* Figure out which controllers we need */ if (c->cpu_accounting || - c->cpu_shares != (unsigned long) -1 || - c->startup_cpu_shares != (unsigned long) -1 || + cgroup_context_has_cpu_weight(c) || + cgroup_context_has_cpu_shares(c) || c->cpu_quota_per_sec_usec != USEC_INFINITY) mask |= CGROUP_MASK_CPUACCT | CGROUP_MASK_CPU; - if (c->blockio_accounting || - c->blockio_weight != (unsigned long) -1 || - c->startup_blockio_weight != (unsigned long) -1 || - c->blockio_device_weights || - c->blockio_device_bandwidths) - mask |= CGROUP_MASK_BLKIO; + if (cgroup_context_has_io_config(c) || cgroup_context_has_blockio_config(c)) + mask |= CGROUP_MASK_IO | CGROUP_MASK_BLKIO; if (c->memory_accounting || - c->memory_limit != (uint64_t) -1) + c->memory_limit != CGROUP_LIMIT_MAX || + cgroup_context_has_unified_memory_config(c)) mask |= CGROUP_MASK_MEMORY; if (c->device_allow || c->device_policy != CGROUP_AUTO) - mask |= CGROUP_MASK_DEVICE; + mask |= CGROUP_MASK_DEVICES; + + if (c->tasks_accounting || + c->tasks_max != (uint64_t) -1) + mask |= CGROUP_MASK_PIDS; return mask; } @@ -736,7 +1235,7 @@ int unit_set_cgroup_path(Unit *u, const char *path) { } int unit_watch_cgroup(Unit *u) { - _cleanup_free_ char *populated = NULL; + _cleanup_free_ char *events = NULL; int r; assert(u); @@ -750,7 +1249,7 @@ int unit_watch_cgroup(Unit *u) { /* Only applies to the unified hierarchy */ r = cg_unified(); if (r < 0) - return log_unit_error_errno(u, r, "Failed detect wether the unified hierarchy is used: %m"); + return log_unit_error_errno(u, r, "Failed detect whether the unified hierarchy is used: %m"); if (r == 0) return 0; @@ -762,11 +1261,11 @@ int unit_watch_cgroup(Unit *u) { if (r < 0) return log_oom(); - r = cg_get_path(ELOGIND_CGROUP_CONTROLLER, u->cgroup_path, "cgroup.populated", &populated); + r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "cgroup.events", &events); if (r < 0) return log_oom(); - u->cgroup_inotify_wd = inotify_add_watch(u->manager->cgroup_inotify_fd, populated, IN_MODIFY); + u->cgroup_inotify_wd = inotify_add_watch(u->manager->cgroup_inotify_fd, events, IN_MODIFY); if (u->cgroup_inotify_wd < 0) { if (errno == ENOENT) /* If the directory is already @@ -828,6 +1327,7 @@ static int unit_create_cgroup( /* Keep track that this is now realized */ u->cgroup_realized = true; u->cgroup_realized_mask = target_mask; + u->cgroup_enabled_mask = enable_mask; if (u->type != UNIT_SLICE && !c->delegate) { @@ -857,10 +1357,10 @@ int unit_attach_pids_to_cgroup(Unit *u) { return 0; } -static bool unit_has_mask_realized(Unit *u, CGroupMask target_mask) { +static bool unit_has_mask_realized(Unit *u, CGroupMask target_mask, CGroupMask enable_mask) { assert(u); - return u->cgroup_realized && u->cgroup_realized_mask == target_mask; + return u->cgroup_realized && u->cgroup_realized_mask == target_mask && u->cgroup_enabled_mask == enable_mask; } /* Check if necessary controllers and attributes for a unit are in place. @@ -881,7 +1381,9 @@ static int unit_realize_cgroup_now(Unit *u, ManagerState state) { } target_mask = unit_get_target_mask(u); - if (unit_has_mask_realized(u, target_mask)) + enable_mask = unit_get_enable_mask(u); + + if (unit_has_mask_realized(u, target_mask, enable_mask)) return 0; /* First, realize parents */ @@ -892,13 +1394,12 @@ static int unit_realize_cgroup_now(Unit *u, ManagerState state) { } /* And then do the real work */ - enable_mask = unit_get_enable_mask(u); r = unit_create_cgroup(u, target_mask, enable_mask); if (r < 0) return r; /* Finally, apply the necessary attributes. */ - cgroup_context_apply(unit_get_cgroup_context(u), target_mask, u->cgroup_path, state); + cgroup_context_apply(u, target_mask, state); return 0; } @@ -961,7 +1462,7 @@ static void unit_queue_siblings(Unit *u) { /* If the unit doesn't need any new controllers * and has current ones realized, it doesn't need * any changes. */ - if (unit_has_mask_realized(m, unit_get_target_mask(m))) + if (unit_has_mask_realized(m, unit_get_target_mask(m), unit_get_enable_mask(m))) continue; unit_add_to_cgroup_queue(m); @@ -1029,7 +1530,7 @@ void unit_prune_cgroup(Unit *u) { r = cg_trim_everywhere(u->manager->cgroup_supported, u->cgroup_path, !is_root_slice); if (r < 0) { - log_debug_errno(r, "Failed to destroy cgroup %s, ignoring: %m", u->cgroup_path); + log_unit_debug_errno(u, r, "Failed to destroy cgroup %s, ignoring: %m", u->cgroup_path); return; } @@ -1040,6 +1541,7 @@ void unit_prune_cgroup(Unit *u) { u->cgroup_realized = false; u->cgroup_realized_mask = 0; + u->cgroup_enabled_mask = 0; } int unit_search_main_pid(Unit *u, pid_t *ret) { @@ -1053,7 +1555,7 @@ int unit_search_main_pid(Unit *u, pid_t *ret) { if (!u->cgroup_path) return -ENXIO; - r = cg_enumerate_processes(ELOGIND_CGROUP_CONTROLLER, u->cgroup_path, &f); + r = cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, &f); if (r < 0) return r; @@ -1065,7 +1567,7 @@ int unit_search_main_pid(Unit *u, pid_t *ret) { continue; /* Ignore processes that aren't our kids */ - if (get_parent_of_pid(npid, &ppid) >= 0 && ppid != mypid) + if (get_process_ppid(npid, &ppid) >= 0 && ppid != mypid) continue; if (pid != 0) @@ -1090,7 +1592,7 @@ static int unit_watch_pids_in_path(Unit *u, const char *path) { assert(u); assert(path); - r = cg_enumerate_processes(ELOGIND_CGROUP_CONTROLLER, path, &f); + r = cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, path, &f); if (r < 0) ret = r; else { @@ -1106,7 +1608,7 @@ static int unit_watch_pids_in_path(Unit *u, const char *path) { ret = r; } - r = cg_enumerate_subgroups(ELOGIND_CGROUP_CONTROLLER, path, &d); + r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, path, &d); if (r < 0) { if (ret >= 0) ret = r; @@ -1159,7 +1661,7 @@ int unit_notify_cgroup_empty(Unit *u) { if (!u->cgroup_path) return 0; - r = cg_is_empty_recursive(ELOGIND_CGROUP_CONTROLLER, u->cgroup_path); + r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path); if (r <= 0) return r; @@ -1226,11 +1728,11 @@ int manager_setup_cgroup(Manager *m) { /* 1. Determine hierarchy */ m->cgroup_root = mfree(m->cgroup_root); - r = cg_pid_get_path(ELOGIND_CGROUP_CONTROLLER, 0, &m->cgroup_root); + r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &m->cgroup_root); if (r < 0) return log_error_errno(r, "Cannot determine cgroup we are running in: %m"); -/// elogind does not support systemd scopes and slices -#if 0 + +#if 0 /// elogind does not support systemd scopes and slices /* Chop off the init scope, if we are already located in it */ e = endswith(m->cgroup_root, "/" SPECIAL_INIT_SCOPE); @@ -1238,7 +1740,7 @@ int manager_setup_cgroup(Manager *m) { * it. This is to support live upgrades from older systemd * versions where PID 1 was moved there. Also see * cg_get_root_path(). */ - if (!e) { + if (!e && MANAGER_IS_SYSTEM(m)) { e = endswith(m->cgroup_root, "/" SPECIAL_SYSTEM_SLICE); if (!e) e = endswith(m->cgroup_root, "/system"); /* even more legacy */ @@ -1253,10 +1755,10 @@ int manager_setup_cgroup(Manager *m) { while ((e = endswith(m->cgroup_root, "/"))) *e = 0; log_debug_elogind("Cgroup Controller \"%s\" -> root \"%s\"", - ELOGIND_CGROUP_CONTROLLER, m->cgroup_root); + SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root); /* 2. Show data */ - r = cg_get_path(ELOGIND_CGROUP_CONTROLLER, m->cgroup_root, NULL, &path); + r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, NULL, &path); if (r < 0) return log_error_errno(r, "Cannot find cgroup mount point: %m"); @@ -1266,7 +1768,7 @@ int manager_setup_cgroup(Manager *m) { if (unified > 0) log_debug("Unified cgroup hierarchy is located at %s.", path); else - log_debug("Using cgroup controller " ELOGIND_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path); + log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path); if (!m->test_run) { const char *scope_path; @@ -1274,10 +1776,10 @@ int manager_setup_cgroup(Manager *m) { /* 3. Install agent */ if (unified) { - /* In the unified hierarchy we can can get + /* In the unified hierarchy we can get * cgroup empty notifications via inotify. */ -/// elogind does not support the unified hierarchy, yet. -#if 0 + +#if 0 /// elogind does not support the unified hierarchy, yet. m->cgroup_inotify_event_source = sd_event_source_unref(m->cgroup_inotify_event_source); safe_close(m->cgroup_inotify_fd); @@ -1289,7 +1791,9 @@ int manager_setup_cgroup(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to watch control group inotify object: %m"); - r = sd_event_source_set_priority(m->cgroup_inotify_event_source, SD_EVENT_PRIORITY_IDLE - 5); + /* Process cgroup empty notifications early, but after service notifications and SIGCHLD. Also + * see handling of cgroup agent notifications, for the classic cgroup hierarchy support. */ + r = sd_event_source_set_priority(m->cgroup_inotify_event_source, SD_EVENT_PRIORITY_NORMAL-5); if (r < 0) return log_error_errno(r, "Failed to set priority of inotify event source: %m"); @@ -1298,14 +1802,15 @@ int manager_setup_cgroup(Manager *m) { #else return log_error_errno(EOPNOTSUPP, "Unified cgroup hierarchy not supported: %m"); #endif // 0 - } else if (m->running_as == MANAGER_SYSTEM) { + } else if (MANAGER_IS_SYSTEM(m)) { + /* On the legacy hierarchy we only get * notifications via cgroup agents. (Which * isn't really reliable, since it does not * generate events when control groups with * children run empty. */ - r = cg_install_release_agent(ELOGIND_CGROUP_CONTROLLER, ELOGIND_CGROUP_AGENT_PATH); + r = cg_install_release_agent(SYSTEMD_CGROUP_CONTROLLER, SYSTEMD_CGROUP_AGENT_PATH); if (r < 0) log_warning_errno(r, "Failed to install release agent, ignoring: %m"); else if (r > 0) @@ -1314,19 +1819,21 @@ int manager_setup_cgroup(Manager *m) { log_debug("Release agent already installed."); } -/// elogind is not meant to run in systemd init scope -#if 0 +#if 0 /// elogind is not meant to run in systemd init scope /* 4. Make sure we are in the special "init.scope" unit in the root slice. */ scope_path = strjoina(m->cgroup_root, "/" SPECIAL_INIT_SCOPE); - r = cg_create_and_attach(ELOGIND_CGROUP_CONTROLLER, scope_path, 0); + r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, scope_path, 0); #else - if (streq(m->cgroup_root, "/elogind")) + if (streq(SYSTEMD_CGROUP_CONTROLLER, "name=elogind")) + // we are our own cgroup controller + scope_path = strjoina(""); + else if (streq(m->cgroup_root, "/elogind")) // root already is our cgroup scope_path = strjoina(m->cgroup_root); else // we have to create our own group scope_path = strjoina(m->cgroup_root, "/elogind"); - r = cg_create_and_attach(ELOGIND_CGROUP_CONTROLLER, scope_path, 0); + r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, scope_path, 0); #endif // 0 if (r < 0) return log_error_errno(r, "Failed to create %s control group: %m", scope_path); @@ -1334,11 +1841,9 @@ int manager_setup_cgroup(Manager *m) { /* also, move all other userspace processes remaining * in the root cgroup into that scope. */ - if (!streq(m->cgroup_root, scope_path)) { - r = cg_migrate(ELOGIND_CGROUP_CONTROLLER, m->cgroup_root, ELOGIND_CGROUP_CONTROLLER, scope_path, false); - if (r < 0) - log_warning_errno(r, "Couldn't move remaining userspace processes, ignoring: %m"); - } + r = cg_migrate(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, SYSTEMD_CGROUP_CONTROLLER, scope_path, 0); + if (r < 0) + log_warning_errno(r, "Couldn't move remaining userspace processes, ignoring: %m"); /* 5. And pin it, so that it cannot be unmounted */ safe_close(m->pin_cgroupfs_fd); @@ -1357,7 +1862,7 @@ int manager_setup_cgroup(Manager *m) { return log_error_errno(r, "Failed to determine supported controllers: %m"); for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) - log_debug("Controller '%s' supported: %s", cgroup_controller_to_string(c), yes_no(m->cgroup_supported & c)); + log_debug("Controller '%s' supported: %s", cgroup_controller_to_string(c), yes_no(m->cgroup_supported & CGROUP_CONTROLLER_TO_MASK(c))); return 0; } @@ -1368,10 +1873,9 @@ void manager_shutdown_cgroup(Manager *m, bool delete) { /* We can't really delete the group, since we are in it. But * let's trim it. */ if (delete && m->cgroup_root) - (void) cg_trim(ELOGIND_CGROUP_CONTROLLER, m->cgroup_root, false); + (void) cg_trim(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, false); -/// elogind does not support the unified hierarchy, yet. -#if 0 +#if 0 /// elogind does not support the unified hierarchy, yet. m->cgroup_inotify_wd_unit = hashmap_free(m->cgroup_inotify_wd_unit); m->cgroup_inotify_event_source = sd_event_source_unref(m->cgroup_inotify_event_source); @@ -1383,8 +1887,7 @@ void manager_shutdown_cgroup(Manager *m, bool delete) { m->cgroup_root = mfree(m->cgroup_root); } -/// UNNEEDED by elogind -#if 0 +#if 0 /// UNNEEDED by elogind Unit* manager_get_unit_by_cgroup(Manager *m, const char *cgroup) { char *p; Unit *u; @@ -1421,7 +1924,7 @@ Unit *manager_get_unit_by_pid_cgroup(Manager *m, pid_t pid) { if (pid <= 0) return NULL; - r = cg_pid_get_path(ELOGIND_CGROUP_CONTROLLER, pid, &cgroup); + r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup); if (r < 0) return NULL; @@ -1449,20 +1952,45 @@ Unit *manager_get_unit_by_pid(Manager *m, pid_t pid) { return manager_get_unit_by_pid_cgroup(m, pid); } +#endif // 0 +#if 0 /// elogind must substitute this with its own variant int manager_notify_cgroup_empty(Manager *m, const char *cgroup) { Unit *u; assert(m); assert(cgroup); + log_debug("Got cgroup empty notification for: %s", cgroup); + u = manager_get_unit_by_cgroup(m, cgroup); if (!u) return 0; return unit_notify_cgroup_empty(u); } +#else +int manager_notify_cgroup_empty(Manager *m, const char *cgroup) { + Session *s; + + assert(m); + assert(cgroup); + + log_debug("Got cgroup empty notification for: %s", cgroup); + + s = hashmap_get(m->sessions, cgroup); + + if (s) { + session_finalize(s); + session_free(s); + } else + log_warning("Session not found: %s", cgroup); + + return 0; +} +#endif // 0 +#if 0 /// UNNEEDED by elogind int unit_get_memory_current(Unit *u, uint64_t *ret) { _cleanup_free_ char *v = NULL; int r; @@ -1488,9 +2016,8 @@ int unit_get_memory_current(Unit *u, uint64_t *ret) { return safe_atou64(v, ret); } -static int unit_get_cpu_usage_raw(Unit *u, nsec_t *ret) { +int unit_get_tasks_current(Unit *u, uint64_t *ret) { _cleanup_free_ char *v = NULL; - uint64_t ns; int r; assert(u); @@ -1499,18 +2026,60 @@ static int unit_get_cpu_usage_raw(Unit *u, nsec_t *ret) { if (!u->cgroup_path) return -ENODATA; - if ((u->cgroup_realized_mask & CGROUP_MASK_CPUACCT) == 0) + if ((u->cgroup_realized_mask & CGROUP_MASK_PIDS) == 0) return -ENODATA; - r = cg_get_attribute("cpuacct", u->cgroup_path, "cpuacct.usage", &v); + r = cg_get_attribute("pids", u->cgroup_path, "pids.current", &v); if (r == -ENOENT) return -ENODATA; if (r < 0) return r; - r = safe_atou64(v, &ns); - 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; + int r; + + assert(u); + assert(ret); + + if (!u->cgroup_path) + return -ENODATA; + + if (cg_unified() > 0) { + const char *keys[] = { "usage_usec", NULL }; + _cleanup_free_ char *val = NULL; + uint64_t us; + + if ((u->cgroup_realized_mask & CGROUP_MASK_CPU) == 0) + return -ENODATA; + + r = cg_get_keyed_attribute("cpu", u->cgroup_path, "cpu.stat", keys, &val); + if (r < 0) + return r; + + r = safe_atou64(val, &us); + if (r < 0) + return r; + + ns = us * NSEC_PER_USEC; + } else { + if ((u->cgroup_realized_mask & CGROUP_MASK_CPUACCT) == 0) + return -ENODATA; + + r = cg_get_attribute("cpuacct", u->cgroup_path, "cpuacct.usage", &v); + if (r == -ENOENT) + return -ENODATA; + if (r < 0) + return r; + + r = safe_atou64(v, &ns); + if (r < 0) + return r; + } *ret = ns; return 0; @@ -1524,8 +2093,8 @@ int unit_get_cpu_usage(Unit *u, nsec_t *ret) { if (r < 0) return r; - if (ns > u->cpuacct_usage_base) - ns -= u->cpuacct_usage_base; + if (ns > u->cpu_usage_base) + ns -= u->cpu_usage_base; else ns = 0; @@ -1541,11 +2110,11 @@ int unit_reset_cpu_usage(Unit *u) { r = unit_get_cpu_usage_raw(u, &ns); if (r < 0) { - u->cpuacct_usage_base = 0; + u->cpu_usage_base = 0; return r; } - u->cpuacct_usage_base = ns; + u->cpu_usage_base = ns; return 0; } @@ -1561,6 +2130,36 @@ bool unit_cgroup_delegate(Unit *u) { return c->delegate; } +void unit_invalidate_cgroup(Unit *u, CGroupMask m) { + assert(u); + + if (!UNIT_HAS_CGROUP_CONTEXT(u)) + return; + + if (m == 0) + return; + + /* always invalidate compat pairs together */ + if (m & (CGROUP_MASK_IO | CGROUP_MASK_BLKIO)) + m |= CGROUP_MASK_IO | CGROUP_MASK_BLKIO; + + if ((u->cgroup_realized_mask & m) == 0) + return; + + u->cgroup_realized_mask &= ~m; + unit_add_to_cgroup_queue(u); +} + +void manager_invalidate_startup_units(Manager *m) { + Iterator i; + Unit *u; + + assert(m); + + SET_FOREACH(u, m->startup_units, i) + unit_invalidate_cgroup(u, CGROUP_MASK_CPU|CGROUP_MASK_IO|CGROUP_MASK_BLKIO); +} + static const char* const cgroup_device_policy_table[_CGROUP_DEVICE_POLICY_MAX] = { [CGROUP_AUTO] = "auto", [CGROUP_CLOSED] = "closed",