diff options
author | Bui Quang Minh <minhquangbui99@gmail.com> | 2022-09-04 11:31:13 +0300 |
---|---|---|
committer | Andrei Vagin <avagin@gmail.com> | 2022-11-02 07:35:04 +0300 |
commit | c3a519272881bc36243d142427b8d9f2ddc46f82 (patch) | |
tree | 3c1d7bddb2ab50f876da05ada7e6db13241062fc | |
parent | a8328c72a01ac825cebc974f2beb7d9e06cfdebb (diff) |
cgroup-v2: Dump cgroup controllers of every threads in a process
Currently, we assume all threads in process are in the same cgroup controllers.
However, with threaded controllers, threads in a process may be in different
controllers. So we need to dump cgroup controllers of every threads in process
and fixup the procfs cgroup parsing to parse from self/task/<tid>/cgroup.
Signed-off-by: Bui Quang Minh <minhquangbui99@gmail.com>
-rw-r--r-- | criu/cgroup.c | 38 | ||||
-rw-r--r-- | criu/cr-dump.c | 53 | ||||
-rw-r--r-- | criu/image.c | 2 | ||||
-rw-r--r-- | criu/include/cgroup.h | 8 | ||||
-rw-r--r-- | criu/include/parasite.h | 7 | ||||
-rw-r--r-- | criu/parasite-syscall.c | 1 | ||||
-rw-r--r-- | criu/pie/parasite.c | 2 | ||||
-rw-r--r-- | criu/proc_parse.c | 5 | ||||
-rw-r--r-- | images/cgroup.proto | 1 | ||||
-rw-r--r-- | images/core.proto | 1 |
10 files changed, 98 insertions, 20 deletions
diff --git a/criu/cgroup.c b/criu/cgroup.c index 4f68836be..b238b6402 100644 --- a/criu/cgroup.c +++ b/criu/cgroup.c @@ -174,6 +174,7 @@ struct cg_controller *new_controller(const char *name) nc->n_controllers = 1; nc->n_heads = 0; + nc->is_threaded = false; INIT_LIST_HEAD(&nc->heads); return nc; @@ -371,7 +372,8 @@ static void free_all_cgroup_props(struct cgroup_dir *ncd) ncd->n_properties = 0; } -static int dump_cg_props_array(const char *fpath, struct cgroup_dir *ncd, const cgp_t *cgp) +static int dump_cg_props_array(const char *fpath, struct cgroup_dir *ncd, const cgp_t *cgp, + struct cg_controller *controller) { int j; char buf[PATH_MAX]; @@ -422,6 +424,13 @@ static int dump_cg_props_array(const char *fpath, struct cgroup_dir *ncd, const prop->value = new; } + /* + * Set the is_threaded flag if cgroup.type's value is threaded, + * ignore all other values. + */ + if (!strcmp("cgroup.type", prop->name) && !strcmp("threaded", prop->value)) + controller->is_threaded = true; + pr_info("Dumping value %s from %s/%s\n", prop->value, fpath, prop->name); list_add_tail(&prop->list, &ncd->properties); ncd->n_properties++; @@ -437,7 +446,7 @@ static int add_cgroup_properties(const char *fpath, struct cgroup_dir *ncd, stru for (i = 0; i < controller->n_controllers; ++i) { const cgp_t *cgp = cgp_get_props(controller->controllers[i]); - if (dump_cg_props_array(fpath, ncd, cgp) < 0) { + if (dump_cg_props_array(fpath, ncd, cgp, controller) < 0) { pr_err("dumping known properties failed\n"); return -1; } @@ -445,12 +454,12 @@ static int add_cgroup_properties(const char *fpath, struct cgroup_dir *ncd, stru /* cgroup v2 */ if (controller->controllers[0][0] == 0) { - if (dump_cg_props_array(fpath, ncd, &cgp_global_v2) < 0) { + if (dump_cg_props_array(fpath, ncd, &cgp_global_v2, controller) < 0) { pr_err("dumping global properties v2 failed\n"); return -1; } } else { - if (dump_cg_props_array(fpath, ncd, &cgp_global) < 0) { + if (dump_cg_props_array(fpath, ncd, &cgp_global, controller) < 0) { pr_err("dumping global properties failed\n"); return -1; } @@ -735,9 +744,9 @@ static int collect_cgroups(struct list_head *ctls) return 0; } -int dump_task_cgroup(struct pstree_item *item, u32 *cg_id, struct parasite_dump_cgroup_args *args) +int dump_thread_cgroup(const struct pstree_item *item, u32 *cg_id, struct parasite_dump_cgroup_args *args, int id) { - int pid; + int pid, tid; LIST_HEAD(ctls); unsigned int n_ctls = 0; struct cg_set *cs; @@ -750,8 +759,13 @@ int dump_task_cgroup(struct pstree_item *item, u32 *cg_id, struct parasite_dump_ else pid = getpid(); - pr_info("Dumping cgroups for %d\n", pid); - if (parse_task_cgroup(pid, args, &ctls, &n_ctls)) + if (id < 0) + tid = pid; + else + tid = item->threads[id].real; + + pr_info("Dumping cgroups for thread %d\n", tid); + if (parse_thread_cgroup(pid, tid, args, &ctls, &n_ctls)) return -1; cs = get_cg_set(&ctls, n_ctls, item); @@ -764,9 +778,10 @@ int dump_task_cgroup(struct pstree_item *item, u32 *cg_id, struct parasite_dump_ pr_info("Set %d is criu one\n", cs->id); } else { if (item == root_item) { - BUG_ON(root_cgset); - root_cgset = cs; - pr_info("Set %d is root one\n", cs->id); + if (!root_cgset) { + root_cgset = cs; + pr_info("Set %d is root one\n", cs->id); + } } else { struct cg_ctl *root, *stray; @@ -913,6 +928,7 @@ static int dump_controllers(CgroupEntry *cg) list_for_each_entry(cur, &cgroups, l) { cg_controller_entry__init(ce); + ce->is_threaded = cur->is_threaded; ce->cnames = cur->controllers; ce->n_cnames = cur->n_controllers; ce->n_dirs = cur->n_heads; diff --git a/criu/cr-dump.c b/criu/cr-dump.c index 210f66232..e31b2f702 100644 --- a/criu/cr-dump.c +++ b/criu/cr-dump.c @@ -759,6 +759,7 @@ static int dump_task_core_all(struct parasite_ctl *ctl, struct pstree_item *item pid_t pid = item->pid->real; int ret = -1; struct parasite_dump_cgroup_args cgroup_args, *info = NULL; + u32 *cg_set; BUILD_BUG_ON(sizeof(cgroup_args) < PARASITE_ARG_SIZE_MIN); @@ -804,13 +805,23 @@ static int dump_task_core_all(struct parasite_ctl *ctl, struct pstree_item *item */ if (item->ids->has_cgroup_ns_id && !item->parent) { info = &cgroup_args; + strcpy(cgroup_args.thread_cgrp, "self/cgroup"); ret = parasite_dump_cgroup(ctl, &cgroup_args); if (ret) goto err; } - core->tc->has_cg_set = true; - ret = dump_task_cgroup(item, &core->tc->cg_set, info); + /* + * We don't support multithreads zombie tasks so there is + * no thread_core in zombie tasks, store the cg_set in + * task_core in these cases. + */ + cg_set = &core->thread_core->cg_set; + if (item->pid->state == TASK_THREAD) { + core->tc->has_cg_set = true; + cg_set = &core->tc->cg_set; + } + ret = dump_thread_cgroup(item, cg_set, info, -1); if (ret) goto err; @@ -1409,6 +1420,38 @@ err: return ret; } +static int dump_task_cgroup(struct parasite_ctl *parasite_ctl, const struct pstree_item *item) +{ + struct parasite_dump_cgroup_args cgroup_args, *info; + int i; + + BUILD_BUG_ON(sizeof(cgroup_args) < PARASITE_ARG_SIZE_MIN); + for (i = 0; i < item->nr_threads; i++) { + CoreEntry *core = item->core[i]; + + /* Leader is already dumped */ + if (item->pid->real == item->threads[i].real) + continue; + + /* For now, we only need to dump the root task's cgroup ns, because we + * know all the tasks are in the same cgroup namespace because we don't + * allow nesting. + */ + info = NULL; + if (item->ids->has_cgroup_ns_id && !item->parent) { + info = &cgroup_args; + sprintf(cgroup_args.thread_cgrp, "self/task/%d/cgroup", item->threads[i].ns[0].virt); + if (parasite_dump_cgroup(parasite_ctl, &cgroup_args)) + return -1; + } + + if (dump_thread_cgroup(item, &core->thread_core->cg_set, info, i)) + return -1; + } + + return 0; +} + static int pre_dump_one_task(struct pstree_item *item, InventoryEntry *parent_ie) { pid_t pid = item->pid->real; @@ -1681,6 +1724,12 @@ static int dump_one_task(struct pstree_item *item, InventoryEntry *parent_ie) goto err_cure; } + ret = dump_task_cgroup(parasite_ctl, item); + if (ret) { + pr_err("Dump cgroup of threads in process (pid: %d) failed with %d\n", pid, ret); + goto err_cure; + } + ret = compel_stop_daemon(parasite_ctl); if (ret) { pr_err("Can't stop daemon in parasite (pid: %d)\n", pid); diff --git a/criu/image.c b/criu/image.c index 3c2127ac6..9fb390ab7 100644 --- a/criu/image.c +++ b/criu/image.c @@ -228,7 +228,7 @@ int prepare_inventory(InventoryEntry *he) if (!opts.unprivileged) he->has_root_cg_set = true; - if (dump_task_cgroup(NULL, &he->root_cg_set, NULL)) + if (dump_thread_cgroup(NULL, &he->root_cg_set, NULL, -1)) return -1; he->root_ids = crt.i.ids; diff --git a/criu/include/cgroup.h b/criu/include/cgroup.h index 2e9b8933c..5a254559d 100644 --- a/criu/include/cgroup.h +++ b/criu/include/cgroup.h @@ -7,7 +7,7 @@ struct pstree_item; struct parasite_dump_cgroup_args; extern u32 root_cg_set; -int dump_task_cgroup(struct pstree_item *, u32 *, struct parasite_dump_cgroup_args *args); +int dump_thread_cgroup(const struct pstree_item *, u32 *, struct parasite_dump_cgroup_args *args, int id); int dump_cgroups(void); int prepare_task_cgroup(struct pstree_item *); int prepare_cgroup(void); @@ -60,6 +60,9 @@ struct cg_controller { /* for cgroup list in cgroup.c */ struct list_head l; + + /* controller is a threaded cgroup or not */ + int is_threaded; }; struct cg_controller *new_controller(const char *name); @@ -87,7 +90,8 @@ struct cg_ctl { */ struct list_head; struct parasite_dump_cgroup_args; -extern int parse_task_cgroup(int pid, struct parasite_dump_cgroup_args *args, struct list_head *l, unsigned int *n); +extern int parse_thread_cgroup(int pid, int tid, struct parasite_dump_cgroup_args *args, struct list_head *l, + unsigned int *n); extern void put_ctls(struct list_head *); int collect_controllers(struct list_head *cgroups, unsigned int *n_cgroups); diff --git a/criu/include/parasite.h b/criu/include/parasite.h index d2a06889f..787c927be 100644 --- a/criu/include/parasite.h +++ b/criu/include/parasite.h @@ -241,7 +241,12 @@ struct parasite_dump_cgroup_args { * * The string is null terminated. */ - char contents[1 << 12]; + char contents[(1 << 12) - 32]; + /* + * Contains the path to thread cgroup procfs. + * "self/task/<tid>/cgroup" + */ + char thread_cgrp[32]; }; #endif /* !__ASSEMBLY__ */ diff --git a/criu/parasite-syscall.c b/criu/parasite-syscall.c index ee4fa86f4..d3541d996 100644 --- a/criu/parasite-syscall.c +++ b/criu/parasite-syscall.c @@ -513,6 +513,7 @@ int parasite_dump_cgroup(struct parasite_ctl *ctl, struct parasite_dump_cgroup_a struct parasite_dump_cgroup_args *ca; ca = compel_parasite_args(ctl, struct parasite_dump_cgroup_args); + memcpy(ca->thread_cgrp, cgroup->thread_cgrp, sizeof(ca->thread_cgrp)); ret = compel_rpc_call_sync(PARASITE_CMD_DUMP_CGROUP, ctl); if (ret) { pr_err("Parasite failed to dump /proc/self/cgroup\n"); diff --git a/criu/pie/parasite.c b/criu/pie/parasite.c index f75fe13bb..2303f41c3 100644 --- a/criu/pie/parasite.c +++ b/criu/pie/parasite.c @@ -745,7 +745,7 @@ static int parasite_dump_cgroup(struct parasite_dump_cgroup_args *args) return -1; } - cgroup = sys_openat(proc, "self/cgroup", O_RDONLY, 0); + cgroup = sys_openat(proc, args->thread_cgrp, O_RDONLY, 0); sys_close(proc); if (cgroup < 0) { pr_err("can't get /proc/self/cgroup fd\n"); diff --git a/criu/proc_parse.c b/criu/proc_parse.c index 946b0fc40..abac5908b 100644 --- a/criu/proc_parse.c +++ b/criu/proc_parse.c @@ -2549,7 +2549,8 @@ err: return -1; } -int parse_task_cgroup(int pid, struct parasite_dump_cgroup_args *args, struct list_head *retl, unsigned int *n) +int parse_thread_cgroup(int pid, int tid, struct parasite_dump_cgroup_args *args, struct list_head *retl, + unsigned int *n) { FILE *f; int ret; @@ -2557,7 +2558,7 @@ int parse_task_cgroup(int pid, struct parasite_dump_cgroup_args *args, struct li unsigned int n_internal = 0; struct cg_ctl *intern, *ext; - f = fopen_proc(pid, "cgroup"); + f = fopen_proc(pid, "task/%d/cgroup", tid); if (!f) return -1; diff --git a/images/cgroup.proto b/images/cgroup.proto index ee0354124..5c7d16c6d 100644 --- a/images/cgroup.proto +++ b/images/cgroup.proto @@ -24,6 +24,7 @@ message cgroup_dir_entry { message cg_controller_entry { repeated string cnames = 1; repeated cgroup_dir_entry dirs = 2; + required bool is_threaded = 3; } message cg_member_entry { diff --git a/images/core.proto b/images/core.proto index 345bdca53..1ee32bfda 100644 --- a/images/core.proto +++ b/images/core.proto @@ -105,6 +105,7 @@ message thread_core_entry { optional string comm = 13; optional uint64 blk_sigset_extended = 14; optional rseq_entry rseq_entry = 15; + required uint32 cg_set = 16; } message task_rlimits_entry { |