[CRIU] [PATCH 2/2] Cgroup property restoration infrastructure

gbellack at google.com gbellack at google.com
Tue Aug 5 12:50:11 PDT 2014


From: Garrison Bellack <gbellack at google.com>

Restores 2 cgroup properties after the criu restoration of tasks.
Currently the cgroup files to be restored are static but
are easily extendable. If a cgroup exists during restoration,
its properties will not be overwritten.
Work based off Tycho Anderson tycho.andersen at canonical.com

Change-Id: Ida32b9773eeac1d4d6e82ad644524ed099d5f9b1
Signed-off-by: Garrison Bellack <gbellack at google.com>
---
 cgroup.c              | 343 +++++++++++++++++++++++++++++++++++++++++++++++++-
 cr-restore.c          |   3 +
 include/cgroup.h      |  11 ++
 protobuf/cgroup.proto |   8 +-
 4 files changed, 359 insertions(+), 6 deletions(-)

diff --git a/cgroup.c b/cgroup.c
index 8c99e9d..1a91857 100644
--- a/cgroup.c
+++ b/cgroup.c
@@ -20,6 +20,23 @@
 #include "protobuf/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.
+ * Currently the code only supports properties with 1 value
+ */
+
+static const char *cpu_props[] = {
+	"cpu.shares",
+	NULL
+};
+
+static const char *memory_props[] = {
+	"memory.limit_in_bytes",
+	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
@@ -224,6 +241,129 @@ static int find_dir(const char *path, struct list_head *dirs, struct cgroup_dir
 	return NO_MATCH;
 }
 
+/*
+ * Currently this function only supports properties that have 1 value, under 100
+ * chars
+ */
+static int read_cgroup_prop(struct cgroup_prop *property, const char *fpath)
+{
+	char pbuf[PATH_MAX], buf[100];
+	FILE *f;
+
+	if (snprintf(pbuf, PATH_MAX, "%s/%s", fpath, property->name) >= PATH_MAX) {
+		pr_err("snprintf output was truncated");
+		return -1;
+	}
+
+	f = fopen(pbuf, "r");
+	if (!f) {
+		pr_err("Failed opening %s\n", pbuf);
+		property->value = NULL;
+		return -1;
+	}
+
+	memset(buf, 0, sizeof(buf));
+	if (fread(buf, sizeof(buf), 1, f) != 1) {
+		if (!feof(f)) {
+			pr_err("Failed scanning %s\n", pbuf);
+			fclose(f);
+			return -1;
+		}
+	}
+
+	if (fclose(f) != 0) {
+		pr_err("Failed closing %s\n", pbuf);
+		return -1;
+	}
+
+	property->value = xstrdup(buf);
+	if (!property->value)
+		return -1;
+
+	return 0;
+}
+
+static struct cgroup_prop *create_cgroup_prop(const char *name)
+{
+	struct cgroup_prop *property;
+
+	property = xmalloc(sizeof(*property));
+	if (!property)
+		return NULL;
+
+	property->name = xstrdup(name);
+	if (!property->name) {
+		xfree(property);
+		return NULL;
+	}
+
+	property->value = NULL;
+	return property;
+}
+
+static void free_cgroup_prop(struct cgroup_prop *prop)
+{
+	xfree(prop->name);
+	xfree(prop->value);
+	xfree(prop);
+}
+
+static void free_all_cgroup_props(struct list_head *properties,
+				  unsigned int *n_properties)
+{
+	struct cgroup_prop *prop;
+
+	list_for_each_entry(prop, properties, list) {
+		free_cgroup_prop(prop);
+	}
+
+	INIT_LIST_HEAD(properties);
+	*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;
+
+	return prop_arr;
+}
+
+static int add_cgroup_properties(const char *fpath, struct list_head *properties,
+				 unsigned int *n_properties,
+				 struct cg_controller *controller)
+{
+	int i, j;
+	struct cgroup_prop *prop;
+
+	for (i = 0; i < controller->n_controllers; ++i) {
+
+		const char **prop_arr = get_known_properties(controller->controllers[i]);
+
+		for (j = 0; prop_arr != NULL && prop_arr[j] != NULL; ++j) {
+			prop = create_cgroup_prop(prop_arr[j]);
+			if (!prop) {
+				free_all_cgroup_props(properties, n_properties);
+				return -1;
+			}
+			if (read_cgroup_prop(prop, fpath) < 0) {
+				free_cgroup_prop(prop);
+				free_all_cgroup_props(properties, n_properties);
+				return -1;
+			}
+			pr_info("Dumping value %s from %s/%s\n", prop->value, fpath, prop->name);
+			list_add_tail(&prop->list, properties);
+			(*n_properties)++;
+		}
+	}
+
+	return 0;
+}
+
 static int add_cgroup(const char *fpath, const struct stat *sb, int typeflag)
 {
 	struct cgroup_dir *ncd = NULL, *match;
@@ -267,6 +407,16 @@ static int add_cgroup(const char *fpath, const struct stat *sb, int typeflag)
 
 		INIT_LIST_HEAD(&ncd->children);
 		ncd->n_children = 0;
+
+		INIT_LIST_HEAD(&ncd->properties);
+		ncd->n_properties = 0;
+		if (add_cgroup_properties(fpath, &ncd->properties,
+					  &ncd->n_properties,
+					  current_controller) < 0) {
+			ret = -1;
+			goto out;
+		}
+
 		return 0;
 	} else
 		return 0;
@@ -398,6 +548,44 @@ int dump_task_cgroup(struct pstree_item *item, u32 *cg_id)
 	return 0;
 }
 
