xref: /openbmc/linux/tools/perf/util/cgroup.c (revision 0ac25fd0)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <subcmd/parse-options.h>
3 #include "evsel.h"
4 #include "cgroup.h"
5 #include "evlist.h"
6 #include <linux/stringify.h>
7 #include <linux/zalloc.h>
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <fcntl.h>
11 
12 int nr_cgroups;
13 
14 static int
15 cgroupfs_find_mountpoint(char *buf, size_t maxlen)
16 {
17 	FILE *fp;
18 	char mountpoint[PATH_MAX + 1], tokens[PATH_MAX + 1], type[PATH_MAX + 1];
19 	char path_v1[PATH_MAX + 1], path_v2[PATH_MAX + 2], *path;
20 	char *token, *saved_ptr = NULL;
21 
22 	fp = fopen("/proc/mounts", "r");
23 	if (!fp)
24 		return -1;
25 
26 	/*
27 	 * in order to handle split hierarchy, we need to scan /proc/mounts
28 	 * and inspect every cgroupfs mount point to find one that has
29 	 * perf_event subsystem
30 	 */
31 	path_v1[0] = '\0';
32 	path_v2[0] = '\0';
33 
34 	while (fscanf(fp, "%*s %"__stringify(PATH_MAX)"s %"__stringify(PATH_MAX)"s %"
35 				__stringify(PATH_MAX)"s %*d %*d\n",
36 				mountpoint, type, tokens) == 3) {
37 
38 		if (!path_v1[0] && !strcmp(type, "cgroup")) {
39 
40 			token = strtok_r(tokens, ",", &saved_ptr);
41 
42 			while (token != NULL) {
43 				if (!strcmp(token, "perf_event")) {
44 					strcpy(path_v1, mountpoint);
45 					break;
46 				}
47 				token = strtok_r(NULL, ",", &saved_ptr);
48 			}
49 		}
50 
51 		if (!path_v2[0] && !strcmp(type, "cgroup2"))
52 			strcpy(path_v2, mountpoint);
53 
54 		if (path_v1[0] && path_v2[0])
55 			break;
56 	}
57 	fclose(fp);
58 
59 	if (path_v1[0])
60 		path = path_v1;
61 	else if (path_v2[0])
62 		path = path_v2;
63 	else
64 		return -1;
65 
66 	if (strlen(path) < maxlen) {
67 		strcpy(buf, path);
68 		return 0;
69 	}
70 	return -1;
71 }
72 
73 static int open_cgroup(const char *name)
74 {
75 	char path[PATH_MAX + 1];
76 	char mnt[PATH_MAX + 1];
77 	int fd;
78 
79 
80 	if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1))
81 		return -1;
82 
83 	scnprintf(path, PATH_MAX, "%s/%s", mnt, name);
84 
85 	fd = open(path, O_RDONLY);
86 	if (fd == -1)
87 		fprintf(stderr, "no access to cgroup %s\n", path);
88 
89 	return fd;
90 }
91 
92 static struct cgroup *evlist__find_cgroup(struct evlist *evlist, const char *str)
93 {
94 	struct evsel *counter;
95 	/*
96 	 * check if cgrp is already defined, if so we reuse it
97 	 */
98 	evlist__for_each_entry(evlist, counter) {
99 		if (!counter->cgrp)
100 			continue;
101 		if (!strcmp(counter->cgrp->name, str))
102 			return cgroup__get(counter->cgrp);
103 	}
104 
105 	return NULL;
106 }
107 
108 static struct cgroup *cgroup__new(const char *name)
109 {
110 	struct cgroup *cgroup = zalloc(sizeof(*cgroup));
111 
112 	if (cgroup != NULL) {
113 		refcount_set(&cgroup->refcnt, 1);
114 
115 		cgroup->name = strdup(name);
116 		if (!cgroup->name)
117 			goto out_err;
118 		cgroup->fd = open_cgroup(name);
119 		if (cgroup->fd == -1)
120 			goto out_free_name;
121 	}
122 
123 	return cgroup;
124 
125 out_free_name:
126 	zfree(&cgroup->name);
127 out_err:
128 	free(cgroup);
129 	return NULL;
130 }
131 
132 struct cgroup *evlist__findnew_cgroup(struct evlist *evlist, const char *name)
133 {
134 	struct cgroup *cgroup = evlist__find_cgroup(evlist, name);
135 
136 	return cgroup ?: cgroup__new(name);
137 }
138 
139 static int add_cgroup(struct evlist *evlist, const char *str)
140 {
141 	struct evsel *counter;
142 	struct cgroup *cgrp = evlist__findnew_cgroup(evlist, str);
143 	int n;
144 
145 	if (!cgrp)
146 		return -1;
147 	/*
148 	 * find corresponding event
149 	 * if add cgroup N, then need to find event N
150 	 */
151 	n = 0;
152 	evlist__for_each_entry(evlist, counter) {
153 		if (n == nr_cgroups)
154 			goto found;
155 		n++;
156 	}
157 
158 	cgroup__put(cgrp);
159 	return -1;
160 found:
161 	counter->cgrp = cgrp;
162 	return 0;
163 }
164 
165 static void cgroup__delete(struct cgroup *cgroup)
166 {
167 	close(cgroup->fd);
168 	zfree(&cgroup->name);
169 	free(cgroup);
170 }
171 
172 void cgroup__put(struct cgroup *cgrp)
173 {
174 	if (cgrp && refcount_dec_and_test(&cgrp->refcnt)) {
175 		cgroup__delete(cgrp);
176 	}
177 }
178 
179 struct cgroup *cgroup__get(struct cgroup *cgroup)
180 {
181        if (cgroup)
182 		refcount_inc(&cgroup->refcnt);
183        return cgroup;
184 }
185 
186 static void evsel__set_default_cgroup(struct evsel *evsel, struct cgroup *cgroup)
187 {
188 	if (evsel->cgrp == NULL)
189 		evsel->cgrp = cgroup__get(cgroup);
190 }
191 
192 void evlist__set_default_cgroup(struct evlist *evlist, struct cgroup *cgroup)
193 {
194 	struct evsel *evsel;
195 
196 	evlist__for_each_entry(evlist, evsel)
197 		evsel__set_default_cgroup(evsel, cgroup);
198 }
199 
200 int parse_cgroups(const struct option *opt, const char *str,
201 		  int unset __maybe_unused)
202 {
203 	struct evlist *evlist = *(struct evlist **)opt->value;
204 	struct evsel *counter;
205 	struct cgroup *cgrp = NULL;
206 	const char *p, *e, *eos = str + strlen(str);
207 	char *s;
208 	int ret, i;
209 
210 	if (list_empty(&evlist->core.entries)) {
211 		fprintf(stderr, "must define events before cgroups\n");
212 		return -1;
213 	}
214 
215 	for (;;) {
216 		p = strchr(str, ',');
217 		e = p ? p : eos;
218 
219 		/* allow empty cgroups, i.e., skip */
220 		if (e - str) {
221 			/* termination added */
222 			s = strndup(str, e - str);
223 			if (!s)
224 				return -1;
225 			ret = add_cgroup(evlist, s);
226 			free(s);
227 			if (ret)
228 				return -1;
229 		}
230 		/* nr_cgroups is increased een for empty cgroups */
231 		nr_cgroups++;
232 		if (!p)
233 			break;
234 		str = p+1;
235 	}
236 	/* for the case one cgroup combine to multiple events */
237 	i = 0;
238 	if (nr_cgroups == 1) {
239 		evlist__for_each_entry(evlist, counter) {
240 			if (i == 0)
241 				cgrp = counter->cgrp;
242 			else {
243 				counter->cgrp = cgrp;
244 				refcount_inc(&cgrp->refcnt);
245 			}
246 			i++;
247 		}
248 	}
249 	return 0;
250 }
251