[CRIU] [PATCH 1/2] cgroups: Add ability to reuse precreated controllers and cgroups

Cyrill Gorcunov gorcunov at openvz.org
Thu May 21 12:33:55 PDT 2015


When been playing wich checkpoint/restore of container I found
that we can't reuse existing controller if they were pre-created.
For example currently in PCS7 we're bindmount cgroups which belong
to a container in a form of

 /sys/fs/cgroup/<controller>/<container> ==> /sys/fs/cgroup/<controller>

so that CRIU dumps such configuration fine but on restore
it recreates controllers from the scratch which we would
like to bindmount them and ask CRIU to restore subcgroups
and their parameters.

So I extended --manage-cgroups option to take <mode> arguments
which might be

 - strict: default mode -- no change in current behaviour,
   we restore everything from the scratch failing with error
   if some toplevel controller or cgroup already exist

 - opport: opportunistic mode -- if controller or toplevel
   cgroup doesn't exist we create it, otherwise simply continue
   execution. iow here we try to continue as long as we can.

 - bind: bindmount mode -- implies that controller and toplevel
   cgroup must already exist.

The last mode allows me to premount cgroups for container externally
and restore its limits from CRIU.

Signed-off-by: Cyrill Gorcunov <gorcunov at openvz.org>
---
 Documentation/criu.txt | 14 ++++++++++++-
 cgroup.c               | 53 +++++++++++++++++++++++++++++++++++++++++---------
 crtools.c              | 41 ++++++++++++++++++++++++++++++++++----
 include/cr_options.h   | 11 ++++++++++-
 4 files changed, 104 insertions(+), 15 deletions(-)

diff --git a/Documentation/criu.txt b/Documentation/criu.txt
index 81783635af89..c307abf7605f 100644
--- a/Documentation/criu.txt
+++ b/Documentation/criu.txt
@@ -211,8 +211,20 @@ Restores previously checkpointed processes.
 *-r*, *--root* '<path>'::
     Change the root filesystem to <path> (when run in mount namespace).
 
-*--manage-cgroups*::
+*--manage-cgroups* [<mode>,<mode>]::
     Restore cgroups configuration associated with a task from the image.
+    '<mode>' may be a combination of keywords from below.
+
+    - *strict*. Require all cgroups to be recreated from the scratch. If
+                any cgroup or subcgroup exist it cause restore procedure
+                to abort. This is default mode.
+
+    - *opport*. Opportunistic mode where *criu* tries to restore all the
+                cgroups but does not fail if some cgroup already exist.
+
+    - *bind*.   Bind mode where all toplevel cgroups should be premounted
+                and *criu* only tries to restore subcgroups and their
+                parameters.
 
 *--cgroup-root* '[<controller>:]/<newroot>'::
     Change the root cgroup the controller will be installed into. No controller
diff --git a/cgroup.c b/cgroup.c
index b7fef38dd334..d0d693f50605 100644
--- a/cgroup.c
+++ b/cgroup.c
@@ -1103,6 +1103,49 @@ static int prepare_cgroup_dirs(char **controllers, int n_controllers, char *paux
 	return 0;
 }
 
+static int prepare_toplevel(char *dir, char *mopt)
+{
+	/*
+	 * For bind mode simply check that directory
+	 * is here, it's enough.
+	 */
+	if (opts.manage_cgroups & CG_MODE_BIND) {
+		if (access(dir, F_OK) == 0) {
+			pr_err("\tHit existing cgyard dir %s\n", dir);
+			return -1;
+		} else if (errno != ENOENT) {
+			pr_perror("\tFiled accessing dir %s\n", dir);
+			return -1;
+		}
+		return 0;
+	}
+
+	/*
+	 * It's either oppotunistic either strict mode so
+	 * create and mount.
+	 */
+	pr_debug("\tMaking cgyard dir %s (%s)\n", dir, mopt);
+	if (mkdir(dir, 0700)) {
+		if (errno == EEXIST) {
+			/*
+			 * For opportunistic mode simply imply
+			 * that cgroup is already mounted.
+			 */
+			if (opts.manage_cgroups & CG_MODE_OPPORT)
+				return 0;
+		}
+		pr_perror("Can't make cgyard dir %s", dir);
+		return -1;
+	}
+
+	if (mount("none", dir, "cgroup", 0, mopt) < 0) {
+		pr_perror("Can't mount cgyard dir %s", dir);
+		return -1;
+	}
+
+	return 0;
+}
+
 /*
  * Prepare the CGROUP_YARD service descriptor. This guy is
  * tmpfs mount with the set of ctl->name directories each
@@ -1179,16 +1222,8 @@ static int prepare_cgroup_sfd(CgroupEntry *ce)
 				paux + ctl_off, sizeof(paux) - ctl_off,
 				opt, sizeof(opt));
 
-		pr_debug("\tMaking subdir %s (%s)\n", paux, opt);
-		if (mkdir(paux, 0700)) {
-			pr_perror("Can't make cgyard subdir %s", paux);
-			goto err;
-		}
-
-		if (mount("none", paux, "cgroup", 0, opt) < 0) {
-			pr_perror("Can't mount %s cgyard", paux);
+		if (prepare_toplevel(paux, opt))
 			goto err;
-		}
 
 		/* We skip over the .criu.cgyard.XXXXXX/, since those will be
 		 * referred to by the cg yard service fd. */