+static int dump_cg_dir_props(struct list_head *props, size_t n_props,
+			     CgroupPropEntry ***ents)
+{
+	struct cgroup_prop *prop_cur;
+	CgroupPropEntry *cpe;
+	void *m;
+	int i = 0;
+
+	m = xmalloc(n_props * (sizeof(CgroupPropEntry *) + sizeof(CgroupPropEntry)));
+	*ents = m;
+	if (!m)
+		return -1;
+
+	cpe = m + n_props * sizeof(CgroupPropEntry *);
+
+	list_for_each_entry(prop_cur, props, list) {
+		cgroup_prop_entry__init(cpe);
+		cpe->name = xstrdup(prop_cur->name);
+		cpe->value = xstrdup(prop_cur->value);
+		if (!cpe->name || !cpe->value)
+			goto error;
+		(*ents)[i++] = cpe++;
+	}
+
+	return 0;
+
+error:
+	while (i >= 0) {
+		xfree(cpe->name);
+		xfree(cpe->value);
+		--cpe;
+		--i;
+	}
+
+	xfree(*ents);
+	return -1;
+}
+
 static int dump_cg_dirs(struct list_head *dirs, size_t n_dirs, CgroupDirEntry ***ents)
 {
 	struct cgroup_dir *cur;
@@ -423,6 +611,16 @@ static int dump_cg_dirs(struct list_head *dirs, size_t n_dirs, CgroupDirEntry **
 				xfree(*ents);
 				return -1;
 			}
+
+		cde->n_properties = cur->n_properties;
+		if (cde->n_properties > 0) {
+			if (dump_cg_dir_props(&cur->properties,
+					      cde->n_properties, &cde->properties) < 0) {
+				xfree(*ents);
+				return -1;
+			}
+		}
+
 		(*ents)[i++] = cde++;
 	}
 
@@ -659,6 +857,126 @@ void fini_cgroup(void)
 	xfree(cg_yard);
 }
 
