mirror of
https://github.com/checkpoint-restore/criu.git
synced 2026-08-03 23:33:01 +00:00
zdtm: Check threads are restored into correct threaded controllers
This test creates a process with 2 threads in different threaded controllers and check if CRIU restores these threads' cgroup controllers properly. Signed-off-by: Bui Quang Minh <minhquangbui99@gmail.com>
This commit is contained in:
parent
20ea8a0647
commit
07d538cefc
5 changed files with 219 additions and 0 deletions
|
|
@ -387,6 +387,7 @@ TST_DIR = \
|
|||
cgroup03 \
|
||||
cgroup04 \
|
||||
cgroupv2_00 \
|
||||
cgroupv2_01 \
|
||||
cgroup_ifpriomap \
|
||||
cgroup_ignore \
|
||||
cgroup_stray \
|
||||
|
|
@ -679,6 +680,8 @@ sk-unix-listen02: CFLAGS += -DSK_UNIX_LISTEN02
|
|||
sk-unix-listen03: CFLAGS += -DSK_UNIX_LISTEN03
|
||||
sk-unix-listen04: CFLAGS += -DSK_UNIX_LISTEN02 -DSK_UNIX_LISTEN03
|
||||
|
||||
cgroupv2_01: LDLIBS += -pthread
|
||||
|
||||
$(LIB): force
|
||||
$(Q) $(MAKE) -C $(LIBDIR)
|
||||
|
||||
|
|
|
|||
180
test/zdtm/static/cgroupv2_01.c
Normal file
180
test/zdtm/static/cgroupv2_01.c
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <pthread.h>
|
||||
#include <syscall.h>
|
||||
|
||||
#include "zdtmtst.h"
|
||||
|
||||
const char *test_doc = "Check that cgroup-v2 threaded controllers";
|
||||
const char *test_author = "Bui Quang Minh <minhquangbui99@gmail.com>";
|
||||
|
||||
char *dirname;
|
||||
TEST_OPTION(dirname, string, "cgroup-v2 directory name", 1);
|
||||
const char *cgname = "subcg01";
|
||||
|
||||
task_waiter_t t;
|
||||
|
||||
#define gettid(code) syscall(__NR_gettid)
|
||||
|
||||
void cleanup(void)
|
||||
{
|
||||
char path[1024];
|
||||
|
||||
sprintf(path, "%s/%s/%s", dirname, cgname, "thread2");
|
||||
rmdir(path);
|
||||
sprintf(path, "%s/%s/%s", dirname, cgname, "thread1");
|
||||
rmdir(path);
|
||||
sprintf(path, "%s/%s", dirname, cgname);
|
||||
rmdir(path);
|
||||
sprintf(path, "%s", dirname);
|
||||
umount(path);
|
||||
}
|
||||
|
||||
int is_in_cgroup(char *cgname)
|
||||
{
|
||||
FILE *cgf;
|
||||
char buffer[1024];
|
||||
|
||||
sprintf(buffer, "/proc/self/task/%ld/cgroup", gettid());
|
||||
cgf = fopen(buffer, "r");
|
||||
if (cgf == NULL) {
|
||||
pr_err("Fail to open thread's cgroup procfs\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (fgets(buffer, sizeof(buffer), cgf)) {
|
||||
if (strstr(buffer, cgname)) {
|
||||
fclose(cgf);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(cgf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *thread_func(void *arg)
|
||||
{
|
||||
char path[1024], aux[1024];
|
||||
|
||||
sprintf(path, "%s/%s/%s/%s", dirname, cgname, "thread2", "cgroup.threads");
|
||||
sprintf(aux, "%ld", gettid());
|
||||
if (write_value(path, aux)) {
|
||||
cleanup();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
read_value(path, aux, sizeof(aux));
|
||||
|
||||
task_waiter_complete(&t, 1);
|
||||
|
||||
/* Wait for restore */
|
||||
task_waiter_wait4(&t, 2);
|
||||
|
||||
sprintf(path, "/%s/%s", cgname, "thread2");
|
||||
if (!is_in_cgroup(path)) {
|
||||
fail("Thread2's cgroup is not restored");
|
||||
cleanup();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char path[1024], aux[1024];
|
||||
pthread_t thread2;
|
||||
int ret = 1;
|
||||
|
||||
test_init(argc, argv);
|
||||
task_waiter_init(&t);
|
||||
|
||||
if (mkdir(dirname, 0700) < 0 && errno != EEXIST) {
|
||||
pr_perror("Can't make dir");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mount("cgroup2", dirname, "cgroup2", 0, NULL)) {
|
||||
pr_perror("Can't mount cgroup-v2");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sprintf(path, "%s/%s", dirname, cgname);
|
||||
if (mkdir(path, 0700) < 0 && errno != EEXIST) {
|
||||
pr_perror("Can't make dir");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Make cpuset controllers available in children directory */
|
||||
sprintf(path, "%s/%s", dirname, "cgroup.subtree_control");
|
||||
sprintf(aux, "%s", "+cpuset");
|
||||
if (write_value(path, aux))
|
||||
goto out;
|
||||
|
||||
sprintf(path, "%s/%s/%s", dirname, cgname, "cgroup.subtree_control");
|
||||
sprintf(aux, "%s", "+cpuset");
|
||||
if (write_value(path, aux))
|
||||
goto out;
|
||||
|
||||
sprintf(path, "%s/%s/%s", dirname, cgname, "cgroup.procs");
|
||||
sprintf(aux, "%d", getpid());
|
||||
if (write_value(path, aux))
|
||||
goto out;
|
||||
|
||||
sprintf(path, "%s/%s/%s", dirname, cgname, "thread1");
|
||||
if (mkdir(path, 0700) < 0 && errno != EEXIST) {
|
||||
pr_perror("Can't make dir");
|
||||
goto out;
|
||||
}
|
||||
|
||||
sprintf(path, "%s/%s/%s/%s", dirname, cgname, "thread1", "cgroup.type");
|
||||
sprintf(aux, "%s", "threaded");
|
||||
if (write_value(path, aux))
|
||||
goto out;
|
||||
|
||||
sprintf(path, "%s/%s/%s", dirname, cgname, "thread2");
|
||||
if (mkdir(path, 0700) < 0 && errno != EEXIST) {
|
||||
pr_perror("Can't make dir");
|
||||
goto out;
|
||||
}
|
||||
|
||||
sprintf(path, "%s/%s/%s/%s", dirname, cgname, "thread2", "cgroup.type");
|
||||
sprintf(aux, "%s", "threaded");
|
||||
if (write_value(path, aux))
|
||||
goto out;
|
||||
|
||||
ret = pthread_create(&thread2, NULL, thread_func, NULL);
|
||||
if (ret < 0) {
|
||||
pr_err("pthread_create %s\n", strerror(ret));
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
sprintf(path, "%s/%s/%s/%s", dirname, cgname, "thread1", "cgroup.threads");
|
||||
sprintf(aux, "%ld", gettid());
|
||||
if (write_value(path, aux))
|
||||
goto out;
|
||||
|
||||
task_waiter_wait4(&t, 1);
|
||||
|
||||
test_daemon();
|
||||
test_waitsig();
|
||||
|
||||
task_waiter_complete(&t, 2);
|
||||
|
||||
sprintf(path, "/%s/%s", cgname, "thread1");
|
||||
if (!is_in_cgroup(path)) {
|
||||
fail("Main thread's cgroup is not restored");
|
||||
cleanup();
|
||||
exit(1);
|
||||
}
|
||||
pthread_join(thread2, NULL);
|
||||
pass();
|
||||
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
cleanup();
|
||||
return ret;
|
||||
}
|
||||
11
test/zdtm/static/cgroupv2_01.checkskip
Executable file
11
test/zdtm/static/cgroupv2_01.checkskip
Executable file
|
|
@ -0,0 +1,11 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [ -f /sys/fs/cgroup/cgroup.controllers ]; then
|
||||
grep -q "cpuset" /sys/fs/cgroup/cgroup.controllers && exit 0
|
||||
fi
|
||||
|
||||
if [ -d /sys/fs/cgroup/unified ]; then
|
||||
grep -q "cpuset" /sys/fs/cgroup/unified/cgroup.controllers && exit 0
|
||||
fi
|
||||
|
||||
exit 1
|
||||
1
test/zdtm/static/cgroupv2_01.desc
Normal file
1
test/zdtm/static/cgroupv2_01.desc
Normal file
|
|
@ -0,0 +1 @@
|
|||
{'flavor': 'h ns', 'flags': 'suid', 'opts': '--manage-cgroups=full'}
|
||||
24
test/zdtm/static/cgroupv2_01.hook
Executable file
24
test/zdtm/static/cgroupv2_01.hook
Executable file
|
|
@ -0,0 +1,24 @@
|
|||
#!/bin/bash
|
||||
|
||||
[ "$1" == "--clean" -o "$1" == "--pre-restore" ] || exit 0
|
||||
|
||||
set -e
|
||||
cgname="subcg01"
|
||||
tname=$(mktemp -d cgclean.XXXXXX)
|
||||
mount -t cgroup2 cgroup2 $tname
|
||||
|
||||
echo "Cleaning $tname"
|
||||
|
||||
set +e
|
||||
rmdir "$tname/$cgname/thread1"
|
||||
|
||||
# When the test finishes, the cleanup() function removes this directory
|
||||
# successfully because the thread in this controller exit and no other
|
||||
# threads belong to this controller
|
||||
if [ "$1" == "--pre-restore" ]; then
|
||||
rmdir "$tname/$cgname/thread2"
|
||||
fi
|
||||
|
||||
rmdir "$tname/$cgname"
|
||||
umount "$tname"
|
||||
rmdir "$tname"
|
||||
Loading…
Add table
Add a link
Reference in a new issue