2
0
mirror of https://github.com/checkpoint-restore/criu synced 2025-08-22 01:51:51 +00:00

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>
This commit is contained in:
Bui Quang Minh 2022-09-04 15:31:13 +07:00 committed by Andrei Vagin
parent ad3936e81e
commit 17d1d8810e
10 changed files with 98 additions and 20 deletions

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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__ */

View File

@ -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");

View File

@ -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");

View File

@ -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;

View File

@ -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 {

View File

@ -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 {