Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/checkpoint-restore/criu.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBui Quang Minh <minhquangbui99@gmail.com>2022-09-04 11:31:13 +0300
committerAndrei Vagin <avagin@gmail.com>2022-11-02 07:35:04 +0300
commitc3a519272881bc36243d142427b8d9f2ddc46f82 (patch)
tree3c1d7bddb2ab50f876da05ada7e6db13241062fc
parenta8328c72a01ac825cebc974f2beb7d9e06cfdebb (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.c38
-rw-r--r--criu/cr-dump.c53
-rw-r--r--criu/image.c2
-rw-r--r--criu/include/cgroup.h8
-rw-r--r--criu/include/parasite.h7
-rw-r--r--criu/parasite-syscall.c1
-rw-r--r--criu/pie/parasite.c2
-rw-r--r--criu/proc_parse.c5
-rw-r--r--images/cgroup.proto1
-rw-r--r--images/core.proto1
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 {