diff --git a/Documentation/criu.txt b/Documentation/criu.txt index e9fd55b27..0c6a88a41 100644 --- a/Documentation/criu.txt +++ b/Documentation/criu.txt @@ -211,8 +211,25 @@ Restores previously checkpointed processes. *-r*, *--root* '':: Change the root filesystem to (when run in mount namespace). -*--manage-cgroups*:: +*--manage-cgroups* []:: Restore cgroups configuration associated with a task from the image. + Controllers are always restored in optimistic way -- if already present + in system *criu* reuses it, otherwise it will be created. ++ +The '' may be one of below. + + - *none*. Do not restore cgroup properties but require cgroup to + pre-exist at the moment of *restore* procedure. + + - *props*. Restore cgroup properties and require cgroup to pre-exist. + + - *soft*. Restore cgroup properties if only cgroup has been created + by *criu*, otherwise do not restore properies. + + - *full*. Always restore all cgroups and their properties. + + - *strict*. Restore all cgroups and their properties from the scratch, + requiring them to not present in the system. *--cgroup-root* '[:]/':: Change the root cgroup the controller will be installed into. No controller diff --git a/cgroup.c b/cgroup.c index 6541c628f..75a8f8be0 100644 --- a/cgroup.c +++ b/cgroup.c @@ -1053,18 +1053,17 @@ static int prepare_cgroup_dirs(char **controllers, int n_controllers, char *paux for (i = 0; i < n_ents; i++) { size_t off2 = off; e = ents[i]; - struct stat st; off2 += sprintf(paux + off, "/%s", e->dir_name); - /* - * Checking to see if file already exists. If not, create it. If - * it does exist, prevent us from overwriting the properties - * later by removing the CgroupDirEntry's properties. - */ - if (fstatat(cg, paux, &st, 0) < 0) { + if (faccessat(cg, paux, F_OK, 0) < 0) { if (errno != ENOENT) { - pr_perror("Failed accessing file %s", paux); + pr_perror("Failed accessing cgroup dir %s", paux); + return -1; + } + + if (opts.manage_cgroups & (CG_MODE_NONE | CG_MODE_PROPS)) { + pr_err("Cgroup dir %s doesn't exist\n", paux); return -1; } @@ -1072,7 +1071,7 @@ static int prepare_cgroup_dirs(char **controllers, int n_controllers, char *paux pr_perror("Can't make cgroup dir %s", paux); return -1; } - pr_info("Created dir %s\n", paux); + pr_info("Created cgroup dir %s\n", paux); for (j = 0; j < n_controllers; j++) { if (strcmp(controllers[j], "cpuset") == 0) { @@ -1083,12 +1082,21 @@ static int prepare_cgroup_dirs(char **controllers, int n_controllers, char *paux } } } else { - if (e->n_properties > 0) { - xfree(e->properties); - e->properties = NULL; - e->n_properties = 0; + pr_info("Determined cgroup dir %s already exist\n", paux); + + if (opts.manage_cgroups & CG_MODE_STRICT) { + pr_err("Abort restore of existing cgroups\n"); + return -1; + } + + if (opts.manage_cgroups & (CG_MODE_SOFT | CG_MODE_NONE)) { + pr_info("Skip restoring properties on cgroup dir %s\n", paux); + if (e->n_properties > 0) { + xfree(e->properties); + e->properties = NULL; + e->n_properties = 0; + } } - pr_info("Determined dir %s already existed\n", paux); } if (prepare_cgroup_dirs(controllers, n_controllers, paux, off2, @@ -1122,10 +1130,14 @@ static int prepare_cgroup_sfd(CgroupEntry *ce) int off, i, ret; char paux[PATH_MAX]; - pr_info("Preparing cgroups yard\n"); + pr_info("Preparing cgroups yard (cgroups restore mode %#x)\n", + opts.manage_cgroups); + if (!opts.manage_cgroups) + return 0; cg_yard = opts.cg_yard; off = strlen(opts.cg_yard); + strcpy(paux, opts.cg_yard); pr_debug("Opening %s as cg yard\n", cg_yard); i = open(cg_yard, O_DIRECTORY); @@ -1139,7 +1151,6 @@ static int prepare_cgroup_sfd(CgroupEntry *ce) if (ret < 0) goto err; - paux[off++] = '/'; for (i = 0; i < ce->n_controllers; i++) { @@ -1156,26 +1167,28 @@ 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; + /* Create controller if not yet present */ + if (access(paux, F_OK)) { + pr_debug("\tMaking controller dir %s (%s)\n", paux, opt); + if (mkdir(paux, 0700)) { + pr_perror("\tCan't make controller dir %s", paux); + return -1; + } + if (mount("none", paux, "cgroup", 0, opt) < 0) { + pr_perror("\tCan't mount controller dir %s", paux); + return -1; + } } - if (mount("none", paux, "cgroup", 0, opt) < 0) { - pr_perror("Can't mount %s cgyard", paux); - goto err; - } - - /* We skip over the .criu.cgyard.XXXXXX/, since those will be - * referred to by the cg yard service fd. */ + /* + * Finally handle all cgroups for this controller. + */ yard = paux + strlen(cg_yard) + 1; yard_off = ctl_off - (strlen(cg_yard) + 1); if (opts.manage_cgroups && prepare_cgroup_dirs(ctrl->cnames, ctrl->n_cnames, yard, yard_off, ctrl->dirs, ctrl->n_dirs)) goto err; - } return 0; diff --git a/crtools.c b/crtools.c index b44202b44..ef391f9dd 100644 --- a/crtools.c +++ b/crtools.c @@ -58,7 +58,7 @@ void init_opts(void) opts.cg_yard = "/sys/fs/cgroup"; opts.cpu_cap = CPU_CAP_DEFAULT; - opts.manage_cgroups = false; + opts.manage_cgroups = CG_MODE_DEFAULT; opts.ps_socket = -1; } @@ -148,6 +148,33 @@ Esyntax: return -1; } +static int parse_manage_cgroups(struct cr_options *opts, const char *optarg) +{ + if (!optarg) { + opts->manage_cgroups = CG_MODE_SOFT; + return 0; + } + + if (!strcmp(optarg, "none")) { + opts->manage_cgroups = CG_MODE_NONE; + } else if (!strcmp(optarg, "props")) { + opts->manage_cgroups = CG_MODE_PROPS; + } else if (!strcmp(optarg, "soft")) { + opts->manage_cgroups = CG_MODE_SOFT; + } else if (!strcmp(optarg, "full")) { + opts->manage_cgroups = CG_MODE_FULL; + } else if (!strcmp(optarg, "strict")) { + opts->manage_cgroups = CG_MODE_STRICT; + } 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; @@ -200,7 +227,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 }, @@ -394,7 +421,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: { @@ -674,7 +702,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" +" 'none', 'props', 'soft' (default), 'full' and 'strict'.\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 ecd382522..d405ad40c 100644 --- a/include/cr_options.h +++ b/include/cr_options.h @@ -21,6 +21,18 @@ struct cg_root_opt { char *newroot; }; +/* + * Cgroup management options. + */ +#define CG_MODE_IGNORE (0u << 0) /* Zero is important here */ +#define CG_MODE_NONE (1u << 0) +#define CG_MODE_PROPS (1u << 1) +#define CG_MODE_SOFT (1u << 2) +#define CG_MODE_FULL (1u << 3) +#define CG_MODE_STRICT (1u << 4) + +#define CG_MODE_DEFAULT (CG_MODE_IGNORE) + struct cr_options { int final_state; char *show_dump_file; @@ -59,7 +71,7 @@ struct cr_options { unsigned int cpu_cap; bool force_irmap; char **exec_cmd; - bool manage_cgroups; + unsigned int manage_cgroups; char *cg_yard; char *new_global_cg_root; struct list_head new_cgroup_roots;