xref: /openbmc/linux/tools/perf/util/cgroup.c (revision afba8b0a)
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 "rblist.h"
7 #include "metricgroup.h"
8 #include "stat.h"
9 #include <linux/zalloc.h>
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #include <fcntl.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <api/fs/fs.h>
16 
17 int nr_cgroups;
18 
19 static int open_cgroup(const char *name)
20 {
21 	char path[PATH_MAX + 1];
22 	char mnt[PATH_MAX + 1];
23 	int fd;
24 
25 
26 	if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1, "perf_event"))
27 		return -1;
28 
29 	scnprintf(path, PATH_MAX, "%s/%s", mnt, name);
30 
31 	fd = open(path, O_RDONLY);
32 	if (fd == -1)
33 		fprintf(stderr, "no access to cgroup %s\n", path);
34 
35 	return fd;
36 }
37 
38 static struct cgroup *evlist__find_cgroup(struct evlist *evlist, const char *str)
39 {
40 	struct evsel *counter;
41 	/*
42 	 * check if cgrp is already defined, if so we reuse it
43 	 */
44 	evlist__for_each_entry(evlist, counter) {
45 		if (!counter->cgrp)
46 			continue;
47 		if (!strcmp(counter->cgrp->name, str))
48 			return cgroup__get(counter->cgrp);
49 	}
50 
51 	return NULL;
52 }
53 
54 static struct cgroup *cgroup__new(const char *name, bool do_open)
55 {
56 	struct cgroup *cgroup = zalloc(sizeof(*cgroup));
57 
58 	if (cgroup != NULL) {
59 		refcount_set(&cgroup->refcnt, 1);
60 
61 		cgroup->name = strdup(name);
62 		if (!cgroup->name)
63 			goto out_err;
64 
65 		if (do_open) {
66 			cgroup->fd = open_cgroup(name);
67 			if (cgroup->fd == -1)
68 				goto out_free_name;
69 		} else {
70 			cgroup->fd = -1;
71 		}
72 	}
73 
74 	return cgroup;
75 
76 out_free_name:
77 	zfree(&cgroup->name);
78 out_err:
79 	free(cgroup);
80 	return NULL;
81 }
82 
83 struct cgroup *evlist__findnew_cgroup(struct evlist *evlist, const char *name)
84 {
85 	struct cgroup *cgroup = evlist__find_cgroup(evlist, name);
86 
87 	return cgroup ?: cgroup__new(name, true);
88 }
89 
90 static int add_cgroup(struct evlist *evlist, const char *str)
91 {
92 	struct evsel *counter;
93 	struct cgroup *cgrp = evlist__findnew_cgroup(evlist, str);
94 	int n;
95 
96 	if (!cgrp)
97 		return -1;
98 	/*
99 	 * find corresponding event
100 	 * if add cgroup N, then need to find event N
101 	 */
102 	n = 0;
103 	evlist__for_each_entry(evlist, counter) {
104 		if (n == nr_cgroups)
105 			goto found;
106 		n++;
107 	}
108 
109 	cgroup__put(cgrp);
110 	return -1;
111 found:
112 	counter->cgrp = cgrp;
113 	return 0;
114 }
115 
116 static void cgroup__delete(struct cgroup *cgroup)
117 {
118 	if (cgroup->fd >= 0)
119 		close(cgroup->fd);
120 	zfree(&cgroup->name);
121 	free(cgroup);
122 }
123 
124 void cgroup__put(struct cgroup *cgrp)
125 {
126 	if (cgrp && refcount_dec_and_test(&cgrp->refcnt)) {
127 		cgroup__delete(cgrp);
128 	}
129 }
130 
131 struct cgroup *cgroup__get(struct cgroup *cgroup)
132 {
133        if (cgroup)
134 		refcount_inc(&cgroup->refcnt);
135        return cgroup;
136 }
137 
138 static void evsel__set_default_cgroup(struct evsel *evsel, struct cgroup *cgroup)
139 {
140 	if (evsel->cgrp == NULL)
141 		evsel->cgrp = cgroup__get(cgroup);
142 }
143 
144 void evlist__set_default_cgroup(struct evlist *evlist, struct cgroup *cgroup)
145 {
146 	struct evsel *evsel;
147 
148 	evlist__for_each_entry(evlist, evsel)
149 		evsel__set_default_cgroup(evsel, cgroup);
150 }
151 
152 int parse_cgroups(const struct option *opt, const char *str,
153 		  int unset __maybe_unused)
154 {
155 	struct evlist *evlist = *(struct evlist **)opt->value;
156 	struct evsel *counter;
157 	struct cgroup *cgrp = NULL;
158 	const char *p, *e, *eos = str + strlen(str);
159 	char *s;
160 	int ret, i;
161 
162 	if (list_empty(&evlist->core.entries)) {
163 		fprintf(stderr, "must define events before cgroups\n");
164 		return -1;
165 	}
166 
167 	for (;;) {
168 		p = strchr(str, ',');
169 		e = p ? p : eos;
170 
171 		/* allow empty cgroups, i.e., skip */
172 		if (e - str) {
173 			/* termination added */
174 			s = strndup(str, e - str);
175 			if (!s)
176 				return -1;
177 			ret = add_cgroup(evlist, s);
178 			free(s);
179 			if (ret)
180 				return -1;
181 		}
182 		/* nr_cgroups is increased een for empty cgroups */
183 		nr_cgroups++;
184 		if (!p)
185 			break;
186 		str = p+1;
187 	}
188 	/* for the case one cgroup combine to multiple events */
189 	i = 0;
190 	if (nr_cgroups == 1) {
191 		evlist__for_each_entry(evlist, counter) {
192 			if (i == 0)
193 				cgrp = counter->cgrp;
194 			else {
195 				counter->cgrp = cgrp;
196 				refcount_inc(&cgrp->refcnt);
197 			}
198 			i++;
199 		}
200 	}
201 	return 0;
202 }
203 
204 int evlist__expand_cgroup(struct evlist *evlist, const char *str,
205 			  struct rblist *metric_events, bool open_cgroup)
206 {
207 	struct evlist *orig_list, *tmp_list;
208 	struct evsel *pos, *evsel, *leader;
209 	struct rblist orig_metric_events;
210 	struct cgroup *cgrp = NULL;
211 	const char *p, *e, *eos = str + strlen(str);
212 	int ret = -1;
213 
214 	if (evlist->core.nr_entries == 0) {
215 		fprintf(stderr, "must define events before cgroups\n");
216 		return -EINVAL;
217 	}
218 
219 	orig_list = evlist__new();
220 	tmp_list = evlist__new();
221 	if (orig_list == NULL || tmp_list == NULL) {
222 		fprintf(stderr, "memory allocation failed\n");
223 		return -ENOMEM;
224 	}
225 
226 	/* save original events and init evlist */
227 	perf_evlist__splice_list_tail(orig_list, &evlist->core.entries);
228 	evlist->core.nr_entries = 0;
229 
230 	if (metric_events) {
231 		orig_metric_events = *metric_events;
232 		rblist__init(metric_events);
233 	} else {
234 		rblist__init(&orig_metric_events);
235 	}
236 
237 	for (;;) {
238 		p = strchr(str, ',');
239 		e = p ? p : eos;
240 
241 		/* allow empty cgroups, i.e., skip */
242 		if (e - str) {
243 			/* termination added */
244 			char *name = strndup(str, e - str);
245 			if (!name)
246 				goto out_err;
247 
248 			cgrp = cgroup__new(name, open_cgroup);
249 			free(name);
250 			if (cgrp == NULL)
251 				goto out_err;
252 		} else {
253 			cgrp = NULL;
254 		}
255 
256 		leader = NULL;
257 		evlist__for_each_entry(orig_list, pos) {
258 			evsel = evsel__clone(pos);
259 			if (evsel == NULL)
260 				goto out_err;
261 
262 			cgroup__put(evsel->cgrp);
263 			evsel->cgrp = cgroup__get(cgrp);
264 
265 			if (evsel__is_group_leader(pos))
266 				leader = evsel;
267 			evsel->leader = leader;
268 
269 			evlist__add(tmp_list, evsel);
270 		}
271 		/* cgroup__new() has a refcount, release it here */
272 		cgroup__put(cgrp);
273 		nr_cgroups++;
274 
275 		if (metric_events) {
276 			perf_stat__collect_metric_expr(tmp_list);
277 			if (metricgroup__copy_metric_events(tmp_list, cgrp,
278 							    metric_events,
279 							    &orig_metric_events) < 0)
280 				break;
281 		}
282 
283 		perf_evlist__splice_list_tail(evlist, &tmp_list->core.entries);
284 		tmp_list->core.nr_entries = 0;
285 
286 		if (!p) {
287 			ret = 0;
288 			break;
289 		}
290 		str = p+1;
291 	}
292 
293 out_err:
294 	evlist__delete(orig_list);
295 	evlist__delete(tmp_list);
296 	rblist__exit(&orig_metric_events);
297 
298 	return ret;
299 }
300 
301 static struct cgroup *__cgroup__findnew(struct rb_root *root, uint64_t id,
302 					bool create, const char *path)
303 {
304 	struct rb_node **p = &root->rb_node;
305 	struct rb_node *parent = NULL;
306 	struct cgroup *cgrp;
307 
308 	while (*p != NULL) {
309 		parent = *p;
310 		cgrp = rb_entry(parent, struct cgroup, node);
311 
312 		if (cgrp->id == id)
313 			return cgrp;
314 
315 		if (cgrp->id < id)
316 			p = &(*p)->rb_left;
317 		else
318 			p = &(*p)->rb_right;
319 	}
320 
321 	if (!create)
322 		return NULL;
323 
324 	cgrp = malloc(sizeof(*cgrp));
325 	if (cgrp == NULL)
326 		return NULL;
327 
328 	cgrp->name = strdup(path);
329 	if (cgrp->name == NULL) {
330 		free(cgrp);
331 		return NULL;
332 	}
333 
334 	cgrp->fd = -1;
335 	cgrp->id = id;
336 	refcount_set(&cgrp->refcnt, 1);
337 
338 	rb_link_node(&cgrp->node, parent, p);
339 	rb_insert_color(&cgrp->node, root);
340 
341 	return cgrp;
342 }
343 
344 struct cgroup *cgroup__findnew(struct perf_env *env, uint64_t id,
345 			       const char *path)
346 {
347 	struct cgroup *cgrp;
348 
349 	down_write(&env->cgroups.lock);
350 	cgrp = __cgroup__findnew(&env->cgroups.tree, id, true, path);
351 	up_write(&env->cgroups.lock);
352 	return cgrp;
353 }
354 
355 struct cgroup *cgroup__find(struct perf_env *env, uint64_t id)
356 {
357 	struct cgroup *cgrp;
358 
359 	down_read(&env->cgroups.lock);
360 	cgrp = __cgroup__findnew(&env->cgroups.tree, id, false, NULL);
361 	up_read(&env->cgroups.lock);
362 	return cgrp;
363 }
364 
365 void perf_env__purge_cgroups(struct perf_env *env)
366 {
367 	struct rb_node *node;
368 	struct cgroup *cgrp;
369 
370 	down_write(&env->cgroups.lock);
371 	while (!RB_EMPTY_ROOT(&env->cgroups.tree)) {
372 		node = rb_first(&env->cgroups.tree);
373 		cgrp = rb_entry(node, struct cgroup, node);
374 
375 		rb_erase(node, &env->cgroups.tree);
376 		cgroup__put(cgrp);
377 	}
378 	up_write(&env->cgroups.lock);
379 }
380