xref: /openbmc/linux/tools/perf/util/cgroup.c (revision a28b2ed9)
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/zalloc.h>
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <fcntl.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <api/fs/fs.h>
13 
14 int nr_cgroups;
15 
16 static int open_cgroup(const char *name)
17 {
18 	char path[PATH_MAX + 1];
19 	char mnt[PATH_MAX + 1];
20 	int fd;
21 
22 
23 	if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1, "perf_event"))
24 		return -1;
25 
26 	scnprintf(path, PATH_MAX, "%s/%s", mnt, name);
27 
28 	fd = open(path, O_RDONLY);
29 	if (fd == -1)
30 		fprintf(stderr, "no access to cgroup %s\n", path);
31 
32 	return fd;
33 }
34 
35 static struct cgroup *evlist__find_cgroup(struct evlist *evlist, const char *str)
36 {
37 	struct evsel *counter;
38 	/*
39 	 * check if cgrp is already defined, if so we reuse it
40 	 */
41 	evlist__for_each_entry(evlist, counter) {
42 		if (!counter->cgrp)
43 			continue;
44 		if (!strcmp(counter->cgrp->name, str))
45 			return cgroup__get(counter->cgrp);
46 	}
47 
48 	return NULL;
49 }
50 
51 static struct cgroup *cgroup__new(const char *name)
52 {
53 	struct cgroup *cgroup = zalloc(sizeof(*cgroup));
54 
55 	if (cgroup != NULL) {
56 		refcount_set(&cgroup->refcnt, 1);
57 
58 		cgroup->name = strdup(name);
59 		if (!cgroup->name)
60 			goto out_err;
61 		cgroup->fd = open_cgroup(name);
62 		if (cgroup->fd == -1)
63 			goto out_free_name;
64 	}
65 
66 	return cgroup;
67 
68 out_free_name:
69 	zfree(&cgroup->name);
70 out_err:
71 	free(cgroup);
72 	return NULL;
73 }
74 
75 struct cgroup *evlist__findnew_cgroup(struct evlist *evlist, const char *name)
76 {
77 	struct cgroup *cgroup = evlist__find_cgroup(evlist, name);
78 
79 	return cgroup ?: cgroup__new(name);
80 }
81 
82 static int add_cgroup(struct evlist *evlist, const char *str)
83 {
84 	struct evsel *counter;
85 	struct cgroup *cgrp = evlist__findnew_cgroup(evlist, str);
86 	int n;
87 
88 	if (!cgrp)
89 		return -1;
90 	/*
91 	 * find corresponding event
92 	 * if add cgroup N, then need to find event N
93 	 */
94 	n = 0;
95 	evlist__for_each_entry(evlist, counter) {
96 		if (n == nr_cgroups)
97 			goto found;
98 		n++;
99 	}
100 
101 	cgroup__put(cgrp);
102 	return -1;
103 found:
104 	counter->cgrp = cgrp;
105 	return 0;
106 }
107 
108 static void cgroup__delete(struct cgroup *cgroup)
109 {
110 	if (cgroup->fd >= 0)
111 		close(cgroup->fd);
112 	zfree(&cgroup->name);
113 	free(cgroup);
114 }
115 
116 void cgroup__put(struct cgroup *cgrp)
117 {
118 	if (cgrp && refcount_dec_and_test(&cgrp->refcnt)) {
119 		cgroup__delete(cgrp);
120 	}
121 }
122 
123 struct cgroup *cgroup__get(struct cgroup *cgroup)
124 {
125        if (cgroup)
126 		refcount_inc(&cgroup->refcnt);
127        return cgroup;
128 }
129 
130 static void evsel__set_default_cgroup(struct evsel *evsel, struct cgroup *cgroup)
131 {
132 	if (evsel->cgrp == NULL)
133 		evsel->cgrp = cgroup__get(cgroup);
134 }
135 
136 void evlist__set_default_cgroup(struct evlist *evlist, struct cgroup *cgroup)
137 {
138 	struct evsel *evsel;
139 
140 	evlist__for_each_entry(evlist, evsel)
141 		evsel__set_default_cgroup(evsel, cgroup);
142 }
143 
144 int parse_cgroups(const struct option *opt, const char *str,
145 		  int unset __maybe_unused)
146 {
147 	struct evlist *evlist = *(struct evlist **)opt->value;
148 	struct evsel *counter;
149 	struct cgroup *cgrp = NULL;
150 	const char *p, *e, *eos = str + strlen(str);
151 	char *s;
152 	int ret, i;
153 
154 	if (list_empty(&evlist->core.entries)) {
155 		fprintf(stderr, "must define events before cgroups\n");
156 		return -1;
157 	}
158 
159 	for (;;) {
160 		p = strchr(str, ',');
161 		e = p ? p : eos;
162 
163 		/* allow empty cgroups, i.e., skip */
164 		if (e - str) {
165 			/* termination added */
166 			s = strndup(str, e - str);
167 			if (!s)
168 				return -1;
169 			ret = add_cgroup(evlist, s);
170 			free(s);
171 			if (ret)
172 				return -1;
173 		}
174 		/* nr_cgroups is increased een for empty cgroups */
175 		nr_cgroups++;
176 		if (!p)
177 			break;
178 		str = p+1;
179 	}
180 	/* for the case one cgroup combine to multiple events */
181 	i = 0;
182 	if (nr_cgroups == 1) {
183 		evlist__for_each_entry(evlist, counter) {
184 			if (i == 0)
185 				cgrp = counter->cgrp;
186 			else {
187 				counter->cgrp = cgrp;
188 				refcount_inc(&cgrp->refcnt);
189 			}
190 			i++;
191 		}
192 	}
193 	return 0;
194 }
195 
196 static struct cgroup *__cgroup__findnew(struct rb_root *root, uint64_t id,
197 					bool create, const char *path)
198 {
199 	struct rb_node **p = &root->rb_node;
200 	struct rb_node *parent = NULL;
201 	struct cgroup *cgrp;
202 
203 	while (*p != NULL) {
204 		parent = *p;
205 		cgrp = rb_entry(parent, struct cgroup, node);
206 
207 		if (cgrp->id == id)
208 			return cgrp;
209 
210 		if (cgrp->id < id)
211 			p = &(*p)->rb_left;
212 		else
213 			p = &(*p)->rb_right;
214 	}
215 
216 	if (!create)
217 		return NULL;
218 
219 	cgrp = malloc(sizeof(*cgrp));
220 	if (cgrp == NULL)
221 		return NULL;
222 
223 	cgrp->name = strdup(path);
224 	if (cgrp->name == NULL) {
225 		free(cgrp);
226 		return NULL;
227 	}
228 
229 	cgrp->fd = -1;
230 	cgrp->id = id;
231 	refcount_set(&cgrp->refcnt, 1);
232 
233 	rb_link_node(&cgrp->node, parent, p);
234 	rb_insert_color(&cgrp->node, root);
235 
236 	return cgrp;
237 }
238 
239 struct cgroup *cgroup__findnew(struct perf_env *env, uint64_t id,
240 			       const char *path)
241 {
242 	struct cgroup *cgrp;
243 
244 	down_write(&env->cgroups.lock);
245 	cgrp = __cgroup__findnew(&env->cgroups.tree, id, true, path);
246 	up_write(&env->cgroups.lock);
247 	return cgrp;
248 }
249 
250 struct cgroup *cgroup__find(struct perf_env *env, uint64_t id)
251 {
252 	struct cgroup *cgrp;
253 
254 	down_read(&env->cgroups.lock);
255 	cgrp = __cgroup__findnew(&env->cgroups.tree, id, false, NULL);
256 	up_read(&env->cgroups.lock);
257 	return cgrp;
258 }
259 
260 void perf_env__purge_cgroups(struct perf_env *env)
261 {
262 	struct rb_node *node;
263 	struct cgroup *cgrp;
264 
265 	down_write(&env->cgroups.lock);
266 	while (!RB_EMPTY_ROOT(&env->cgroups.tree)) {
267 		node = rb_first(&env->cgroups.tree);
268 		cgrp = rb_entry(node, struct cgroup, node);
269 
270 		rb_erase(node, &env->cgroups.tree);
271 		cgroup__put(cgrp);
272 	}
273 	up_write(&env->cgroups.lock);
274 }
275