+static int restore_cgroup_prop(const CgroupPropEntry * cg_prop_entry_p,
+			       const char *cname, const char *dir, const int *cg_p)
+{
+	char path[PATH_MAX];
+	FILE *f;
+
+	if (!cg_prop_entry_p->value)
+		return 0;
+
+	if (snprintf(path, PATH_MAX, "%s/%s/%s", cname, dir, cg_prop_entry_p->name) >= PATH_MAX) {
+		pr_err("snprintf output was truncated for %s\n", cg_prop_entry_p->name);
+		return -1;
+	}
+
+	f = fopenat(*cg_p, path, "w+");
+	if (!f) {
+		pr_perror("Failed opening %s for writing\n", path);
+		return -1;
+	}
+
+	if (fprintf(f, "%s", cg_prop_entry_p->value) < 0) {
+		fclose(f);
+		pr_err("Failed writing %s to %s\n", cg_prop_entry_p->value, path);
+		return -1;
+	}
+
+	if (fclose(f) != 0) {
+		pr_err("Failed closing %s\n", path);
+		return -1;
+	}
+
+	pr_info("Restored cgroup property value %s to %s\n", cg_prop_entry_p->value, path);
+	return 0;
+}
+
+static int prepare_cgroup_dir_properties(char *controller, CgroupDirEntry **ents,
+					 unsigned int n_ents)
+{
+	unsigned int i, j;
+	int cg;
+
+	cg = get_service_fd(CGROUP_YARD);
+
+	for (i = 0; i < n_ents; i++) {
+		CgroupDirEntry *e = ents[i];
+
+		/*
+		 * Check to see if we made e->properties NULL during restore
+		 * because directory already existed and as such we don't want to
+		 * change its properties
+		 */
+		if (e->properties) {
+			for (j = 0; j < e->n_properties; ++j) {
+				if (restore_cgroup_prop(e->properties[j], controller, e->path, &cg) < 0)
+					return -1;
+			}
+		}
+
+		if (prepare_cgroup_dir_properties(controller, e->children, e->n_children) < 0)
+			return -1;
+	}
+
+	return 0;
+}
+
+static int get_controller_path(char *cname_path, int size, CgControllerEntry *c)
+{
+	int i;
+	int cnt = 0;
+	int n = strlen(c->cnames[0]);
+
+	if (n >= size - cnt) {
+		pr_err("Ran out of room for cname_path\n");
+		return -1;
+	}
+	strcpy(&cname_path[cnt], c->cnames[0]);
+	cnt += n;
+
+	/*
+	 * Co-Mounted Case
+	 * adds on other controllers for full path, ie. "cpu,cpuacct"
+	 */
+	for (i = 1; i < c->n_cnames; ++i) {
+		n = strlen(c->cnames[i]);
+		/* +1 for comma */
+		if (n + 1 >= size - cnt) {
+			pr_err("Ran out of room for cname_path\n");
+			return -1;
+		}
+		cname_path[cnt++] = ',';
+		strcpy(&cname_path[cnt], c->cnames[i]);
+		cnt += n;
+	}
+
+	return 0;
+}
+
+int prepare_cgroup_properties(void)
+{
+	char cname_path[PATH_MAX];
+	unsigned int i;
+
+	for (i = 0; i < n_controllers; i++) {
+		CgControllerEntry *c = controllers[i];
+
+		if (c->n_cnames < 1) {
+			pr_err("Each CgControllerEntry should have at least 1 cname\n");
+			return -1;
+		}
+
+		if (get_controller_path(cname_path, PATH_MAX, c) < 0)
+			return -1;
+
+		if (prepare_cgroup_dir_properties(cname_path, c->dirs, c->n_dirs) < 0)
+			return -1;
+	}
+
+	return 0;
+}
+
 static int prepare_cgroup_dirs(char *paux, size_t off, CgroupDirEntry **ents, size_t n_ents)
 {
 	size_t i;
@@ -669,13 +987,28 @@ static int prepare_cgroup_dirs(char *paux, size_t off, CgroupDirEntry **ents, si
 
 		sprintf(paux + off, "/%s", e->path);
 
-		pr_debug("\t`- %s\n", paux);
-		if (mkdirp(paux)) {
-			pr_perror("Can't make cgroup dir %s", paux);
-			return -1;
+		/*
+		 * Checking to see if file already exists. If not, create it. If
+		 * it does exist, prevent us from overwriting the properties
+		 * later by setting the CgroupDirEntry's properties pointer to NULL
+		 */
+		if (access(paux, F_OK) < 0) {
+			if (errno != ENOENT) {
+				pr_perror("Failed accessing file %s\n", paux);
+				return -1;
+			}
+			if (mkdirp(paux)) {
+				pr_perror("Can't make cgroup dir %s", paux);
+				return -1;
+			}
+			pr_info("Created dir %s\n", paux);
+		} else {
+			e->properties = NULL;
+			pr_info("Determined dir %s already existed\n", paux);
 		}
 
-		prepare_cgroup_dirs(paux, off, e->children, e->n_children);
+		if (prepare_cgroup_dirs(paux, off, e->children, e->n_children) < 0)
+			return -1;
 	}
 
 	return 0;
diff --git a/cr-restore.c b/cr-restore.c
index c2515bf..5e633f8 100644
--- a/cr-restore.c
+++ b/cr-restore.c
@@ -1714,6 +1714,9 @@ int cr_restore_tasks(void)
 
 	ret = restore_root_task(root_item);
 
+	if (prepare_cgroup_properties() < 0)
+		goto err;
+
 	fini_cgroup();
 err:
 	cr_plugin_fini();
diff --git a/include/cgroup.h b/include/cgroup.h
index aac7348..c49fb19 100644
--- a/include/cgroup.h
+++ b/include/cgroup.h
@@ -7,16 +7,27 @@ int dump_task_cgroup(struct pstree_item *, u32 *);
 int dump_cgroups(void);
 int prepare_task_cgroup(struct pstree_item *);
 int prepare_cgroup(void);
+/* Restore things like cpu_limit in known cgroups. */
+int prepare_cgroup_properties(void);
 void fini_cgroup(void);
 
 struct cg_controller;
 
+struct cgroup_prop {
+	char			*name;
+	char			*value;
+	struct list_head	list;
+};
+
 /* This describes a particular cgroup path, e.g. the '/lxc/u1' part of
  * 'blkio/lxc/u1' and any properties it has.
  */
 struct cgroup_dir {
 	char			*path;
 
+	struct list_head	properties;
+	unsigned int		n_properties;
+
 	/* this is how children are linked together */
 	struct list_head	siblings;
 
diff --git a/protobuf/cgroup.proto b/protobuf/cgroup.proto
index 6ec003c..fcd5834 100644
--- a/protobuf/cgroup.proto
+++ b/protobuf/cgroup.proto
@@ -1,6 +1,12 @@
+message cgroup_prop_entry {
+	required string			name		= 1;
+	required string			value		= 2;
+}
+
 message cgroup_dir_entry {
 	required string 		path		= 1;
-	repeated cgroup_dir_entry	children 	= 4;
+	repeated cgroup_dir_entry	children 	= 2;
+	repeated cgroup_prop_entry	properties	= 3;
 }
 
 message cg_controller_entry {
-- 
2.0.0.526.g5318336



More information about the CRIU mailing list