Skip to content

Commit

Permalink
core: unified cgroup hierarchy support
Browse files Browse the repository at this point in the history
This patch set adds full support the new unified cgroup hierarchy logic
of modern kernels.

A new kernel command line option "systemd.unified_cgroup_hierarchy=1" is
added. If specified the unified hierarchy is mounted to /sys/fs/cgroup
instead of a tmpfs. No further hierarchies are mounted. The kernel
command line option defaults to off. We can turn it on by default as
soon as the kernel's APIs regarding this are stabilized (but even then
downstream distros might want to turn this off, as this will break any
tools that access cgroupfs directly).

It is possibly to choose for each boot individually whether the unified
or the legacy hierarchy is used. nspawn will by default provide the
legacy hierarchy to containers if the host is using it, and the unified
otherwise. However it is possible to run containers with the unified
hierarchy on a legacy host and vice versa, by setting the
$UNIFIED_CGROUP_HIERARCHY environment variable for nspawn to 1 or 0,
respectively.

The unified hierarchy provides reliable cgroup empty notifications for
the first time, via inotify. To make use of this we maintain one
manager-wide inotify fd, and each cgroup to it.

This patch also removes cg_delete() which is unused now.

On kernel 4.2 only the "memory" controller is compatible with the
unified hierarchy, hence that's the only controller systemd exposes when
booted in unified heirarchy mode.

This introduces a new enum for enumerating supported controllers, plus a
related enum for the mask bits mapping to it. The core is changed to
make use of this everywhere.

This moves PID 1 into a new "init.scope" implicit scope unit in the root
slice. This is necessary since on the unified hierarchy cgroups may
either contain subgroups or processes but not both. PID 1 hence has to
move out of the root cgroup (strictly speaking the root cgroup is the
only one where processes and subgroups are still allowed, but in order
to support containers nicey, we move PID 1 into the new scope in all
cases.) This new unit is also used on legacy hierarchy setups. It's
actually pretty useful on all systems, as it can then be used to filter
journal messages coming from PID 1, and so on.

The root slice ("-.slice") is now implicitly created and started (and
does not require a unit file on disk anymore), since
that's where "init.scope" is located and the slice needs to be started
before the scope can.

To check whether we are in unified or legacy hierarchy mode we use
statfs() on /sys/fs/cgroup. If the .f_type field reports tmpfs we are in
legacy mode, if it reports cgroupfs we are in unified mode.

This patch set carefuly makes sure that cgls and cgtop continue to work
as desired.

When invoking nspawn as a service it will implicitly create two
subcgroups in the cgroup it is using, one to move the nspawn process
into, the other to move the actual container processes into. This is
done because of the requirement that cgroups may either contain
processes or other subgroups.
  • Loading branch information
poettering committed Sep 1, 2015
1 parent 92dcf85 commit efdb023
Show file tree
Hide file tree
Showing 23 changed files with 1,495 additions and 593 deletions.
752 changes: 517 additions & 235 deletions src/basic/cgroup-util.c

Large diffs are not rendered by default.

54 changes: 38 additions & 16 deletions src/basic/cgroup-util.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,28 @@
#include "set.h"
#include "def.h"

/* An enum of well known cgroup controllers */
typedef enum CGroupController {
CGROUP_CONTROLLER_CPU,
CGROUP_CONTROLLER_CPUACCT,
CGROUP_CONTROLLER_BLKIO,
CGROUP_CONTROLLER_MEMORY,
CGROUP_CONTROLLER_DEVICE,
_CGROUP_CONTROLLER_MAX,
_CGROUP_CONTROLLER_INVALID = -1,
} CGroupController;

#define CGROUP_CONTROLLER_TO_MASK(c) (1 << (c))

/* A bit mask of well known cgroup controllers */
typedef enum CGroupControllerMask {
CGROUP_CPU = 1,
CGROUP_CPUACCT = 2,
CGROUP_BLKIO = 4,
CGROUP_MEMORY = 8,
CGROUP_DEVICE = 16,
_CGROUP_CONTROLLER_MASK_ALL = 31
} CGroupControllerMask;
typedef enum CGroupMask {
CGROUP_MASK_CPU = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_CPU),
CGROUP_MASK_CPUACCT = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_CPUACCT),
CGROUP_MASK_BLKIO = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BLKIO),
CGROUP_MASK_MEMORY = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_MEMORY),
CGROUP_MASK_DEVICE = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_DEVICE),
_CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1
} CGroupMask;

/*
* General rules:
Expand Down Expand Up @@ -77,7 +90,6 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path);
int cg_trim(const char *controller, const char *path, bool delete_root);

int cg_rmdir(const char *controller, const char *path);
int cg_delete(const char *controller, const char *path);

int cg_create(const char *controller, const char *path);
int cg_attach(const char *controller, const char *path, pid_t pid);
Expand Down Expand Up @@ -126,14 +138,24 @@ bool cg_controller_is_valid(const char *p);

int cg_slice_to_path(const char *unit, char **ret);

typedef const char* (*cg_migrate_callback_t)(CGroupControllerMask mask, void *userdata);
typedef const char* (*cg_migrate_callback_t)(CGroupMask mask, void *userdata);

int cg_create_everywhere(CGroupControllerMask supported, CGroupControllerMask mask, const char *path);
int cg_attach_everywhere(CGroupControllerMask supported, const char *path, pid_t pid, cg_migrate_callback_t callback, void *userdata);
int cg_attach_many_everywhere(CGroupControllerMask supported, const char *path, Set* pids, cg_migrate_callback_t callback, void *userdata);
int cg_migrate_everywhere(CGroupControllerMask supported, const char *from, const char *to, cg_migrate_callback_t callback, void *userdata);
int cg_trim_everywhere(CGroupControllerMask supported, const char *path, bool delete_root);
int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path);
int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_migrate_callback_t callback, void *userdata);
int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids, cg_migrate_callback_t callback, void *userdata);
int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t callback, void *userdata);
int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root);
int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p);

CGroupControllerMask cg_mask_supported(void);
int cg_mask_supported(CGroupMask *ret);

int cg_kernel_controllers(Set *controllers);

int cg_unified(void);
void cg_unified_flush(void);

bool cg_is_unified_wanted(void);
bool cg_is_legacy_wanted(void);

const char* cgroup_controller_to_string(CGroupController c) _const_;
CGroupController cgroup_controller_from_string(const char *s) _pure_;
2 changes: 1 addition & 1 deletion src/basic/def.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
* the watchdog pings will keep the loop busy. */
#define DEFAULT_EXIT_USEC (30*USEC_PER_SEC)

#define SYSTEMD_CGROUP_CONTROLLER "systemd"
#define SYSTEMD_CGROUP_CONTROLLER "name=systemd"

#define SIGNALS_CRASH_HANDLER SIGSEGV,SIGILL,SIGFPE,SIGBUS,SIGQUIT,SIGABRT
#define SIGNALS_IGNORE SIGPIPE
Expand Down
8 changes: 8 additions & 0 deletions src/basic/missing.h
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,14 @@ struct btrfs_ioctl_quota_ctl_args {
#define BTRFS_SUPER_MAGIC 0x9123683E
#endif

#ifndef CGROUP_SUPER_MAGIC
#define CGROUP_SUPER_MAGIC 0x27e0eb
#endif

#ifndef TMPFS_MAGIC
#define TMPFS_MAGIC 0x01021994
#endif

#ifndef MS_MOVE
#define MS_MOVE 8192
#endif
Expand Down
3 changes: 3 additions & 0 deletions src/basic/special.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,6 @@
#define SPECIAL_USER_SLICE "user.slice"
#define SPECIAL_MACHINE_SLICE "machine.slice"
#define SPECIAL_ROOT_SLICE "-.slice"

/* The scope unit systemd itself lives in. */
#define SPECIAL_INIT_SCOPE "init.scope"
5 changes: 4 additions & 1 deletion src/cgls/cgls.c
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,10 @@ int main(int argc, char *argv[]) {
} else
path = root;

printf("Controller %s; control group %s:\n", controller, path);
if (cg_unified() > 0)
printf("Control group %s:\n", path);
else
printf("Controller %s; control group %s:\n", controller, path);
fflush(stdout);

q = show_cgroup(controller, path, NULL, 0, arg_kernel_threads, output_flags);
Expand Down
9 changes: 6 additions & 3 deletions src/cgtop/cgtop.c
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ static int process(
if (g->n_tasks > 0)
g->n_tasks_valid = true;

} else if (streq(controller, "cpuacct")) {
} else if (streq(controller, "cpuacct") && cg_unified() <= 0) {
_cleanup_free_ char *p = NULL, *v = NULL;
uint64_t new_usage;
nsec_t timestamp;
Expand Down Expand Up @@ -217,7 +217,10 @@ static int process(
} else if (streq(controller, "memory")) {
_cleanup_free_ char *p = NULL, *v = NULL;

r = cg_get_path(controller, path, "memory.usage_in_bytes", &p);
if (cg_unified() <= 0)
r = cg_get_path(controller, path, "memory.usage_in_bytes", &p);
else
r = cg_get_path(controller, path, "memory.current", &p);
if (r < 0)
return r;

Expand All @@ -234,7 +237,7 @@ static int process(
if (g->memory > 0)
g->memory_valid = true;

} else if (streq(controller, "blkio")) {
} else if (streq(controller, "blkio") && cg_unified() <= 0) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *p = NULL;
uint64_t wr = 0, rd = 0;
Expand Down
Loading

0 comments on commit efdb023

Please sign in to comment.