diff --git a/crtools.c b/crtools.c
index 6da7afeed067..d8993be28cd0 100644
--- a/crtools.c
+++ b/crtools.c
@@ -57,7 +57,7 @@ void init_opts(void)
 	INIT_LIST_HEAD(&opts.new_cgroup_roots);
 
 	opts.cpu_cap = CPU_CAP_DEFAULT;
-	opts.manage_cgroups = false;
+	opts.manage_cgroups = CG_MODE_DEAULT;
 	opts.ps_socket = -1;
 }
 
@@ -147,6 +147,37 @@ Esyntax:
 	return -1;
 }
 
+static int parse_manage_cgroups(struct cr_options *opts, const char *optarg)
+{
+	if (!optarg) {
+		opts->manage_cgroups = CG_MODE_DEAULT;
+		return 0;
+	}
+
+	while (*optarg) {
+		if (optarg[0] == ',') {
+			optarg++;
+			continue;
+		} else if (!strncmp(optarg, "opport", 6)) {
+			opts->manage_cgroups |= CG_MODE_OPPORT;
+			optarg += 3;
+		} else if (!strncmp(optarg, "bind", 4)) {
+			opts->manage_cgroups |= CG_MODE_BIND;
+			optarg += 4;
+		} else if (!strncmp(optarg, "strict", 6)) {
+			opts->manage_cgroups |= CG_MODE_STRICT;
+			optarg += 6;
+		} else
+			goto Esyntax;
+	}
+
+	return 0;
+
+Esyntax:
+	pr_err("Unknown cgroups mode `%s' selected\n", optarg);
+	return -1;
+}
+
 int main(int argc, char *argv[], char *envp[])
 {
 	pid_t pid = 0, tree_id = 0;
@@ -199,7 +230,7 @@ int main(int argc, char *argv[], char *envp[])
 		{ "force-irmap",		no_argument,		0, 1058	},
 		{ "ext-mount-map",		required_argument,	0, 'M'	},
 		{ "exec-cmd",			no_argument,		0, 1059	},
-		{ "manage-cgroups",		no_argument,		0, 1060	},
+		{ "manage-cgroups",		optional_argument,	0, 1060	},
 		{ "cgroup-root",		required_argument,	0, 1061	},
 		{ "inherit-fd",			required_argument,	0, 1062	},
 		{ "feature",			required_argument,	0, 1063	},
@@ -392,7 +423,8 @@ int main(int argc, char *argv[], char *envp[])
 			has_exec_cmd = true;
 			break;
 		case 1060:
-			opts.manage_cgroups = true;
+			if (parse_manage_cgroups(&opts, optarg))
+				goto usage;
 			break;
 		case 1061:
 			{
@@ -669,7 +701,8 @@ usage:
 "                        allow autoresolving mounts with external sharing\n"
 "  --enable-external-masters\n"
 "                        allow autoresolving mounts with external masters\n"
-"  --manage-cgroups      dump or restore cgroups the process is in\n"
+"  --manage-cgroups [m]  dump or restore cgroups the process is in usig mode:\n"
+"                        'opport','strict' (default) or 'bind'.\n"
 "  --cgroup-root [controller:]/newroot\n"
 "                        change the root cgroup the controller will be\n"
 "                        installed into. No controller means that root is the\n"
diff --git a/include/cr_options.h b/include/cr_options.h
index 28bed45b0cd8..0843187d34b2 100644
--- a/include/cr_options.h
+++ b/include/cr_options.h
@@ -21,6 +21,15 @@ struct cg_root_opt {
 	char *newroot;
 };
 
+/*
+ * Cgroup management options.
+ */
+#define CG_MODE_OPPORT		(1u << 0)	/* Restore everything ignoring if cgroup exists */
+#define CG_MODE_STRICT		(1u << 1)	/* It must be clean cgroups, recreate from scratch */
+#define CG_MODE_BIND		(1u << 2)	/* Toplevel are bound, everything else to restore */
+
+#define CG_MODE_DEAULT		(CG_MODE_STRICT)
+
 struct cr_options {
 	int			final_state;
 	char			*show_dump_file;
@@ -59,7 +68,7 @@ struct cr_options {
 	unsigned int		cpu_cap;
 	bool			force_irmap;
 	char			**exec_cmd;
-	bool			manage_cgroups;
+	unsigned int		manage_cgroups;
 	char			*new_global_cg_root;
 	struct list_head	new_cgroup_roots;
 	bool			autodetect_ext_mounts;
-- 
2.1.0



More information about the CRIU mailing list