[CRIU] [PATCH 1/4] cg: Add ability to dump custom cgroup properties

Pavel Emelyanov xemul at virtuozzo.com
Fri May 6 05:30:21 PDT 2016


On 05/03/2016 02:30 PM, 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",
> +};

I thought we agreed on putting these "defaults" into a text file sitting somewhere
in ... I don't know ... /etc? /var/something? ... anyway. So have we agreed on that
or not?

> +
> +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();
> +
> +		cgp_entry = NULL;
> +	}
> +
> +	ret = 0;
> +out:
> +	return ret;
> +
> +err_parse:
> +	cgp_free(cgp_entry);
> +	ret = -EINVAL;
> +	goto out;
> +}
> +
> +static int cgp_parse_file(char *path)
> +{
> +	void *mem = MAP_FAILED;
> +	int fd = -1, ret = -1;
> +	struct stat st;
> +
> +	fd = open(path, O_RDONLY);
> +	if (fd < 0) {
> +		pr_perror("Can't open file %s\n", path);
> +		goto err;
> +	}
> +
> +	if (fstat(fd, &st)) {
> +		pr_perror("Can't stat file %s\n", path);
> +		goto err;
> +	}
> +
> +	mem = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FILE, fd, 0);
> +	if (mem == MAP_FAILED) {
> +		pr_perror("Can't mmap file %s\n", path);
> +		goto err;
> +	}
> +
> +	if (cgp_parse_stream(mem, st.st_size)) {
> +		pr_err("Failed to parse file `%s'\n", path);
> +		goto err;
> +	}
> +
> +	ret = 0;
> +err:
> +	if (mem != MAP_FAILED)
> +		munmap(mem, st.st_size);
> +	close_safe(&fd);
> +	return ret;
> +}
> +
> +int cgp_init(char *stream, size_t len, char *path)
> +{
> +	int ret = 0;
> +
> +	if (stream && len) {
> +		ret = cgp_parse_stream(stream, len);
> +		if (ret)
> +			goto err;
> +	}
> +
> +	if (path)
> +		ret = cgp_parse_file(path);
> +err:
> +	return ret;
> +}
> +
> +bool cgp_should_skip_controller(const char *name)
> +{
> +	/*
> +	 * FIXME Implement!
> +	 */
> +	return false;
> +}
> +
> +const cgp_t *cgp_get_props(const char *name)
> +{
> +	cgp_list_entry_t *p;
> +
> +	p = cgp_get_predefined(name);
> +	if (p)
> +		return &p->cgp;
> +
> +	list_for_each_entry(p, &cgp_list, list) {
> +		if (!strcmp(p->cgp.name, name))
> +			return &p->cgp;
> +	}
> +
> +	return NULL;
> +}
> +
> +void cgp_fini(void)
> +{
> +	cgp_list_entry_t *p, *t;
> +
> +	list_for_each_entry_safe(p, t, &cgp_list, list)
> +		cgp_free(p);
> +	INIT_LIST_HEAD(&cgp_list);
> +}
> diff --git a/criu/cgroup.c b/criu/cgroup.c
> index a706ff6f76ce..1bf52851ea17 100644
> --- a/criu/cgroup.c
> +++ b/criu/cgroup.c
> @@ -10,6 +10,7 @@
>  #include "list.h"
>  #include "xmalloc.h"
>  #include "cgroup.h"
> +#include "cgroup-props.h"
>  #include "cr_options.h"
>  #include "pstree.h"
>  #include "proc_parse.h"
> @@ -25,73 +26,6 @@
>  #include "images/cgroup.pb-c.h"
>  
>  /*
> - * These string arrays have the names of all the properties that will be
> - * restored. To add a property for a cgroup type, add it to the
> - * corresponding char array above the NULL terminator. If you are adding
> - * a new cgroup family all together, you must also edit get_known_properties()
> - * Currently the code only supports properties with 1 value
> - */
> -
> -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",
> -	NULL
> -};
> -
> -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",
> -	NULL
> -};
> -
> -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",
> -	NULL
> -};
> -
> -static const char *blkio_props[] = {
> -	"blkio.weight",
> -	"notify_on_release",
> -	NULL
> -};
> -
> -static const char *freezer_props[] = {
> -	"notify_on_release",
> -	NULL
> -};
> -
> -static const char *global_props[] = {
> -	"cgroup.clone_children",
> -	"notify_on_release",
> -	"cgroup.procs",
> -	"tasks",
> -	NULL
> -};
> -
> -/*
>   * This structure describes set of controller groups
>   * a task lives in. The cg_ctl entries are stored in
>   * the @ctls list sorted by the .name field and then
> @@ -421,33 +355,14 @@ static void free_all_cgroup_props(struct cgroup_dir *ncd)
>  	ncd->n_properties = 0;
>  }
>  
> -static const char **get_known_properties(char *controller)
> -{
> -	const char **prop_arr = NULL;
> -
> -	if (!strcmp(controller, "cpu"))
> -		prop_arr = cpu_props;
> -	else if (!strcmp(controller, "memory"))
> -		prop_arr = memory_props;
> -	else if (!strcmp(controller, "cpuset"))
> -		prop_arr = cpuset_props;
> -	else if (!strcmp(controller, "blkio"))
> -		prop_arr = blkio_props;
> -	else if (!strcmp(controller, "freezer"))
> -		prop_arr = freezer_props;
> -
> -	return prop_arr;
> -}
> -
> -static int dump_cg_props_array(const char *fpath, struct cgroup_dir *ncd,
> -			       const char **prop_arr)
> +static int dump_cg_props_array(const char *fpath, struct cgroup_dir *ncd, const cgp_t *cgp)
>  {
>  	int j;
>  	char buf[PATH_MAX];
>  	struct cgroup_prop *prop;
>  
> -	for (j = 0; prop_arr != NULL && prop_arr[j] != NULL; ++j) {
> -		if (snprintf(buf, PATH_MAX, "%s/%s", fpath, prop_arr[j]) >= PATH_MAX) {
> +	for (j = 0; cgp && j < cgp->nr_props; j++) {
> +		if (snprintf(buf, PATH_MAX, "%s/%s", fpath, cgp->props[j]) >= PATH_MAX) {
>  			pr_err("snprintf output was truncated\n");
>  			return -1;
>  		}
> @@ -457,7 +372,7 @@ static int dump_cg_props_array(const char *fpath, struct cgroup_dir *ncd,
>  			continue;
>  		}
>  
> -		prop = create_cgroup_prop(prop_arr[j]);
> +		prop = create_cgroup_prop(cgp->props[j]);
>  		if (!prop) {
>  			free_all_cgroup_props(ncd);
>  			return -1;
> @@ -483,15 +398,14 @@ static int add_cgroup_properties(const char *fpath, struct cgroup_dir *ncd,
>  	int i;
>  
>  	for (i = 0; i < controller->n_controllers; ++i) {
> +		const cgp_t *cgp = cgp_get_props(controller->controllers[i]);
>  
> -		const char **prop_arr = get_known_properties(controller->controllers[i]);
> -
> -		if (dump_cg_props_array(fpath, ncd, prop_arr) < 0) {
> +		if (dump_cg_props_array(fpath, ncd, cgp) < 0) {
>  			pr_err("dumping known properties failed");
>  			return -1;
>  		}
>  
> -		if (dump_cg_props_array(fpath, ncd, global_props) < 0) {
> +		if (dump_cg_props_array(fpath, ncd, &cgp_global) < 0) {
>  			pr_err("dumping global properties failed");
>  			return -1;
>  		}
> diff --git a/criu/cr-dump.c b/criu/cr-dump.c
> index 5ac9fd041e4e..b5531e43d115 100644
> --- a/criu/cr-dump.c
> +++ b/criu/cr-dump.c
> @@ -61,6 +61,7 @@
>  #include "cpu.h"
>  #include "elf.h"
>  #include "cgroup.h"
> +#include "cgroup-props.h"
>  #include "file-lock.h"
>  #include "page-xfer.h"
>  #include "kerndat.h"
> @@ -1554,6 +1555,7 @@ static int cr_dump_finish(int ret)
>  		ret = -1;
>  
>  	cr_plugin_fini(CR_PLUGIN_STAGE__DUMP, ret);
> +	cgp_fini();
>  
>  	if (!ret) {
>  		/*
> @@ -1653,6 +1655,12 @@ int cr_dump_tasks(pid_t pid)
>  	if (vdso_init())
>  		goto err;
>  
> +	if (cgp_init(opts.cgroup_props,
> +		     opts.cgroup_props ?
> +		     strlen(opts.cgroup_props) : 0,
> +		     opts.cgroup_props_file))
> +		goto err;
> +
>  	if (parse_cg_info())
>  		goto err;
>  
> diff --git a/criu/crtools.c b/criu/crtools.c
> index 7a0f9778db74..d2bb96b9092f 100644
> --- a/criu/crtools.c
> +++ b/criu/crtools.c
> @@ -38,6 +38,7 @@
>  #include "mount.h"
>  #include "namespaces.h"
>  #include "cgroup.h"
> +#include "cgroup-props.h"
>  #include "cpu.h"
>  #include "action-scripts.h"
>  #include "irmap.h"
> @@ -320,6 +321,8 @@ int main(int argc, char *argv[], char *envp[])
>  		{ "extra",			no_argument,		0, 1077	},
>  		{ "experimental",		no_argument,		0, 1078	},
>  		{ "all",			no_argument,		0, 1079	},
> +		{ "cgroup-props",		required_argument,	0, 1080	},
> +		{ "cgroup-props-file",		required_argument,	0, 1081	},
>  		{ },
>  	};
>  
> @@ -622,6 +625,12 @@ int main(int argc, char *argv[], char *envp[])
>  			opts.check_extra_features = true;
>  			opts.check_experimental_features = true;
>  			break;
> +		case 1080:
> +			opts.cgroup_props = optarg;
> +			break;
> +		case 1081:
> +			opts.cgroup_props_file = optarg;
> +			break;
>  		case 'V':
>  			pr_msg("Version: %s\n", CRIU_VERSION);
>  			if (strcmp(CRIU_GITID, "0"))
> @@ -873,6 +882,13 @@ usage:
>  "                        change the root cgroup the controller will be\n"
>  "                        installed into. No controller means that root is the\n"
>  "                        default for all controllers not specified.\n"
> +"  --cgroup-props STRING\n"
> +"                        define cgroup controllers and properties\n"
> +"                        to be checkpointed, which are described\n"
> +"                        via STRING using simplified YAML format.\n"
> +"  --cgroup-props-file FILE\n"
> +"                        same as --cgroup-props but taking descrition\n"
> +"                        from the path specified.\n"
>  "  --skip-mnt PATH       ignore this mountpoint when dumping the mount namespace.\n"
>  "  --enable-fs FSNAMES   a comma separated list of filesystem names or \"all\".\n"
>  "                        force criu to (try to) dump/restore these filesystem's\n"
> diff --git a/criu/include/cgroup-props.h b/criu/include/cgroup-props.h
> new file mode 100644
> index 000000000000..e6c96bae94f8
> --- /dev/null
> +++ b/criu/include/cgroup-props.h
> @@ -0,0 +1,19 @@
> +#ifndef __CR_CGROUP_PROPS_H__
> +#define __CR_CGROUP_PROPS_H__
> +
> +#include <stdbool.h>
> +
> +typedef struct {
> +	const char	*name;
> +	size_t		nr_props;
> +	const char	**props;
> +} cgp_t;
> +
> +extern cgp_t cgp_global;
> +extern const cgp_t *cgp_get_props(const char *name);
> +extern bool cgp_should_skip_controller(const char *name);
> +
> +extern int cgp_init(char *stream, size_t len, char *path);
> +extern void cgp_fini(void);
> +
> +#endif /* __CR_CGROUP_PROPS_H__ */
> diff --git a/criu/include/cr_options.h b/criu/include/cr_options.h
> index 551ca7045b84..e36c36271e9d 100644
> --- a/criu/include/cr_options.h
> +++ b/criu/include/cr_options.h
> @@ -96,6 +96,8 @@ struct cr_options {
>  	char			**exec_cmd;
>  	unsigned int		manage_cgroups;
>  	char			*new_global_cg_root;
> +	char			*cgroup_props;
> +	char			*cgroup_props_file;
>  	struct list_head	new_cgroup_roots;
>  	bool			autodetect_ext_mounts;
>  	bool			enable_external_sharing;
> diff --git a/criu/proc_parse.c b/criu/proc_parse.c
> index 174b54044794..c9b132be1786 100644
> --- a/criu/proc_parse.c
> +++ b/criu/proc_parse.c
> @@ -33,6 +33,7 @@
>  #include "namespaces.h"
>  #include "files-reg.h"
>  #include "cgroup.h"
> +#include "cgroup-props.h"
>  
>  #include "protobuf.h"
>  #include "images/fdinfo.pb-c.h"
> @@ -2185,6 +2186,18 @@ int parse_cgroup_file(FILE *f, struct list_head *retl, unsigned int *n)
>  		if (e)
>  			*e = '\0';
>  
> +		/*
> +		 * Controllers and their props might be
> +		 * configured the way some of them are
> +		 * not taken into the image for migration
> +		 * sake or container specifics.
> +		 */
> +		if (cgp_should_skip_controller(name)) {
> +			pr_debug("cg-prop: Skipping controller %s\n", name);
> +			xfree(ncc);
> +			continue;
> +		}
> +
>  		ncc->name = xstrdup(name);
>  		ncc->path = xstrdup(path);
>  		ncc->cgns_prefix = 0;
> 



More information about the CRIU mailing list