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:
parent
ad3936e81e
commit
17d1d8810e
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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__ */
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user