1 /* SPDX-License-Identifier: GPL-2.0 */
7 #include <linux/limits.h>
13 #include <sys/types.h>
17 #include "cgroup_util.h"
18 #include "../clone3/clone3_selftests.h"
20 static ssize_t
read_text(const char *path
, char *buf
, size_t max_len
)
25 fd
= open(path
, O_RDONLY
);
29 len
= read(fd
, buf
, max_len
- 1);
39 static ssize_t
write_text(const char *path
, char *buf
, ssize_t len
)
43 fd
= open(path
, O_WRONLY
| O_APPEND
);
47 len
= write(fd
, buf
, len
);
58 char *cg_name(const char *root
, const char *name
)
60 size_t len
= strlen(root
) + strlen(name
) + 2;
61 char *ret
= malloc(len
);
63 snprintf(ret
, len
, "%s/%s", root
, name
);
68 char *cg_name_indexed(const char *root
, const char *name
, int index
)
70 size_t len
= strlen(root
) + strlen(name
) + 10;
71 char *ret
= malloc(len
);
73 snprintf(ret
, len
, "%s/%s_%d", root
, name
, index
);
78 char *cg_control(const char *cgroup
, const char *control
)
80 size_t len
= strlen(cgroup
) + strlen(control
) + 2;
81 char *ret
= malloc(len
);
83 snprintf(ret
, len
, "%s/%s", cgroup
, control
);
88 int cg_read(const char *cgroup
, const char *control
, char *buf
, size_t len
)
92 snprintf(path
, sizeof(path
), "%s/%s", cgroup
, control
);
94 if (read_text(path
, buf
, len
) >= 0)
100 int cg_read_strcmp(const char *cgroup
, const char *control
,
101 const char *expected
)
107 /* Handle the case of comparing against empty string */
111 size
= strlen(expected
) + 1;
117 if (cg_read(cgroup
, control
, buf
, size
)) {
122 ret
= strcmp(expected
, buf
);
127 int cg_read_strstr(const char *cgroup
, const char *control
, const char *needle
)
131 if (cg_read(cgroup
, control
, buf
, sizeof(buf
)))
134 return strstr(buf
, needle
) ? 0 : -1;
137 long cg_read_long(const char *cgroup
, const char *control
)
141 if (cg_read(cgroup
, control
, buf
, sizeof(buf
)))
147 long cg_read_key_long(const char *cgroup
, const char *control
, const char *key
)
152 if (cg_read(cgroup
, control
, buf
, sizeof(buf
)))
155 ptr
= strstr(buf
, key
);
159 return atol(ptr
+ strlen(key
));
162 long cg_read_lc(const char *cgroup
, const char *control
)
165 const char delim
[] = "\n";
169 if (cg_read(cgroup
, control
, buf
, sizeof(buf
)))
172 for (line
= strtok(buf
, delim
); line
; line
= strtok(NULL
, delim
))
178 int cg_write(const char *cgroup
, const char *control
, char *buf
)
181 ssize_t len
= strlen(buf
);
183 snprintf(path
, sizeof(path
), "%s/%s", cgroup
, control
);
185 if (write_text(path
, buf
, len
) == len
)
191 int cg_find_unified_root(char *root
, size_t len
)
193 char buf
[10 * PAGE_SIZE
];
194 char *fs
, *mount
, *type
;
195 const char delim
[] = "\n\t ";
197 if (read_text("/proc/self/mounts", buf
, sizeof(buf
)) <= 0)
202 * cgroup /sys/fs/cgroup cgroup2 rw,seclabel,noexec,relatime 0 0
204 for (fs
= strtok(buf
, delim
); fs
; fs
= strtok(NULL
, delim
)) {
205 mount
= strtok(NULL
, delim
);
206 type
= strtok(NULL
, delim
);
211 if (strcmp(type
, "cgroup2") == 0) {
212 strncpy(root
, mount
, len
);
220 int cg_create(const char *cgroup
)
222 return mkdir(cgroup
, 0644);
225 int cg_wait_for_proc_count(const char *cgroup
, int count
)
227 char buf
[10 * PAGE_SIZE
] = {0};
231 for (attempts
= 10; attempts
>= 0; attempts
--) {
234 if (cg_read(cgroup
, "cgroup.procs", buf
, sizeof(buf
)))
237 for (ptr
= buf
; *ptr
; ptr
++)
250 int cg_killall(const char *cgroup
)
255 if (cg_read(cgroup
, "cgroup.procs", buf
, sizeof(buf
)))
258 while (ptr
< buf
+ sizeof(buf
)) {
259 int pid
= strtol(ptr
, &ptr
, 10);
267 if (kill(pid
, SIGKILL
))
274 int cg_destroy(const char *cgroup
)
280 if (ret
&& errno
== EBUSY
) {
286 if (ret
&& errno
== ENOENT
)
292 int cg_enter(const char *cgroup
, int pid
)
296 snprintf(pidbuf
, sizeof(pidbuf
), "%d", pid
);
297 return cg_write(cgroup
, "cgroup.procs", pidbuf
);
300 int cg_enter_current(const char *cgroup
)
302 return cg_write(cgroup
, "cgroup.procs", "0");
305 int cg_enter_current_thread(const char *cgroup
)
307 return cg_write(cgroup
, "cgroup.threads", "0");
310 int cg_run(const char *cgroup
,
311 int (*fn
)(const char *cgroup
, void *arg
),
319 } else if (pid
== 0) {
322 snprintf(buf
, sizeof(buf
), "%d", getpid());
323 if (cg_write(cgroup
, "cgroup.procs", buf
))
325 exit(fn(cgroup
, arg
));
327 waitpid(pid
, &retcode
, 0);
328 if (WIFEXITED(retcode
))
329 return WEXITSTATUS(retcode
);
335 pid_t
clone_into_cgroup(int cgroup_fd
)
337 #ifdef CLONE_ARGS_SIZE_VER2
340 struct __clone_args args
= {
341 .flags
= CLONE_INTO_CGROUP
,
342 .exit_signal
= SIGCHLD
,
346 pid
= sys_clone3(&args
, sizeof(struct __clone_args
));
348 * Verify that this is a genuine test failure:
349 * ENOSYS -> clone3() not available
350 * E2BIG -> CLONE_INTO_CGROUP not available
352 if (pid
< 0 && (errno
== ENOSYS
|| errno
== E2BIG
))
363 int clone_reap(pid_t pid
, int options
)
371 ret
= waitid(P_PID
, pid
, &info
, options
| __WALL
| __WNOTHREAD
);
378 if (options
& WEXITED
) {
379 if (WIFEXITED(info
.si_status
))
380 return WEXITSTATUS(info
.si_status
);
383 if (options
& WSTOPPED
) {
384 if (WIFSTOPPED(info
.si_status
))
385 return WSTOPSIG(info
.si_status
);
388 if (options
& WCONTINUED
) {
389 if (WIFCONTINUED(info
.si_status
))
396 int dirfd_open_opath(const char *dir
)
398 return open(dir
, O_DIRECTORY
| O_CLOEXEC
| O_NOFOLLOW
| O_PATH
);
401 #define close_prot_errno(fd) \
408 static int clone_into_cgroup_run_nowait(const char *cgroup
,
409 int (*fn
)(const char *cgroup
, void *arg
),
415 cgroup_fd
= dirfd_open_opath(cgroup
);
419 pid
= clone_into_cgroup(cgroup_fd
);
420 close_prot_errno(cgroup_fd
);
422 exit(fn(cgroup
, arg
));
427 int cg_run_nowait(const char *cgroup
,
428 int (*fn
)(const char *cgroup
, void *arg
),
433 pid
= clone_into_cgroup_run_nowait(cgroup
, fn
, arg
);
437 /* Genuine test failure. */
438 if (pid
< 0 && errno
!= ENOSYS
)
445 snprintf(buf
, sizeof(buf
), "%d", getpid());
446 if (cg_write(cgroup
, "cgroup.procs", buf
))
448 exit(fn(cgroup
, arg
));
454 int get_temp_fd(void)
456 return open(".", O_TMPFILE
| O_RDWR
| O_EXCL
);
459 int alloc_pagecache(int fd
, size_t size
)
470 if (ftruncate(fd
, size
))
473 for (i
= 0; i
< size
; i
+= sizeof(buf
))
474 read(fd
, buf
, sizeof(buf
));
482 int alloc_anon(const char *cgroup
, void *arg
)
484 size_t size
= (unsigned long)arg
;
488 for (ptr
= buf
; ptr
< buf
+ size
; ptr
+= PAGE_SIZE
)
495 int is_swap_enabled(void)
498 const char delim
[] = "\n";
502 if (read_text("/proc/swaps", buf
, sizeof(buf
)) <= 0)
505 for (line
= strtok(buf
, delim
); line
; line
= strtok(NULL
, delim
))
511 int set_oom_adj_score(int pid
, int score
)
516 sprintf(path
, "/proc/%d/oom_score_adj", pid
);
518 fd
= open(path
, O_WRONLY
| O_APPEND
);
522 len
= dprintf(fd
, "%d", score
);
532 ssize_t
proc_read_text(int pid
, bool thread
, const char *item
, char *buf
, size_t size
)
537 snprintf(path
, sizeof(path
), "/proc/%s/%s",
538 thread
? "thread-self" : "self", item
);
540 snprintf(path
, sizeof(path
), "/proc/%d/%s", pid
, item
);
542 return read_text(path
, buf
, size
);
545 int proc_read_strstr(int pid
, bool thread
, const char *item
, const char *needle
)
549 if (proc_read_text(pid
, thread
, item
, buf
, sizeof(buf
)) < 0)
552 return strstr(buf
, needle
) ? 0 : -1;
555 int clone_into_cgroup_run_wait(const char *cgroup
)
560 cgroup_fd
= dirfd_open_opath(cgroup
);
564 pid
= clone_into_cgroup(cgroup_fd
);
565 close_prot_errno(cgroup_fd
);
573 * We don't care whether this fails. We only care whether the initial
576 (void)clone_reap(pid
, WEXITED
);