[CRIU] [PATCH 1/4] cg: Add ability to dump custom cgroup properties
Tycho Andersen
tycho.andersen at canonical.com
Tue May 3 07:39:41 PDT 2016
On Tue, May 03, 2016 at 02:30:16PM +0300, Cyrill Gorcunov wrote:
> We have some common predefined properties such as "cpuset.cpus" and etc gathered
> in @cgp_predefined set, but there might be situation when only predefined ones
> are not enough, so add ability to specify properties via --cgroup-props
> and/or --cgroup-props-file options.
>
> For example one may pass file with content
>
> "cpu":
> - "strategy": "merge"
> - "properties": ["cpu.shares", "cpu.cfs_period_us"]
>
> to dump custom properties for cpu controller.
>
> The description is implemented in almost valid yaml, probably we will
> need to support the various forms, but oneline is enough for now.
>
> Signed-off-by: Cyrill Gorcunov <gorcunov at openvz.org>
> ---
> criu/Makefile.crtools | 1 +
> criu/cgroup-props.c | 551 ++++++++++++++++++++++++++++++++++++++++++++
> criu/cgroup.c | 102 +-------
> criu/cr-dump.c | 8 +
> criu/crtools.c | 16 ++
> criu/include/cgroup-props.h | 19 ++
> criu/include/cr_options.h | 2 +
> criu/proc_parse.c | 13 ++
> 8 files changed, 618 insertions(+), 94 deletions(-)
> create mode 100644 criu/cgroup-props.c
> create mode 100644 criu/include/cgroup-props.h
>
> diff --git a/criu/Makefile.crtools b/criu/Makefile.crtools
> index 1e452f4e16b1..657b08e9f0c9 100644
> --- a/criu/Makefile.crtools
> +++ b/criu/Makefile.crtools
> @@ -5,6 +5,7 @@ obj-y += aio.o
> obj-y += bfd.o
> obj-y += bitmap.o
> obj-y += cgroup.o
> +obj-y += cgroup-props.o
> obj-y += cr-check.o
> obj-y += cr-dedup.o
> obj-y += cr-dump.o
> diff --git a/criu/cgroup-props.c b/criu/cgroup-props.c
> new file mode 100644
> index 000000000000..7713ed4bb98d
> --- /dev/null
> +++ b/criu/cgroup-props.c
> @@ -0,0 +1,551 @@
> +#include <stdio.h>
> +#include <string.h>
> +#include <ctype.h>
> +#include <unistd.h>
> +#include <fcntl.h>
> +
> +#include <sys/mman.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +
> +#include "compiler.h"
> +#include "cgroup-props.h"
> +#include "cr_options.h"
> +#include "config.h"
> +#include "xmalloc.h"
> +#include "string.h"
> +#include "util.h"
> +#include "list.h"
> +#include "log.h"
> +#include "bug.h"
> +
> +#undef LOG_PREFIX
> +#define LOG_PREFIX "cg-prop: "
> +
> +/*
> + * Predefined properties.
> + */
> +static const char *cpu_props[] = {
> + "cpu.shares",
> + "cpu.cfs_period_us",
> + "cpu.cfs_quota_us",
> + "cpu.rt_period_us",
> + "cpu.rt_runtime_us",
> + "notify_on_release",
> +};
> +
> +static const char *memory_props[] = {
> + /* limit_in_bytes and memsw.limit_in_bytes must be set in this order */
> + "memory.limit_in_bytes",
> + "memory.memsw.limit_in_bytes",
> + "memory.use_hierarchy",
> + "notify_on_release",
> +};
> +
> +static const char *cpuset_props[] = {
> + /*
> + * cpuset.cpus and cpuset.mems must be set before the process moves
> + * into its cgroup; they are "initialized" below to whatever the root
> + * values are in copy_special_cg_props so as not to cause ENOSPC when
> + * values are restored via this code.
> + */
> + "cpuset.cpus",
> + "cpuset.mems",
> + "cpuset.memory_migrate",
> + "cpuset.cpu_exclusive",
> + "cpuset.mem_exclusive",
> + "cpuset.mem_hardwall",
> + "cpuset.memory_spread_page",
> + "cpuset.memory_spread_slab",
> + "cpuset.sched_load_balance",
> + "cpuset.sched_relax_domain_level",
> + "notify_on_release",
> +};
> +
> +static const char *blkio_props[] = {
> + "blkio.weight",
> + "notify_on_release",
> +};
> +
> +static const char *freezer_props[] = {
> + "notify_on_release",
> +};
> +
> +static const char *____criu_global_props____[] = {
> + "cgroup.clone_children",
> + "notify_on_release",
> + "cgroup.procs",
> + "tasks",
> +};
> +
> +cgp_t cgp_global = {
> + .name = "____criu_global_props____",
> + .nr_props = ARRAY_SIZE(____criu_global_props____),
> + .props = ____criu_global_props____,
> +};
> +
> +typedef struct {
> + struct list_head list;
> + cgp_t cgp;
> +} cgp_list_entry_t;
> +
> +static cgp_list_entry_t cgp_predefined[5];
> +
> +static struct list_head cgp_predefined_list = {
> + .next = &cgp_predefined[0].list,
> + .prev = &cgp_predefined[4].list,
> +};
> +
> +static cgp_list_entry_t cgp_predefined[5] = {
> + {
> + .list.next = &cgp_predefined[1].list,
> + .list.prev = &cgp_predefined_list,
> + .cgp = {
> + .name = "cpu",
> + .nr_props = ARRAY_SIZE(cpu_props),
> + .props = cpu_props,
> + },
> + }, {
> + .list.next = &cgp_predefined[2].list,
> + .list.prev = &cgp_predefined[1].list,
> + .cgp = {
> + .name = "memory",
> + .nr_props = ARRAY_SIZE(memory_props),
> + .props = memory_props,
> + },
> + }, {
> + .list.next = &cgp_predefined[3].list,
> + .list.prev = &cgp_predefined[2].list,
> + .cgp = {
> + .name = "cpuset",
> + .nr_props = ARRAY_SIZE(cpuset_props),
> + .props = cpuset_props,
> + },
> + }, {
> + .list.next = &cgp_predefined[4].list,
> + .list.prev = &cgp_predefined[3].list,
> + .cgp = {
> + .name = "blkio",
> + .nr_props = ARRAY_SIZE(blkio_props),
> + .props = blkio_props,
> + },
> + }, {
> + .list.next = &cgp_predefined_list,
> + .list.prev = &cgp_predefined[4].list,
> + .cgp = {
> + .name = "freezer",
> + .nr_props = ARRAY_SIZE(freezer_props),
> + .props = freezer_props,
> + },
> + },
> +};
> +
> +static LIST_HEAD(cgp_list);
> +
> +static void cgp_free(cgp_list_entry_t *p)
> +{
> + size_t i;
> +
> + if (p) {
> + for (i = 0; i < p->cgp.nr_props; i++)
> + xfree((void *)p->cgp.props[i]);
> + xfree((void *)p->cgp.name);
> + xfree((void *)p->cgp.props);
> + xfree(p);
> + }
> +}
> +
> +static cgp_list_entry_t *cgp_get_predefined(const char *name)
> +{
> + cgp_list_entry_t *p;
> +
> + list_for_each_entry(p, &cgp_predefined_list, list) {
> + if (!strcmp(p->cgp.name, name))
> + return p;
> + }
> +
> + return NULL;
> +}
> +
> +static int cgp_replace_props(cgp_list_entry_t *t)
> +{
> + cgp_list_entry_t *p, *n;
> +
> + list_for_each_entry_safe(p, n, &cgp_predefined_list, list) {
> + if (strcmp(t->cgp.name, p->cgp.name))
> + continue;
> + pr_debug("Replacing \"%s\" controller props\n", t->cgp.name);
> + list_del(&p->list);
> + break;
> + }
> +
> + return 0;
> +}
> +
> +static int cgp_merge_props(cgp_list_entry_t *t)
> +{
> + cgp_list_entry_t *p, *n;
> + size_t nr_props, i, j;
> +
> + list_for_each_entry_safe(p, n, &cgp_predefined_list, list) {
> + if (strcmp(t->cgp.name, p->cgp.name))
> + continue;
> +
> + pr_debug("Merging \"%s\" controller props\n", t->cgp.name);
> +
> + nr_props = t->cgp.nr_props + p->cgp.nr_props;
> + if (xrealloc_safe(&t->cgp.props, nr_props * sizeof(char *)))
> + return -ENOMEM;
> +
> + for (i = t->cgp.nr_props, j = 0; i < nr_props; i++, j++) {
> + t->cgp.props[i] = xstrdup(p->cgp.props[j]);
> + if (!t->cgp.props[i])
> + return -ENOMEM;
> + t->cgp.nr_props++;
> + }
> +
> + list_del(&p->list);
> + break;
> + }
> +
> + return 0;
> +}
> +
> +static char *skip_spaces(char **stream, size_t *len)
> +{
> + if (stream && *len) {
> + char *p = *stream;
> +
> + while (p && *len && *p == ' ')
> + p++, (*len)--;
> + if (p != *stream)
> + *stream = p;
> + return p;
> + }
> +
> + return NULL;
> +}
> +
> +static bool eat_symbol(char **stream, size_t *len, char sym, bool skip_ws)
> +{
> + char *p = skip_ws ? skip_spaces(stream, len) : (stream ? *stream : NULL);
> +
> + if (!p || *p != sym || !*len)
> + return false;
> + (*stream) = p + 1;
> + (*len)--;
> + return true;
> +}
> +
> +static bool eat_symbols(char **stream, size_t *len, char *syms, size_t n_syms, bool skip_ws)
> +{
> + char *p = skip_ws ? skip_spaces(stream, len) : (stream ? *stream : NULL);
> + size_t i;
> +
> + if (p && *len) {
> + char *stream_orig = *stream;
> + size_t len_orig = *len;
> +
> + for (i = 0; i < n_syms; i++) {
> + if (!eat_symbol(stream, len, syms[i], false)) {
> + *stream = stream_orig;
> + *len = len_orig;
> + goto nomatch;
> + }
> + }
> + return true;
> + }
> +nomatch:
> + return false;
> +}
> +
> +static bool eat_word(char **stream, size_t *len, char *word, size_t word_len, bool skip_ws)
> +{
> + char *p = skip_ws ? skip_spaces(stream, len) : (stream ? *stream : NULL);
> +
> + if (p && *len >= word_len) {
> + if (!strncmp(p, word, word_len)) {
> + (*stream) += word_len;
> + (*len) -= word_len;
> + return true;
> + }
> + }
> +
> + return false;
> +}
> +
> +static char *get_quoted(char **stream, size_t *len, bool skip_ws)
> +{
> + char *p = skip_ws ? skip_spaces(stream, len) : (stream ? *stream : NULL);
> + char *from = p + 1;
> + char *dst;
> +
> + if (!p || *p != '\"')
> + return NULL;
> +
> + for (p = from, (*len)--; (*len); p++, (*len)--) {
> + if (*p == '\"') {
> + if (p == from)
> + break;
> + dst = xmalloc(p - from + 1);
> + if (!dst)
> + break;
> +
> + memcpy(dst, from, p - from);
> + dst[p - from] = '\0';
> +
> + (*stream) = p + 1;
> + (*len)--;
> + return dst;
> + }
> + }
> +
> + return NULL;
> +}
> +
> +static int cgp_parse_stream(char *stream, size_t len)
> +{
> + cgp_list_entry_t *cgp_entry = NULL;
> + int ret = 0;
> + char *p;
> +
> + enum {
> + CGP_MERGE,
> + CGP_REPLACE,
> + } strategy;
> +
> + /*
> + * We expect the following format here
> + * (very simplified YAML!)
> + *
> + * "cpu":
> + * - "strategy": "replace"
> + * - "properties": ["cpu.shares", "cpu.cfs_period_us"]
> + * "memory":
> + * - "strategy": "merge"
> + * - "properties": ["memory.limit_in_bytes", "memory.memsw.limit_in_bytes"]
> + *
> + * and etc.
> + */
> +
> + while (len) {
> + /*
> + * Controller name.
> + */
> + p = get_quoted(&stream, &len, false);
> + if (!p) {
> + pr_err("Expecting controller name\n");
> + goto err_parse;
> + }
> +
> + pr_info("Parsing controller %s\n", p);
> +
> + cgp_entry = xzalloc(sizeof(*cgp_entry));
> + if (cgp_entry) {
> + INIT_LIST_HEAD(&cgp_entry->list);
> + cgp_entry->cgp.name = p;
> + } else {
> + pr_err("Can't allocate memory for controller %s\n", p);
> + xfree(p);
> + return -ENOMEM;
> + }
> +
> + if (!eat_symbols(&stream, &len, ":\n - ", 5, true)) {
> + pr_err("Expected \':\\n - \' sequence controller's %s stream\n",
> + cgp_entry->cgp.name);
> + goto err_parse;
> + }
> +
> + if (!eat_word(&stream, &len, "\"strategy\":", 11, true)) {
> + pr_err("Expected \'stategy:\' keyword in controller's %s stream\n",
> + cgp_entry->cgp.name);
> + goto err_parse;
> + }
> +
> + p = get_quoted(&stream, &len, true);
> + if (!p) {
> + pr_err("Expected strategy in controller's %s stream\n",
> + cgp_entry->cgp.name);
> + goto err_parse;
> + };
> +
> + if (!strcmp(p, "merge")) {
> + strategy = CGP_MERGE;
> + } else if (!strcmp(p, "replace")) {
> + strategy = CGP_REPLACE;
> + } else {
> + xfree(p);
> + pr_err("Unknown strategy \"%s\" in controller's %s stream\n",
> + p, cgp_entry->cgp.name);
> + goto err_parse;
> + }
> +
> + pr_info("\tStrategy \"%s\"\n", p);
> + xfree(p);
> +
> + if (!eat_symbols(&stream, &len, "\n - ", 4, true)) {
> + pr_err("Expected \':\\n - \' sequence controller's %s stream\n",
> + cgp_entry->cgp.name);
> + goto err_parse;
> + }
> +
> + if (!eat_word(&stream, &len, "\"properties\":", 13, true)) {
> + pr_err("Expected \"properties:\" keyword in controller's %s stream\n",
> + cgp_entry->cgp.name);
> + goto err_parse;
> + }
> +
> + if (!eat_symbol(&stream, &len, '[', true)) {
> + pr_err("Expected \'[\' sequence controller's %s properties stream\n",
> + cgp_entry->cgp.name);
> + goto err_parse;
> + }
> +
> + while ((p = get_quoted(&stream, &len, true))) {
> + if (!p) {
> + pr_err("Expected property name for controller %s\n",
> + cgp_entry->cgp.name);
> + goto err_parse;
> + }
> +
> + if (xrealloc_safe(&cgp_entry->cgp.props,
> + (cgp_entry->cgp.nr_props + 1) * sizeof(char *))) {
> + pr_err("Can't allocate property for controller %s\n",
> + cgp_entry->cgp.name);
> + return -1;
> + }
> +
> + cgp_entry->cgp.props[cgp_entry->cgp.nr_props++] = p;
> + pr_info("\tProperty %s\n", p);
> +
> + if (!eat_symbol(&stream, &len, ',', true)) {
> + if (stream[0] == ']') {
> + stream++, len--;
> + break;
> + }
> + pr_err("Expected ']' in controller's %s stream\n",
> + cgp_entry->cgp.name);
> + goto err_parse;
> + }
> + }
> +
> + if (!eat_symbol(&stream, &len, '\n', true) && len) {
> + pr_err("Expected \'\\n\' symbol in controller's %s stream\n",
> + cgp_entry->cgp.name);
> + goto err_parse;
> + }
> +
> + list_add(&cgp_entry->list, &cgp_list);
> +
> + if (strategy == CGP_MERGE) {
> + if (cgp_merge_props(cgp_entry)) {
> + pr_err("Can't merge controller \'%s\'\n",
> + cgp_entry->cgp.name);
> + goto out;
> + }
> + } else if (strategy == CGP_REPLACE) {
> + if (cgp_replace_props(cgp_entry)) {
> + pr_err("Can't replace controller \'%s\'\n",
> + cgp_entry->cgp.name);
> + goto out;
> + }
> + } else
> + BUG();
This isn't a BUG(), it's invalid user input right?
Tycho
More information about the CRIU
mailing list