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

Cyrill Gorcunov gorcunov at openvz.org
Fri May 13 13:03:43 PDT 2016


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         | 504 ++++++++++++++++++++++++++++++++++++++++++++
 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, 571 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..5328efc22d3e
--- /dev/null
+++ b/criu/cgroup-props.c
@@ -0,0 +1,504 @@
+#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: "
+
+enum {
+	CGP_MERGE,
+	CGP_REPLACE,
+};
+
+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 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 int cgp_merge_props(cgp_list_entry_t *d, cgp_list_entry_t *s)
+{
+	size_t nr_props, i, j;
+
+	nr_props = d->cgp.nr_props + s->cgp.nr_props;
+	if (xrealloc_safe(&d->cgp.props, nr_props * sizeof(char *)))
+		return -ENOMEM;
+
+	/*
+	 * FIXME: Check for duplicates in propties?
+	 */
+	for (i = d->cgp.nr_props, j = 0; i < nr_props; i++, j++) {
+		d->cgp.props[i] = xstrdup(s->cgp.props[j]);
+		if (!d->cgp.props[i])
+			return -ENOMEM;
+		d->cgp.nr_props++;
+	}
+
+	return 0;
+}
+
+static int cgp_handle_props(cgp_list_entry_t *p, int strategy)
+{
+	cgp_list_entry_t *t;
+
+	list_for_each_entry(t, &cgp_list, list) {
+		if (strcmp(t->cgp.name, p->cgp.name))
+			continue;
+
+		pr_debug("%s \"%s\" controller properties\n",
+			 strategy == CGP_MERGE ?
+			 "Merging" : "Replacing",
+			 p->cgp.name);
+
+		if (strategy == CGP_MERGE) {
+			int ret;
+
+			ret = cgp_merge_props(t, p);
+			cgp_free(p);
+			return ret;
+		} else if (strategy == CGP_REPLACE) {
+			/*
+			 * Simply drop out previous instance.
+			 */
+			list_del(&t->list);
+			cgp_free(t);
+			break;
+		} else
+			BUG();
+	}
+
+	/*
+	 * New controller, simply add it.
+	 */
+	list_add(&p->list, &cgp_list);
+	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 strategy;
+	int ret = 0;
+	char *p;
+
+	/*
+	 * 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;
+		}
+
+		if (cgp_handle_props(cgp_entry, strategy))
+			goto err_parse;
+
+		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;
+}
+
+static int cgp_parse_builtins(void)
+{
+	static const char predefined_stream[] =
+		"\"cpu\":\n"
+			" - \"strategy\": \"replace\"\n"
+			" - \"properties\": "
+			"[ "
+				"\"cpu.shares\", "
+				"\"cpu.cfs_period_us\", "
+				"\"cpu.cfs_quota_us\", "
+				"\"cpu.rt_period_us\", "
+				"\"cpu.rt_runtime_us\", "
+				"\"notify_on_release\" "
+			"]\n"
+		/* limit_in_bytes and memsw.limit_in_bytes must be set in this order */
+		"\"memory\":\n"
+			" - \"strategy\": \"replace\"\n"
+			" - \"properties\": "
+			"[ "
+				"\"memory.limit_in_bytes\", "
+				"\"memory.memsw.limit_in_bytes\", "
+				"\"memory.use_hierarchy\", "
+				"\"notify_on_release\" "
+			"]\n"
+		/*
+		 * 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\":\n"
+			" - \"strategy\": \"replace\"\n"
+			" - \"properties\": "
+			"[ "
+				"\"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\" "
+			"]\n"
+		"\"blkio\":\n"
+			" - \"strategy\": \"replace\"\n"
+			" - \"properties\": "
+			"[ "
+				"\"blkio.weight\", "
+				"\"notify_on_release\" "
+			"]\n"
+		"\"freezer\":\n"
+			" - \"strategy\": \"replace\"\n"
+			" - \"properties\": "
+			"[ "
+				"\"notify_on_release\" "
+			"]\n";
+
+	return cgp_parse_stream((void *)predefined_stream,
+				strlen(predefined_stream));
+}
+
+int cgp_init(char *stream, size_t len, char *path)
+{
+	int ret;
+
+	ret = cgp_parse_builtins();
+	if (ret)
+		goto err;
+
+	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;
+
+	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 817fd74b0ab2..8ef46a4cd9f2 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"
@@ -1556,6 +1557,7 @@ static int cr_dump_finish(int ret)
 		ret = -1;
 
 	cr_plugin_fini(CR_PLUGIN_STAGE__DUMP, ret);
+	cgp_fini();
 
 	if (!ret) {
 		/*
@@ -1655,6 +1657,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 ec7cc53a5d4c..99daee3fe2c0 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"))
@@ -868,6 +877,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 521022623910..aacb9c36ae16 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"
@@ -2192,6 +2193,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;
-- 
2.5.5



More information about the CRIU mailing list