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