From ac273431497d56ba7a0ae1af8386f8a345169081 Mon Sep 17 00:00:00 2001 From: Stepan Pieshkin Date: Fri, 2 Feb 2024 07:03:05 +0000 Subject: [PATCH 1/2] cgroup: Add support for restoring a thread in a correct v1 cgroup Currently we have checkpoint/restore support only of cgroup v2 threaded controllers. Threads originating in cgroup v1 environments will be restored to the main thread's cgroup. This change extends the support for a cgroups v1. Signed-off-by: Stepan Pieshkin --- criu/cgroup.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/criu/cgroup.c b/criu/cgroup.c index 67282f269e..6d1f74457d 100644 --- a/criu/cgroup.c +++ b/criu/cgroup.c @@ -427,10 +427,11 @@ static int dump_cg_props_array(const char *fpath, struct cgroup_dir *ncd, const } /* - * Set the is_threaded flag if cgroup.type's value is threaded, - * ignore all other values. + * Set the is_threaded flag if cgroup.type's value is threaded + * or it is a cgroup v1 (it has a 'tasks' property). + * Ignore all other values. */ - if (!strcmp("cgroup.type", prop->name) && !strcmp("threaded", prop->value)) + if ((!strcmp("cgroup.type", prop->name) && !strcmp("threaded", prop->value)) || !strcmp("tasks", prop->name)) controller->is_threaded = true; pr_info("Dumping value %s from %s/%s\n", prop->value, fpath, prop->name); @@ -1922,7 +1923,7 @@ static int prepare_cgroup_sfd(CgroupEntry *ce) if (ctrl->cnames[0][0] == 0) fstype = "cgroup2"; - pr_debug("\tMaking controller dir %s (%s)\n", paux, opt); + pr_debug("\tMaking controller dir %s (%s), type %s\n", paux, opt, fstype); if (mkdir(paux, 0700)) { pr_perror("\tCan't make controller dir %s", paux); return -1; @@ -1985,6 +1986,7 @@ static int cgroupd(int sk) CgMemberEntry *ce = cg_set_entry->ctls[i]; char aux[PATH_MAX]; CgControllerEntry *ctrl = NULL; + const char *format; for (j = 0; j < n_controllers; j++) { CgControllerEntry *cur = controllers[j]; @@ -2008,7 +2010,8 @@ static int cgroupd(int sk) continue; aux_off = ctrl_dir_and_opt(ctrl, aux, sizeof(aux), NULL, 0); - snprintf(aux + aux_off, sizeof(aux) - aux_off, "/%s/cgroup.threads", ce->path); + format = ctrl->cnames[0][0] ? "/%s/tasks" : "/%s/cgroup.threads"; + snprintf(aux + aux_off, sizeof(aux) - aux_off, format, ce->path); /* * Cgroupd runs outside of the namespaces so we don't From 48c95f60ae080beeb4af55bd0881ba749d91e2cd Mon Sep 17 00:00:00 2001 From: Stepan Pieshkin Date: Fri, 2 Feb 2024 07:05:51 +0000 Subject: [PATCH 2/2] zdtm/static: check that cgroup layout of threads is preserved Co-developed-by: Stepan Pieshkin Signed-off-by: Stepan Pieshkin Signed-off-by: Michal Clapinski --- test/zdtm/static/Makefile | 3 + test/zdtm/static/cgroup_threads.c | 155 +++++++++++++++++++++++++++ test/zdtm/static/cgroup_threads.desc | 1 + test/zdtm/static/cgroup_threads.hook | 19 ++++ 4 files changed, 178 insertions(+) create mode 100644 test/zdtm/static/cgroup_threads.c create mode 100644 test/zdtm/static/cgroup_threads.desc create mode 100644 test/zdtm/static/cgroup_threads.hook diff --git a/test/zdtm/static/Makefile b/test/zdtm/static/Makefile index fb856d55b4..548cefac28 100644 --- a/test/zdtm/static/Makefile +++ b/test/zdtm/static/Makefile @@ -402,6 +402,7 @@ TST_DIR = \ cgroup_ignore \ cgroup_stray \ cgroup_yard \ + cgroup_threads \ unlink_fstat04 \ unlink_fstat041 \ mntns_remap \ @@ -684,6 +685,8 @@ s390x_gs_threads: LDFLAGS += -pthread thread_different_uid_gid: LDLIBS += -pthread -lcap +cgroup_threads: LDFLAGS += -pthread + bpf_hash: LDLIBS += -lbpf bpf_array: LDLIBS += -lbpf diff --git a/test/zdtm/static/cgroup_threads.c b/test/zdtm/static/cgroup_threads.c new file mode 100644 index 0000000000..86f925fe58 --- /dev/null +++ b/test/zdtm/static/cgroup_threads.c @@ -0,0 +1,155 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "zdtmtst.h" + +const char *test_doc = "Check that cgroup layout of threads is preserved"; +const char *test_author = "Michał Cłapiński "; + +char *dirname; +TEST_OPTION(dirname, string, "cgroup directory name", 1); +static const char *cgname = "zdtmtst"; +#define SUBNAME "subcg_threads" +#define SUBNAME2 SUBNAME "/subsubcg" + +static int cg_move(char *name) +{ + int cgfd, l; + char paux[256]; + + sprintf(paux, "%s/%s", dirname, name); + mkdir(paux, 0600); + + sprintf(paux, "%s/%s/tasks", dirname, name); + + cgfd = open(paux, O_WRONLY); + if (cgfd < 0) { + pr_perror("Can't open tasks"); + return -1; + } + + l = write(cgfd, "0", 2); + close(cgfd); + + if (l < 0) { + pr_perror("Can't move self to subcg"); + return -1; + } + + return 0; +} + +static int cg_check(char *name) +{ + int found = 0; + FILE *cgf; + char paux[256], aux[128]; + + cgf = fopen("/proc/thread-self/cgroup", "r"); + if (cgf == NULL) + return -1; + + sprintf(aux, "name=%s:/%s", cgname, name); + while (fgets(paux, sizeof(paux), cgf)) { + char *s; + + s = strchr(paux, ':') + 1; + s[strlen(s) - 1] = '\0'; + test_msg("CMP [%s] vs [%s]\n", s, aux); + if (!strcmp(s, aux)) { + found = 1; + break; + } + } + + fclose(cgf); + + return found ? 0 : -1; +} + +int p1[2], pr[2]; + +void *child(void *args) +{ + int status = cg_move(SUBNAME2); + write(p1[1], &status, sizeof(status)); + + if (status == 0) { + read(pr[0], &status, sizeof(status)); + + status = cg_check(SUBNAME2); + write(p1[1], &status, sizeof(status)); + } + + pthread_exit(0); +} + +int main(int argc, char **argv) +{ + char aux[64]; + int status; + pthread_t thread; + + test_init(argc, argv); + + /* + * Pipe to talk to the kid. + * First, it reports that it's ready (int), + * then it reports the restore status (int). + */ + + pipe(p1); + + /* "Restore happened" pipe */ + pipe(pr); + + if (mkdir(dirname, 0700) < 0) { + pr_perror("Can't make dir"); + goto out; + } + + sprintf(aux, "none,name=%s", cgname); + if (mount("none", dirname, "cgroup", 0, aux)) { + pr_perror("Can't mount cgroups"); + goto out_rd; + } + + if (cg_move(SUBNAME)) + goto out_rs; + + pthread_create(&thread, NULL, child, NULL); + + status = -1; + read(p1[0], &status, sizeof(status)); + if (status != 0) { + pr_perror("Error moving into cgroups"); + close(pr[0]); + goto out_rs; + } + + test_daemon(); + test_waitsig(); + + close(pr[1]); + + status = -1; + read(p1[0], &status, sizeof(status)); + if (status != 0) { + fail("child cg changed"); + goto out_rs; + } + + pass(); + +out_rs: + umount(dirname); +out_rd: + rmdir(dirname); +out: + return 0; +} diff --git a/test/zdtm/static/cgroup_threads.desc b/test/zdtm/static/cgroup_threads.desc new file mode 100644 index 0000000000..3c6c4a7e22 --- /dev/null +++ b/test/zdtm/static/cgroup_threads.desc @@ -0,0 +1 @@ +{'flavor': 'h', 'flags': 'suid', 'opts': '--manage-cgroups'} diff --git a/test/zdtm/static/cgroup_threads.hook b/test/zdtm/static/cgroup_threads.hook new file mode 100644 index 0000000000..1e6d31d1b0 --- /dev/null +++ b/test/zdtm/static/cgroup_threads.hook @@ -0,0 +1,19 @@ +#!/bin/bash + +[ "$1" == "--clean" -o "$1" == "--pre-restore" ] || exit 0 + +set -e + +tname=$(mktemp -d cgclean.XXXXXX) +trap 'rmdir "${tname}"' EXIT + +mount -t cgroup none $tname -o "none,name=zdtmtst" +trap 'umount "${tname}"; rmdir "${tname}"' EXIT + +echo "Cleaning $tname" + +rmdir "$tname/subcg_threads/subsubcg/" || true +rmdir "$tname/subcg_threads/" || true + +echo "Left there is:" +ls "$tname"