~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/cgroup/cgroup_util.c

Version: ~ [ linux-5.14-rc1 ] ~ [ linux-5.13.1 ] ~ [ linux-5.12.16 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.49 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.131 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.197 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.239 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.275 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.275 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.18.140 ] ~ [ linux-3.16.85 ] ~ [ linux-3.14.79 ] ~ [ linux-3.12.74 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp