FILE
cgroup_util.c
File preview with syntax highlighting.
linux-6.19.14/tools/testing/selftests/cgroup/lib/cgroup_util.c
1/* SPDX-License-Identifier: GPL-2.0 */ 2 3#define _GNU_SOURCE 4 5#include <errno.h> 6#include <fcntl.h> 7#include <linux/limits.h> 8#include <poll.h> 9#include <signal.h> 10#include <stdio.h> 11#include <stdlib.h> 12#include <string.h> 13#include <sys/inotify.h> 14#include <sys/stat.h> 15#include <sys/types.h> 16#include <sys/wait.h> 17#include <unistd.h> 18 19#include "cgroup_util.h" 20#include "../../clone3/clone3_selftests.h" 21 22bool cg_test_v1_named; 23 24/* Returns read len on success, or -errno on failure. */ 25ssize_t read_text(const char *path, char *buf, size_t max_len) 26{ 27 ssize_t len; 28 int fd; 29 30 fd = open(path, O_RDONLY); 31 if (fd < 0) 32 return -errno; 33 34 len = read(fd, buf, max_len - 1); 35 36 if (len >= 0) 37 buf[len] = 0; 38 39 close(fd); 40 return len < 0 ? -errno : len; 41} 42 43/* Returns written len on success, or -errno on failure. */ 44ssize_t write_text(const char *path, char *buf, ssize_t len) 45{ 46 int fd; 47 48 fd = open(path, O_WRONLY | O_APPEND); 49 if (fd < 0) 50 return -errno; 51 52 len = write(fd, buf, len); 53 close(fd); 54 return len < 0 ? -errno : len; 55} 56 57char *cg_name(const char *root, const char *name) 58{ 59 size_t len = strlen(root) + strlen(name) + 2; 60 char *ret = malloc(len); 61 62 snprintf(ret, len, "%s/%s", root, name); 63 64 return ret; 65} 66 67char *cg_name_indexed(const char *root, const char *name, int index) 68{ 69 size_t len = strlen(root) + strlen(name) + 10; 70 char *ret = malloc(len); 71 72 snprintf(ret, len, "%s/%s_%d", root, name, index); 73 74 return ret; 75} 76 77char *cg_control(const char *cgroup, const char *control) 78{ 79 size_t len = strlen(cgroup) + strlen(control) + 2; 80 char *ret = malloc(len); 81 82 snprintf(ret, len, "%s/%s", cgroup, control); 83 84 return ret; 85} 86 87/* Returns 0 on success, or -errno on failure. */ 88int cg_read(const char *cgroup, const char *control, char *buf, size_t len) 89{ 90 char path[PATH_MAX]; 91 ssize_t ret; 92 93 snprintf(path, sizeof(path), "%s/%s", cgroup, control); 94 95 ret = read_text(path, buf, len); 96 return ret >= 0 ? 0 : ret; 97} 98 99int cg_read_strcmp(const char *cgroup, const char *control, 100 const char *expected) 101{ 102 size_t size; 103 char *buf; 104 int ret; 105 106 /* Handle the case of comparing against empty string */ 107 if (!expected) 108 return -1; 109 else 110 size = strlen(expected) + 1; 111 112 buf = malloc(size); 113 if (!buf) 114 return -1; 115 116 if (cg_read(cgroup, control, buf, size)) { 117 free(buf); 118 return -1; 119 } 120 121 ret = strcmp(expected, buf); 122 free(buf); 123 return ret; 124} 125 126int cg_read_strcmp_wait(const char *cgroup, const char *control, 127 const char *expected) 128{ 129 int i, ret; 130 131 for (i = 0; i < 100; i++) { 132 ret = cg_read_strcmp(cgroup, control, expected); 133 if (!ret) 134 return ret; 135 usleep(10000); 136 } 137 138 return ret; 139} 140 141int cg_read_strstr(const char *cgroup, const char *control, const char *needle) 142{ 143 char buf[PAGE_SIZE]; 144 145 if (cg_read(cgroup, control, buf, sizeof(buf))) 146 return -1; 147 148 return strstr(buf, needle) ? 0 : -1; 149} 150 151long cg_read_long(const char *cgroup, const char *control) 152{ 153 char buf[128]; 154 155 if (cg_read(cgroup, control, buf, sizeof(buf))) 156 return -1; 157 158 return atol(buf); 159} 160 161long cg_read_long_fd(int fd) 162{ 163 char buf[128]; 164 165 if (pread(fd, buf, sizeof(buf), 0) <= 0) 166 return -1; 167 168 return atol(buf); 169} 170 171long cg_read_key_long(const char *cgroup, const char *control, const char *key) 172{ 173 char buf[PAGE_SIZE]; 174 char *ptr; 175 176 if (cg_read(cgroup, control, buf, sizeof(buf))) 177 return -1; 178 179 ptr = strstr(buf, key); 180 if (!ptr) 181 return -1; 182 183 return atol(ptr + strlen(key)); 184} 185 186long cg_read_lc(const char *cgroup, const char *control) 187{ 188 char buf[PAGE_SIZE]; 189 const char delim[] = "\n"; 190 char *line; 191 long cnt = 0; 192 193 if (cg_read(cgroup, control, buf, sizeof(buf))) 194 return -1; 195 196 for (line = strtok(buf, delim); line; line = strtok(NULL, delim)) 197 cnt++; 198 199 return cnt; 200} 201 202/* Returns 0 on success, or -errno on failure. */ 203int cg_write(const char *cgroup, const char *control, char *buf) 204{ 205 char path[PATH_MAX]; 206 ssize_t len = strlen(buf), ret; 207 208 snprintf(path, sizeof(path), "%s/%s", cgroup, control); 209 ret = write_text(path, buf, len); 210 return ret == len ? 0 : ret; 211} 212 213/* 214 * Returns fd on success, or -1 on failure. 215 * (fd should be closed with close() as usual) 216 */ 217int cg_open(const char *cgroup, const char *control, int flags) 218{ 219 char path[PATH_MAX]; 220 221 snprintf(path, sizeof(path), "%s/%s", cgroup, control); 222 return open(path, flags); 223} 224 225int cg_write_numeric(const char *cgroup, const char *control, long value) 226{ 227 char buf[64]; 228 int ret; 229 230 ret = sprintf(buf, "%lu", value); 231 if (ret < 0) 232 return ret; 233 234 return cg_write(cgroup, control, buf); 235} 236 237static int cg_find_root(char *root, size_t len, const char *controller, 238 bool *nsdelegate) 239{ 240 char buf[10 * PAGE_SIZE]; 241 char *fs, *mount, *type, *options; 242 const char delim[] = "\n\t "; 243 244 if (read_text("/proc/self/mounts", buf, sizeof(buf)) <= 0) 245 return -1; 246 247 /* 248 * Example: 249 * cgroup /sys/fs/cgroup cgroup2 rw,seclabel,noexec,relatime 0 0 250 */ 251 for (fs = strtok(buf, delim); fs; fs = strtok(NULL, delim)) { 252 mount = strtok(NULL, delim); 253 type = strtok(NULL, delim); 254 options = strtok(NULL, delim); 255 strtok(NULL, delim); 256 strtok(NULL, delim); 257 if (strcmp(type, "cgroup") == 0) { 258 if (!controller || !strstr(options, controller)) 259 continue; 260 } else if (strcmp(type, "cgroup2") == 0) { 261 if (controller && 262 cg_read_strstr(mount, "cgroup.controllers", controller)) 263 continue; 264 } else { 265 continue; 266 } 267 strncpy(root, mount, len); 268 269 if (nsdelegate) 270 *nsdelegate = !!strstr(options, "nsdelegate"); 271 return 0; 272 273 } 274 275 return -1; 276} 277 278int cg_find_controller_root(char *root, size_t len, const char *controller) 279{ 280 return cg_find_root(root, len, controller, NULL); 281} 282 283int cg_find_unified_root(char *root, size_t len, bool *nsdelegate) 284{ 285 return cg_find_root(root, len, NULL, nsdelegate); 286} 287 288int cg_create(const char *cgroup) 289{ 290 return mkdir(cgroup, 0755); 291} 292 293int cg_wait_for_proc_count(const char *cgroup, int count) 294{ 295 char buf[10 * PAGE_SIZE] = {0}; 296 int attempts; 297 char *ptr; 298 299 for (attempts = 10; attempts >= 0; attempts--) { 300 int nr = 0; 301 302 if (cg_read(cgroup, "cgroup.procs", buf, sizeof(buf))) 303 break; 304 305 for (ptr = buf; *ptr; ptr++) 306 if (*ptr == '\n') 307 nr++; 308 309 if (nr >= count) 310 return 0; 311 312 usleep(100000); 313 } 314 315 return -1; 316} 317 318int cg_killall(const char *cgroup) 319{ 320 char buf[PAGE_SIZE]; 321 char *ptr = buf; 322 323 /* If cgroup.kill exists use it. */ 324 if (!cg_write(cgroup, "cgroup.kill", "1")) 325 return 0; 326 327 if (cg_read(cgroup, "cgroup.procs", buf, sizeof(buf))) 328 return -1; 329 330 while (ptr < buf + sizeof(buf)) { 331 int pid = strtol(ptr, &ptr, 10); 332 333 if (pid == 0) 334 break; 335 if (*ptr) 336 ptr++; 337 else 338 break; 339 if (kill(pid, SIGKILL)) 340 return -1; 341 } 342 343 return 0; 344} 345 346int cg_destroy(const char *cgroup) 347{ 348 int ret; 349 350 if (!cgroup) 351 return 0; 352retry: 353 ret = rmdir(cgroup); 354 if (ret && errno == EBUSY) { 355 cg_killall(cgroup); 356 usleep(100); 357 goto retry; 358 } 359 360 if (ret && errno == ENOENT) 361 ret = 0; 362 363 return ret; 364} 365 366int cg_enter(const char *cgroup, int pid) 367{ 368 char pidbuf[64]; 369 370 snprintf(pidbuf, sizeof(pidbuf), "%d", pid); 371 return cg_write(cgroup, "cgroup.procs", pidbuf); 372} 373 374int cg_enter_current(const char *cgroup) 375{ 376 return cg_write(cgroup, "cgroup.procs", "0"); 377} 378 379int cg_enter_current_thread(const char *cgroup) 380{ 381 return cg_write(cgroup, CG_THREADS_FILE, "0"); 382} 383 384int cg_run(const char *cgroup, 385 int (*fn)(const char *cgroup, void *arg), 386 void *arg) 387{ 388 int pid, retcode; 389 390 pid = fork(); 391 if (pid < 0) { 392 return pid; 393 } else if (pid == 0) { 394 char buf[64]; 395 396 snprintf(buf, sizeof(buf), "%d", getpid()); 397 if (cg_write(cgroup, "cgroup.procs", buf)) 398 exit(EXIT_FAILURE); 399 exit(fn(cgroup, arg)); 400 } else { 401 waitpid(pid, &retcode, 0); 402 if (WIFEXITED(retcode)) 403 return WEXITSTATUS(retcode); 404 else 405 return -1; 406 } 407} 408 409pid_t clone_into_cgroup(int cgroup_fd) 410{ 411#ifdef CLONE_ARGS_SIZE_VER2 412 pid_t pid; 413 414 struct __clone_args args = { 415 .flags = CLONE_INTO_CGROUP, 416 .exit_signal = SIGCHLD, 417 .cgroup = cgroup_fd, 418 }; 419 420 pid = sys_clone3(&args, sizeof(struct __clone_args)); 421 /* 422 * Verify that this is a genuine test failure: 423 * ENOSYS -> clone3() not available 424 * E2BIG -> CLONE_INTO_CGROUP not available 425 */ 426 if (pid < 0 && (errno == ENOSYS || errno == E2BIG)) 427 goto pretend_enosys; 428 429 return pid; 430 431pretend_enosys: 432#endif 433 errno = ENOSYS; 434 return -ENOSYS; 435} 436 437int clone_reap(pid_t pid, int options) 438{ 439 int ret; 440 siginfo_t info = { 441 .si_signo = 0, 442 }; 443 444again: 445 ret = waitid(P_PID, pid, &info, options | __WALL | __WNOTHREAD); 446 if (ret < 0) { 447 if (errno == EINTR) 448 goto again; 449 return -1; 450 } 451 452 if (options & WEXITED) { 453 if (WIFEXITED(info.si_status)) 454 return WEXITSTATUS(info.si_status); 455 } 456 457 if (options & WSTOPPED) { 458 if (WIFSTOPPED(info.si_status)) 459 return WSTOPSIG(info.si_status); 460 } 461 462 if (options & WCONTINUED) { 463 if (WIFCONTINUED(info.si_status)) 464 return 0; 465 } 466 467 return -1; 468} 469 470int dirfd_open_opath(const char *dir) 471{ 472 return open(dir, O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW | O_PATH); 473} 474 475#define close_prot_errno(fd) \ 476 if (fd >= 0) { \ 477 int _e_ = errno; \ 478 close(fd); \ 479 errno = _e_; \ 480 } 481 482static int clone_into_cgroup_run_nowait(const char *cgroup, 483 int (*fn)(const char *cgroup, void *arg), 484 void *arg) 485{ 486 int cgroup_fd; 487 pid_t pid; 488 489 cgroup_fd = dirfd_open_opath(cgroup); 490 if (cgroup_fd < 0) 491 return -1; 492 493 pid = clone_into_cgroup(cgroup_fd); 494 close_prot_errno(cgroup_fd); 495 if (pid == 0) 496 exit(fn(cgroup, arg)); 497 498 return pid; 499} 500 501int cg_run_nowait(const char *cgroup, 502 int (*fn)(const char *cgroup, void *arg), 503 void *arg) 504{ 505 int pid; 506 507 pid = clone_into_cgroup_run_nowait(cgroup, fn, arg); 508 if (pid > 0) 509 return pid; 510 511 /* Genuine test failure. */ 512 if (pid < 0 && errno != ENOSYS) 513 return -1; 514 515 pid = fork(); 516 if (pid == 0) { 517 char buf[64]; 518 519 snprintf(buf, sizeof(buf), "%d", getpid()); 520 if (cg_write(cgroup, "cgroup.procs", buf)) 521 exit(EXIT_FAILURE); 522 exit(fn(cgroup, arg)); 523 } 524 525 return pid; 526} 527 528int proc_mount_contains(const char *option) 529{ 530 char buf[4 * PAGE_SIZE]; 531 ssize_t read; 532 533 read = read_text("/proc/mounts", buf, sizeof(buf)); 534 if (read < 0) 535 return read; 536 537 return strstr(buf, option) != NULL; 538} 539 540int cgroup_feature(const char *feature) 541{ 542 char buf[PAGE_SIZE]; 543 ssize_t read; 544 545 read = read_text("/sys/kernel/cgroup/features", buf, sizeof(buf)); 546 if (read < 0) 547 return read; 548 549 return strstr(buf, feature) != NULL; 550} 551 552ssize_t proc_read_text(int pid, bool thread, const char *item, char *buf, size_t size) 553{ 554 char path[PATH_MAX]; 555 ssize_t ret; 556 557 if (!pid) 558 snprintf(path, sizeof(path), "/proc/%s/%s", 559 thread ? "thread-self" : "self", item); 560 else 561 snprintf(path, sizeof(path), "/proc/%d/%s", pid, item); 562 563 ret = read_text(path, buf, size); 564 return ret < 0 ? -1 : ret; 565} 566 567int proc_read_strstr(int pid, bool thread, const char *item, const char *needle) 568{ 569 char buf[PAGE_SIZE]; 570 571 if (proc_read_text(pid, thread, item, buf, sizeof(buf)) < 0) 572 return -1; 573 574 return strstr(buf, needle) ? 0 : -1; 575} 576 577int clone_into_cgroup_run_wait(const char *cgroup) 578{ 579 int cgroup_fd; 580 pid_t pid; 581 582 cgroup_fd = dirfd_open_opath(cgroup); 583 if (cgroup_fd < 0) 584 return -1; 585 586 pid = clone_into_cgroup(cgroup_fd); 587 close_prot_errno(cgroup_fd); 588 if (pid < 0) 589 return -1; 590 591 if (pid == 0) 592 exit(EXIT_SUCCESS); 593 594 /* 595 * We don't care whether this fails. We only care whether the initial 596 * clone succeeded. 597 */ 598 (void)clone_reap(pid, WEXITED); 599 return 0; 600} 601 602static int __prepare_for_wait(const char *cgroup, const char *filename) 603{ 604 int fd, ret = -1; 605 606 fd = inotify_init1(0); 607 if (fd == -1) 608 return fd; 609 610 ret = inotify_add_watch(fd, cg_control(cgroup, filename), IN_MODIFY); 611 if (ret == -1) { 612 close(fd); 613 fd = -1; 614 } 615 616 return fd; 617} 618 619int cg_prepare_for_wait(const char *cgroup) 620{ 621 return __prepare_for_wait(cgroup, "cgroup.events"); 622} 623 624int memcg_prepare_for_wait(const char *cgroup) 625{ 626 return __prepare_for_wait(cgroup, "memory.events"); 627} 628 629int cg_wait_for(int fd) 630{ 631 int ret = -1; 632 struct pollfd fds = { 633 .fd = fd, 634 .events = POLLIN, 635 }; 636 637 while (true) { 638 ret = poll(&fds, 1, 10000); 639 640 if (ret == -1) { 641 if (errno == EINTR) 642 continue; 643 644 break; 645 } 646 647 if (ret > 0 && fds.revents & POLLIN) { 648 ret = 0; 649 break; 650 } 651 } 652 653 return ret; 654}
~/
Read-only file preview.