linux/tools/testing/selftests/cgroup/cgroup_util.c
<<
>>
Prefs
   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 <signal.h>
   9#include <stdio.h>
  10#include <stdlib.h>
  11#include <string.h>
  12#include <sys/stat.h>
  13#include <sys/types.h>
  14#include <sys/wait.h>
  15#include <unistd.h>
  16
  17#include "cgroup_util.h"
  18
  19static ssize_t read_text(const char *path, char *buf, size_t max_len)
  20{
  21        ssize_t len;
  22        int fd;
  23
  24        fd = open(path, O_RDONLY);
  25        if (fd < 0)
  26                return fd;
  27
  28        len = read(fd, buf, max_len - 1);
  29        if (len < 0)
  30                goto out;
  31
  32        buf[len] = 0;
  33out:
  34        close(fd);
  35        return len;
  36}
  37
  38static ssize_t write_text(const char *path, char *buf, ssize_t len)
  39{
  40        int fd;
  41
  42        fd = open(path, O_WRONLY | O_APPEND);
  43        if (fd < 0)
  44                return fd;
  45
  46        len = write(fd, buf, len);
  47        if (len < 0) {
  48                close(fd);
  49                return len;
  50        }
  51
  52        close(fd);
  53
  54        return 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
  87int cg_read(const char *cgroup, const char *control, char *buf, size_t len)
  88{
  89        char path[PATH_MAX];
  90
  91        snprintf(path, sizeof(path), "%s/%s", cgroup, control);
  92
  93        if (read_text(path, buf, len) >= 0)
  94                return 0;
  95
  96        return -1;
  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                size = 32;
 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_strstr(const char *cgroup, const char *control, const char *needle)
 127{
 128        char buf[PAGE_SIZE];
 129
 130        if (cg_read(cgroup, control, buf, sizeof(buf)))
 131                return -1;
 132
 133        return strstr(buf, needle) ? 0 : -1;
 134}
 135
 136long cg_read_long(const char *cgroup, const char *control)
 137{
 138        char buf[128];
 139
 140        if (cg_read(cgroup, control, buf, sizeof(buf)))
 141                return -1;
 142
 143        return atol(buf);
 144}
 145
 146long cg_read_key_long(const char *cgroup, const char *control, const char *key)
 147{
 148        char buf[PAGE_SIZE];
 149        char *ptr;
 150
 151        if (cg_read(cgroup, control, buf, sizeof(buf)))
 152                return -1;
 153
 154        ptr = strstr(buf, key);
 155        if (!ptr)
 156                return -1;
 157
 158        return atol(ptr + strlen(key));
 159}
 160
 161int cg_write(const char *cgroup, const char *control, char *buf)
 162{
 163        char path[PATH_MAX];
 164        ssize_t len = strlen(buf);
 165
 166        snprintf(path, sizeof(path), "%s/%s", cgroup, control);
 167
 168        if (write_text(path, buf, len) == len)
 169                return 0;
 170
 171        return -1;
 172}
 173
 174int cg_find_unified_root(char *root, size_t len)
 175{
 176        char buf[10 * PAGE_SIZE];
 177        char *fs, *mount, *type;
 178        const char delim[] = "\n\t ";
 179
 180        if (read_text("/proc/self/mounts", buf, sizeof(buf)) <= 0)
 181                return -1;
 182
 183        /*
 184         * Example:
 185         * cgroup /sys/fs/cgroup cgroup2 rw,seclabel,noexec,relatime 0 0
 186         */
 187        for (fs = strtok(buf, delim); fs; fs = strtok(NULL, delim)) {
 188                mount = strtok(NULL, delim);
 189                type = strtok(NULL, delim);
 190                strtok(NULL, delim);
 191                strtok(NULL, delim);
 192                strtok(NULL, delim);
 193
 194                if (strcmp(type, "cgroup2") == 0) {
 195                        strncpy(root, mount, len);
 196                        return 0;
 197                }
 198        }
 199
 200        return -1;
 201}
 202
 203int cg_create(const char *cgroup)
 204{
 205        return mkdir(cgroup, 0644);
 206}
 207
 208int cg_wait_for_proc_count(const char *cgroup, int count)
 209{
 210        char buf[10 * PAGE_SIZE] = {0};
 211        int attempts;
 212        char *ptr;
 213
 214        for (attempts = 10; attempts >= 0; attempts--) {
 215                int nr = 0;
 216
 217                if (cg_read(cgroup, "cgroup.procs", buf, sizeof(buf)))
 218                        break;
 219
 220                for (ptr = buf; *ptr; ptr++)
 221                        if (*ptr == '\n')
 222                                nr++;
 223
 224                if (nr >= count)
 225                        return 0;
 226
 227                usleep(100000);
 228        }
 229
 230        return -1;
 231}
 232
 233int cg_killall(const char *cgroup)
 234{
 235        char buf[PAGE_SIZE];
 236        char *ptr = buf;
 237
 238        if (cg_read(cgroup, "cgroup.procs", buf, sizeof(buf)))
 239                return -1;
 240
 241        while (ptr < buf + sizeof(buf)) {
 242                int pid = strtol(ptr, &ptr, 10);
 243
 244                if (pid == 0)
 245                        break;
 246                if (*ptr)
 247                        ptr++;
 248                else
 249                        break;
 250                if (kill(pid, SIGKILL))
 251                        return -1;
 252        }
 253
 254        return 0;
 255}
 256
 257int cg_destroy(const char *cgroup)
 258{
 259        int ret;
 260
 261retry:
 262        ret = rmdir(cgroup);
 263        if (ret && errno == EBUSY) {
 264                cg_killall(cgroup);
 265                usleep(100);
 266                goto retry;
 267        }
 268
 269        if (ret && errno == ENOENT)
 270                ret = 0;
 271
 272        return ret;
 273}
 274
 275int cg_enter(const char *cgroup, int pid)
 276{
 277        char pidbuf[64];
 278
 279        snprintf(pidbuf, sizeof(pidbuf), "%d", pid);
 280        return cg_write(cgroup, "cgroup.procs", pidbuf);
 281}
 282
 283int cg_enter_current(const char *cgroup)
 284{
 285        char pidbuf[64];
 286
 287        snprintf(pidbuf, sizeof(pidbuf), "%d", getpid());
 288        return cg_write(cgroup, "cgroup.procs", pidbuf);
 289}
 290
 291int cg_run(const char *cgroup,
 292           int (*fn)(const char *cgroup, void *arg),
 293           void *arg)
 294{
 295        int pid, retcode;
 296
 297        pid = fork();
 298        if (pid < 0) {
 299                return pid;
 300        } else if (pid == 0) {
 301                char buf[64];
 302
 303                snprintf(buf, sizeof(buf), "%d", getpid());
 304                if (cg_write(cgroup, "cgroup.procs", buf))
 305                        exit(EXIT_FAILURE);
 306                exit(fn(cgroup, arg));
 307        } else {
 308                waitpid(pid, &retcode, 0);
 309                if (WIFEXITED(retcode))
 310                        return WEXITSTATUS(retcode);
 311                else
 312                        return -1;
 313        }
 314}
 315
 316int cg_run_nowait(const char *cgroup,
 317                  int (*fn)(const char *cgroup, void *arg),
 318                  void *arg)
 319{
 320        int pid;
 321
 322        pid = fork();
 323        if (pid == 0) {
 324                char buf[64];
 325
 326                snprintf(buf, sizeof(buf), "%d", getpid());
 327                if (cg_write(cgroup, "cgroup.procs", buf))
 328                        exit(EXIT_FAILURE);
 329                exit(fn(cgroup, arg));
 330        }
 331
 332        return pid;
 333}
 334
 335int get_temp_fd(void)
 336{
 337        return open(".", O_TMPFILE | O_RDWR | O_EXCL);
 338}
 339
 340int alloc_pagecache(int fd, size_t size)
 341{
 342        char buf[PAGE_SIZE];
 343        struct stat st;
 344        int i;
 345
 346        if (fstat(fd, &st))
 347                goto cleanup;
 348
 349        size += st.st_size;
 350
 351        if (ftruncate(fd, size))
 352                goto cleanup;
 353
 354        for (i = 0; i < size; i += sizeof(buf))
 355                read(fd, buf, sizeof(buf));
 356
 357        return 0;
 358
 359cleanup:
 360        return -1;
 361}
 362
 363int alloc_anon(const char *cgroup, void *arg)
 364{
 365        size_t size = (unsigned long)arg;
 366        char *buf, *ptr;
 367
 368        buf = malloc(size);
 369        for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE)
 370                *ptr = 0;
 371
 372        free(buf);
 373        return 0;
 374}
 375
 376int is_swap_enabled(void)
 377{
 378        char buf[PAGE_SIZE];
 379        const char delim[] = "\n";
 380        int cnt = 0;
 381        char *line;
 382
 383        if (read_text("/proc/swaps", buf, sizeof(buf)) <= 0)
 384                return -1;
 385
 386        for (line = strtok(buf, delim); line; line = strtok(NULL, delim))
 387                cnt++;
 388
 389        return cnt > 1;
 390}
 391
 392int set_oom_adj_score(int pid, int score)
 393{
 394        char path[PATH_MAX];
 395        int fd, len;
 396
 397        sprintf(path, "/proc/%d/oom_score_adj", pid);
 398
 399        fd = open(path, O_WRONLY | O_APPEND);
 400        if (fd < 0)
 401                return fd;
 402
 403        len = dprintf(fd, "%d", score);
 404        if (len < 0) {
 405                close(fd);
 406                return len;
 407        }
 408
 409        close(fd);
 410        return 0;
 411}
 412
 413char proc_read_text(int pid, const char *item, char *buf, size_t size)
 414{
 415        char path[PATH_MAX];
 416
 417        snprintf(path, sizeof(path), "/proc/%d/%s", pid, item);
 418
 419        return read_text(path, buf, size);
 420}
 421