Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests: cgroup: Add task migration tests

Add two new tests that verify that thread and threadgroup migrations
work as expected.

Signed-off-by: Michal Koutný <mkoutny@suse.com>
Signed-off-by: Tejun Heo <tj@kernel.org>

authored by

Michal Koutný and committed by
Tejun Heo
11318989 58c9f75b

+175 -1
+1 -1
tools/testing/selftests/cgroup/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0 2 - CFLAGS += -Wall 2 + CFLAGS += -Wall -pthread 3 3 4 4 all: 5 5
+26
tools/testing/selftests/cgroup/cgroup_util.c
··· 158 158 return atol(ptr + strlen(key)); 159 159 } 160 160 161 + long cg_read_lc(const char *cgroup, const char *control) 162 + { 163 + char buf[PAGE_SIZE]; 164 + const char delim[] = "\n"; 165 + char *line; 166 + long cnt = 0; 167 + 168 + if (cg_read(cgroup, control, buf, sizeof(buf))) 169 + return -1; 170 + 171 + for (line = strtok(buf, delim); line; line = strtok(NULL, delim)) 172 + cnt++; 173 + 174 + return cnt; 175 + } 176 + 161 177 int cg_write(const char *cgroup, const char *control, char *buf) 162 178 { 163 179 char path[PATH_MAX]; ··· 439 423 snprintf(path, sizeof(path), "/proc/%d/%s", pid, item); 440 424 441 425 return read_text(path, buf, size); 426 + } 427 + 428 + int proc_read_strstr(int pid, bool thread, const char *item, const char *needle) 429 + { 430 + char buf[PAGE_SIZE]; 431 + 432 + if (proc_read_text(pid, thread, item, buf, sizeof(buf)) < 0) 433 + return -1; 434 + 435 + return strstr(buf, needle) ? 0 : -1; 442 436 }
+2
tools/testing/selftests/cgroup/cgroup_util.h
··· 30 30 const char *needle); 31 31 extern long cg_read_long(const char *cgroup, const char *control); 32 32 long cg_read_key_long(const char *cgroup, const char *control, const char *key); 33 + extern long cg_read_lc(const char *cgroup, const char *control); 33 34 extern int cg_write(const char *cgroup, const char *control, char *buf); 34 35 extern int cg_run(const char *cgroup, 35 36 int (*fn)(const char *cgroup, void *arg), ··· 49 48 extern int cg_wait_for_proc_count(const char *cgroup, int count); 50 49 extern int cg_killall(const char *cgroup); 51 50 extern ssize_t proc_read_text(int pid, bool thread, const char *item, char *buf, size_t size); 51 + extern int proc_read_strstr(int pid, bool thread, const char *item, const char *needle);
+146
tools/testing/selftests/cgroup/test_core.c
··· 5 5 #include <unistd.h> 6 6 #include <stdio.h> 7 7 #include <errno.h> 8 + #include <signal.h> 9 + #include <string.h> 10 + #include <pthread.h> 8 11 9 12 #include "../kselftest.h" 10 13 #include "cgroup_util.h" ··· 357 354 return ret; 358 355 } 359 356 357 + static void *dummy_thread_fn(void *arg) 358 + { 359 + return (void *)(size_t)pause(); 360 + } 361 + 362 + /* 363 + * Test threadgroup migration. 364 + * All threads of a process are migrated together. 365 + */ 366 + static int test_cgcore_proc_migration(const char *root) 367 + { 368 + int ret = KSFT_FAIL; 369 + int t, c_threads, n_threads = 13; 370 + char *src = NULL, *dst = NULL; 371 + pthread_t threads[n_threads]; 372 + 373 + src = cg_name(root, "cg_src"); 374 + dst = cg_name(root, "cg_dst"); 375 + if (!src || !dst) 376 + goto cleanup; 377 + 378 + if (cg_create(src)) 379 + goto cleanup; 380 + if (cg_create(dst)) 381 + goto cleanup; 382 + 383 + if (cg_enter_current(src)) 384 + goto cleanup; 385 + 386 + for (c_threads = 0; c_threads < n_threads; ++c_threads) { 387 + if (pthread_create(&threads[c_threads], NULL, dummy_thread_fn, NULL)) 388 + goto cleanup; 389 + } 390 + 391 + cg_enter_current(dst); 392 + if (cg_read_lc(dst, "cgroup.threads") != n_threads + 1) 393 + goto cleanup; 394 + 395 + ret = KSFT_PASS; 396 + 397 + cleanup: 398 + for (t = 0; t < c_threads; ++t) { 399 + pthread_cancel(threads[t]); 400 + } 401 + 402 + for (t = 0; t < c_threads; ++t) { 403 + pthread_join(threads[t], NULL); 404 + } 405 + 406 + cg_enter_current(root); 407 + 408 + if (dst) 409 + cg_destroy(dst); 410 + if (src) 411 + cg_destroy(src); 412 + free(dst); 413 + free(src); 414 + return ret; 415 + } 416 + 417 + static void *migrating_thread_fn(void *arg) 418 + { 419 + int g, i, n_iterations = 1000; 420 + char **grps = arg; 421 + char lines[3][PATH_MAX]; 422 + 423 + for (g = 1; g < 3; ++g) 424 + snprintf(lines[g], sizeof(lines[g]), "0::%s", grps[g] + strlen(grps[0])); 425 + 426 + for (i = 0; i < n_iterations; ++i) { 427 + cg_enter_current_thread(grps[(i % 2) + 1]); 428 + 429 + if (proc_read_strstr(0, 1, "cgroup", lines[(i % 2) + 1])) 430 + return (void *)-1; 431 + } 432 + return NULL; 433 + } 434 + 435 + /* 436 + * Test single thread migration. 437 + * Threaded cgroups allow successful migration of a thread. 438 + */ 439 + static int test_cgcore_thread_migration(const char *root) 440 + { 441 + int ret = KSFT_FAIL; 442 + char *dom = NULL; 443 + char line[PATH_MAX]; 444 + char *grps[3] = { (char *)root, NULL, NULL }; 445 + pthread_t thr; 446 + void *retval; 447 + 448 + dom = cg_name(root, "cg_dom"); 449 + grps[1] = cg_name(root, "cg_dom/cg_src"); 450 + grps[2] = cg_name(root, "cg_dom/cg_dst"); 451 + if (!grps[1] || !grps[2] || !dom) 452 + goto cleanup; 453 + 454 + if (cg_create(dom)) 455 + goto cleanup; 456 + if (cg_create(grps[1])) 457 + goto cleanup; 458 + if (cg_create(grps[2])) 459 + goto cleanup; 460 + 461 + if (cg_write(grps[1], "cgroup.type", "threaded")) 462 + goto cleanup; 463 + if (cg_write(grps[2], "cgroup.type", "threaded")) 464 + goto cleanup; 465 + 466 + if (cg_enter_current(grps[1])) 467 + goto cleanup; 468 + 469 + if (pthread_create(&thr, NULL, migrating_thread_fn, grps)) 470 + goto cleanup; 471 + 472 + if (pthread_join(thr, &retval)) 473 + goto cleanup; 474 + 475 + if (retval) 476 + goto cleanup; 477 + 478 + snprintf(line, sizeof(line), "0::%s", grps[1] + strlen(grps[0])); 479 + if (proc_read_strstr(0, 1, "cgroup", line)) 480 + goto cleanup; 481 + 482 + ret = KSFT_PASS; 483 + 484 + cleanup: 485 + cg_enter_current(root); 486 + if (grps[2]) 487 + cg_destroy(grps[2]); 488 + if (grps[1]) 489 + cg_destroy(grps[1]); 490 + if (dom) 491 + cg_destroy(dom); 492 + free(grps[2]); 493 + free(grps[1]); 494 + free(dom); 495 + return ret; 496 + } 497 + 360 498 #define T(x) { x, #x } 361 499 struct corecg_test { 362 500 int (*fn)(const char *root); ··· 510 366 T(test_cgcore_parent_becomes_threaded), 511 367 T(test_cgcore_invalid_domain), 512 368 T(test_cgcore_populated), 369 + T(test_cgcore_proc_migration), 370 + T(test_cgcore_thread_migration), 513 371 }; 514 372 #undef T 515 373