File Index
Files and directories
Sign in
FILE

cgroup_util.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/cgroup/lib/cgroup_util.c 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.