cgroup.c revision b58b8e11c5f769e3c80d5169fdcc4bd04b882b7d
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2013 Lennart Poettering
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <fcntl.h>
#include "path-util.h"
#include "special.h"
#include "cgroup-util.h"
#include "cgroup.h"
void cgroup_context_init(CGroupContext *c) {
assert(c);
/* Initialize everything to the kernel defaults, assuming the
* structure is preinitialized to 0 */
c->cpu_shares = 1024;
c->blockio_weight = 1000;
}
assert(c);
assert(a);
free(a);
}
assert(c);
assert(w);
free(w);
}
void cgroup_context_free_blockio_device_bandwidth(CGroupContext *c, CGroupBlockIODeviceBandwidth *b) {
assert(c);
assert(b);
free(b);
}
void cgroup_context_done(CGroupContext *c) {
assert(c);
while (c->blockio_device_weights)
while (c->blockio_device_bandwidths)
while (c->device_allow)
}
assert(c);
assert(f);
fprintf(f,
"%sCPUAccounting=%s\n"
"%sBlockIOAccounting=%s\n"
"%sMemoryAccounting=%s\n"
"%sCPUShares=%lu\n"
"%sBlockIOWeight%lu\n"
"%sDevicePolicy=%s\n",
prefix, c->cpu_shares,
prefix, c->blockio_weight,
prefix, c->memory_limit,
prefix, c->memory_soft_limit,
fprintf(f,
"%sDeviceAllow=%s %s%s%s\n",
a->path,
a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "");
fprintf(f,
"%sBlockIODeviceWeight=%s %lu",
w->path,
w->weight);
char buf[FORMAT_BYTES_MAX];
fprintf(f,
"%s%s=%s %s\n",
b->path,
}
}
int r;
assert(p);
if (r < 0) {
log_warning("Couldn't stat device %s: %m", p);
return -errno;
}
/* If this is not a device node then find the block
* device this file is stored on */
/* If this is a partition, try to get the originating
* block device */
} else {
log_warning("%s is not a block device and file system block device cannot be determined or is not local.", p);
return -ENODEV;
}
return 0;
}
int r;
return -errno;
}
return -ENODEV;
}
"%c %u:%u %s",
acc);
if (r < 0)
return r;
}
int r;
assert(c);
if (mask == 0)
return;
if (mask & CGROUP_CPU) {
if (r < 0)
}
if (mask & CGROUP_BLKIO) {
if (r < 0)
/* FIXME: no way to reset this list */
if (r < 0)
continue;
if (r < 0)
}
/* FIXME: no way to reset this list */
const char *a;
if (r < 0)
continue;
if (r < 0)
}
}
if (mask & CGROUP_MEMORY) {
if (r < 0)
if (r < 0)
}
if (mask & CGROUP_DEVICE) {
else
if (r < 0)
if (c->device_policy == CGROUP_CLOSED ||
static const char auto_devices[] =
const char *x, *y;
NULSTR_FOREACH_PAIR(x, y, auto_devices)
whitelist_device(path, x, y);
}
char acc[4];
unsigned k = 0;
if (a->r)
acc[k++] = 'r';
if (a->w)
acc[k++] = 'w';
if (a->m)
acc[k++] = 'm';
if (k == 0)
continue;
acc[k++] = 0;
}
}
}
CGroupControllerMask mask = 0;
/* Figure out which controllers we need */
if (c->blockio_accounting ||
c->blockio_weight != 1000 ||
c->blockio_device_weights ||
mask |= CGROUP_BLKIO;
if (c->memory_accounting ||
mask |= CGROUP_MEMORY;
mask |= CGROUP_DEVICE;
return mask;
}
CGroupContext *c;
c = unit_get_cgroup_context(u);
if (!c)
return 0;
return cgroup_context_get_mask(c);
}
CGroupControllerMask mask = 0;
Unit *m;
Iterator i;
assert(u);
if (UNIT_DEREF(m->slice) != u)
continue;
}
return mask;
}
assert(u);
if (!UNIT_ISSET(u->slice))
return 0;
/* Sibling propagation is only relevant for weight-based
* controllers, so let's mask out everything else */
}
int r;
bool is_in_hash = false;
assert(u);
path = unit_default_cgroup_path(u);
if (!path)
return -ENOMEM;
if (r == 0)
is_in_hash = true;
if (r < 0) {
return r;
}
/* First, create our own group */
if (r < 0)
/* Then, possibly move things over */
if (r < 0)
}
if (!is_in_hash) {
/* And remember the new data */
free(u->cgroup_path);
u->cgroup_path = path;
}
u->cgroup_realized = true;
u->cgroup_mask = mask;
return 0;
}
static int unit_realize_cgroup_now(Unit *u) {
assert(u);
if (u->in_cgroup_queue) {
u->in_cgroup_queue = false;
}
if (u->cgroup_realized &&
u->cgroup_mask == mask)
return 0;
/* First, realize parents */
if (UNIT_ISSET(u->slice))
/* And then do the real work */
return unit_create_cgroups(u, mask);
}
static void unit_add_to_cgroup_queue(Unit *u) {
if (u->in_cgroup_queue)
return;
u->in_cgroup_queue = true;
}
unsigned manager_dispatch_cgroup_queue(Manager *m) {
Unit *i;
unsigned n = 0;
while ((i = m->cgroup_queue)) {
assert(i->in_cgroup_queue);
if (unit_realize_cgroup_now(i) >= 0)
n++;
}
return n;
}
static void unit_queue_siblings(Unit *u) {
/* This adds the siblings of the specified unit and the
* siblings of all parent units to the cgroup queue. (But
* neither the specified unit itself nor the parents.) */
Iterator i;
Unit *m;
if (m == u)
continue;
continue;
}
u = slice;
}
}
int unit_realize_cgroup(Unit *u) {
CGroupContext *c;
int r;
assert(u);
c = unit_get_cgroup_context(u);
if (!c)
return 0;
/* So, here's the deal: when realizing the cgroups for this
* unit, we need to first create all parents, but there's more
* actually: for the weight-based controllers we also need to
* make sure that all our siblings (i.e. units that are in the
* same slice as we are) have cgroup too. Otherwise things
* would become very uneven as each of their processes would
* get as much resources as all our group together. This call
* will synchronously create the parent cgroups, but will
* defer work on the siblings to the next event loop
* iteration. */
/* Add all sibling slices to the cgroup queue. */
/* And realize this one now */
r = unit_realize_cgroup_now(u);
/* And apply the values */
if (r >= 0)
return r;
}
void unit_destroy_cgroup(Unit *u) {
int r;
assert(u);
if (!u->cgroup_path)
return;
if (r < 0)
free(u->cgroup_path);
u->cgroup_path = NULL;
u->cgroup_realized = false;
u->cgroup_mask = 0;
}
assert(u);
if (!u->cgroup_path)
return 0;
return 0;
while (cg_read_pid(f, &npid) > 0) {
continue;
/* Ignore processes that aren't our kids */
continue;
if (pid != 0) {
/* Dang, there's more than one daemonized PID
in this group, so we don't know what process
is the main process. */
pid = 0;
break;
}
}
return pid;
}
int manager_setup_cgroup(Manager *m) {
int r;
char *e, *a;
assert(m);
/* 0. Be nice to Ingo Molnar #628004 */
if (path_is_mount_point("/sys/fs/cgroup/systemd", false) <= 0) {
log_warning("No control group support available, not creating root group.");
return 0;
}
/* 1. Determine hierarchy */
free(m->cgroup_root);
m->cgroup_root = NULL;
if (r < 0) {
return r;
}
/* Already in /system.slice? If so, let's cut this off again */
if (m->running_as == SYSTEMD_SYSTEM) {
if (e)
*e = 0;
}
/* And make sure to store away the root value without trailing
* slash, even for the root dir, so that we can easily prepend
* it everywhere. */
m->cgroup_root[0] = 0;
/* 2. Show data */
if (r < 0) {
return r;
}
log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path);
/* 3. Install agent */
if (m->running_as == SYSTEMD_SYSTEM) {
if (r < 0)
else if (r > 0)
log_debug("Installed release agent.");
else
log_debug("Release agent already installed.");
}
/* 4. Realize the system slice and put us in there */
if (m->running_as == SYSTEMD_SYSTEM) {
r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, a, 0);
} else
if (r < 0) {
return r;
}
/* 5. And pin it, so that it cannot be unmounted */
if (m->pin_cgroupfs_fd >= 0)
if (r < 0) {
log_error("Failed to open pin file: %m");
return -errno;
}
/* 6. Figure out which controllers are supported */
m->cgroup_supported = cg_mask_supported();
return 0;
}
assert(m);
/* We can't really delete the group, since we are in it. But
* let's trim it. */
if (delete && m->cgroup_root)
if (m->pin_cgroupfs_fd >= 0) {
m->pin_cgroupfs_fd = -1;
}
free(m->cgroup_root);
m->cgroup_root = NULL;
}
char *p;
Unit *u;
assert(m);
if (u)
return u;
for (;;) {
char *e;
e = strrchr(p, '/');
if (e == p || !e)
return NULL;
*e = 0;
u = hashmap_get(m->cgroup_unit, p);
if (u)
return u;
}
}
int r;
assert(m);
if (pid <= 1)
return NULL;
if (r < 0)
return NULL;
return manager_get_unit_by_cgroup(m, cgroup);
}
Unit *u;
int r;
assert(m);
u = manager_get_unit_by_cgroup(m, cgroup);
if (u) {
if (r > 0) {
if (UNIT_VTABLE(u)->notify_cgroup_empty)
UNIT_VTABLE(u)->notify_cgroup_empty(u);
}
}
return 0;
}
static const char* const cgroup_device_policy_table[_CGROUP_DEVICE_POLICY_MAX] = {
[CGROUP_AUTO] = "auto",
[CGROUP_CLOSED] = "closed",
[CGROUP_STRICT] = "strict",
};